but not the fourth for division. an oversight? usage example:
func divide(x: Int8, y: Int8) -> Int8 {
return x / y // Fatal error: Division results in an overflow
return x &/ y // a hypothetical new operator, gives -128 in this case
}
func foo() {
divide(x: -128, y: -1)
}
also i think it would be useful to have the overflow version of prefix minus operator:
func negative(x: Int8) -> Int8 {
return -x // Swift runtime failure: arithmetic overflow
return &-x // a hypothetical new operator - ok, gives -128 in this case
}
func bar() -> Int8 {
negative(x: -128)
}
Not an oversight. The operators &/ and &% were removed in Swift 1.2 because they did not provide two’s complement behavior like other overflow operators, and to simplify the language. (This is documented in the changelog.)
In the rare circumstance you need this functionality, use dividedReportingOverflow.
let's say you are implementing an app that grabs Int16 samples from the mic or camera does some fancy math and outputs result to the speaker or screen. and there are bunch of those " + - * / % " scattered across the code. and that algorithm wasn't written by you, and it's massive (but contained), and you don't quite understand it. would you prefer the app to crash once in a while due to unfortunate sample/pixel combination happening in the mic/cam data or would you prefer a minor glitch sent to the speaker/screen? if the latter and i didn't have time to do it properly - i'd just do a full text search replace + to &+, etc. i know, it's a bit of a lazy / hammer and not ideal approach. i'd prefer the app not crashing on those math overflow issues at all in release mode, perhaps if i opt-in to that behaviour. (crashing in debug mode is of course very helpful.)
(another approach i would probably do (and i remember actually doing smth like this in the past) - my own types instead of the types the algorithm is using, overriding math ops, and having asserts in those ops overrides for debug builds).
This is not an either-or choice. The preferred option is that you neither crash nor emit unexpected output. To do this, you would handle each computation that may overflow in an appropriate manner, either by asserting that it will not overflow using the ordinary operators, testing for overflow using *ReportingOverflow operations, or obtaining the full result using full-width operations.
But since you're asking, one of the tentpole design features of Swift is safety, and one way in which Swift delivers on that promise is by refusing to continue execution when an unexpected state is encountered. You absolutely shouldn't be emitting unexpected output to my speakers using an algorithm you don't understand with inputs you don't expect--this could quite possibly damage the hardware itself. Users would absolutely want your code to stop executing before it does that.
If your application enters an unexpected state, that is commonly referred to as a “bug”, and Swift does not universally protect against bugs. Of course, overflow can be a common source of bugs, but continuing execution after overflow is not necessarily failing to deliver on the promise of safety, unless you are arguing that Swift should stop execution on every bug.
FWIW, Rust only stops execution on overflow in debug builds, and not in release builds. Clearly debug builds should do their best to report programmer errors, but it is reasonable to want release builds to prioritise other things.
I doubt this true. There are certain rare edge-cases such as resonance which might damage the hardware (or persisting a static image on an OLED screen, etc), but overflow can be guarded against quite easily.
Anecdotally, I blew my speaker because I forgot to renormalize audio frame , on macOS framework no less. At least it's a cheaper third-party speaker and is easy to replace.
The bottom line is, well, I guess it's better to err on the side of caution.
Huh, well I’d still question the software stack and even the hardware that allowed that to happen. There are several layers in the middle that should have done a better job guarding against that IMO.
Having been involved in DSP algorithms for speaker protection in the past, I assure you that it is quite easy to damage physical hardware in many cases, and that overflow bugs are a common way to do it.
In general, lower-levels of the audio stack should be doing this for you, but someone has to write those too.
Swift cannot protect against all bugs by stopping execution, but it provides safety against whole classes of bugs in that way, and among these specifically are out-of-bounds access and arithmetic overflow. It’s not merely a convenience to help developers debug their code, it’s there to limit the damage caused by poorly debugged code when it is released to users.
Swift does not stop you from rejecting this philosophy by writing your own subscripts and arithmetic operators, but it does not make that the path of least resistance. Overflow operators have two’s complement semantics essential for certain computations; they’re not there to enable find-and-replace substitutions to make code you don’t understand “not crash.” In fact, that it makes such an anti-pattern so tempting suggests the operators would probably best have been spelled differently (as they are in Rust).
If given a reference implementation of a numerical algorithm in C (or soon, C++) that you don’t understand, Swift allows you to call it as-is. This is the best way to ensure your code’s behavior is at least no buggier than the original. Translating a numerical algorithm from C to Swift requires accounting for not just overflow handling, but also integer promotion rules in C, differences in operator precedence (particularly bitwise and bit shift operators) between the two languages, and likely other issues that don’t come to mind at the moment.
If given a reference implementation of a numerical algorithm that’s already written in Swift, then “crashing” when you call it is a sign that you’re providing inputs that violate preconditions (or, you got a buggy reference implementation). Editing an implementation you don’t understand essentially to remove preconditions is a surefire way to introduce new bugs, turning an unchecked precondition upfront to a bigger problem down the line.
These things happen in the ordinary course of things simply because we’re human and not necessarily because of a deviation from some norm.
For this reason, I’m confident that the wisdom of Swift’s design choices here will only become more apparent with time—it doesn’t just protect those people who run bad software on cheap hardware, it protects us…from us.