Remix.run Logo
norir 5 days ago

It is very unfortunate that we use fixed width numbers by default in most programming languages and that common ops will silently overflow. Smarter compilers can work with richer numeric primitives and either automatically promote machine words to big numbers or throw an error on overflow.

People talk a lot about the productivity gains of ai, but fixing problems like this at the language level could have an even bigger impact on productivity, but are far less sensational. Think about how much productivity is lost due to obscure but detectable bugs like this one. I don't think rust is a good answer (it doesn't check overflow by default), but at least it points a little bit in the vaguely correct direction.

recursivecaveat 5 days ago | parent | next [-]

The situation with numbers in basically every widely used programming language is kind of an indictment of our industry. Silent overflow for incorrect results, no convenient facilities for units, lossy casts everywhere. It's one of those things where standing in 1975 you'd think surely we'll spend some of the next 40 years of performance gains to give ourselves nice, correct numbers to work with, but we never did.

tomp 5 days ago | parent [-]

"nice, correct numbers" end somewhere between 1/3 and sqrt(2)

so in reality, it's just "pick your own poison" to various degrees...

tialaramex 5 days ago | parent | next [-]

The square root of two is still a computable Real. We choose not to cope with that, but it's not actually impossible it was merely inconvenient. I've mentioned elsewhere that my Rust care realistic is quite happy to work with these numbers e.g. take the square root of ten, and the square root of forty, multiply them together and get the quite ordinary integer twenty.

The non computable reals are a huge problem because, as their name suggests, we can't compute them - and in the strict sense that's Almost All reals, but none of the ones you're thinking of are non-computable so you'll likely be fine.

For the merely rational numbers like a third, or sixteen hundred and five sevenths, it's even more so a matter of choosing not to address it rather than it being out of reach.

GolDDranks 5 days ago | parent [-]

The problem with computables is that equivalence between them is only semi-decidable. (If the two numbers are different, it is decidable, but if they are not, it isn't. The problem is that you don't know if they are different a priori, so you might get lucky and find difference, but you might as well not.)

We know for sure that algebraic numbers behave nicely in terms of equivalence, and there are other, bigger number systems that are conjectured to behave nicely ( https://en.wikipedia.org/wiki/Period_(algebraic_geometry) ), but the problem with these and computers is that they are hard to represent.

tialaramex 5 days ago | parent [-]

Yeah, all these types have problems, we've decided to put up with the IEEE floating point numbers, we could have chosen to have the big rationals, or drawn any other line. I don't disagree that there's no satisfying "correct" answer but it's a little disappointing that programmers so easily accept the status quo as though nothing else could be in its place.

Maybe Python having automatic big numbers like Lisps often did will help introduce new programmers to the idea that the 32-bit two's complement integer provided on all modern computers isn't somehow "really" how numbers work.

int_19h 5 days ago | parent | prev [-]

Even so, we don't need to pick the more potent poison. And most certainly not when we had decades of awareness about better alternatives.

There's really no excuse for a modern PL to not have, at the very least, overflow detection by default.

astrange 5 days ago | parent | prev | next [-]

Swift traps on overflow, which I think is the correct solution. You shouldn't make all your numbers infinitely-ranged, that turns all O(1) operations into O(N) in time and memory, and introduces a lot of possibilities for remote DoS.

devnullbrain 5 days ago | parent | prev [-]

Rust checks overflow by default in debug builds

jeffparsons 5 days ago | parent [-]

I've often thought that I'd prefer it to check by default in release builds, too, but I understand that comes with a performance penalty that a lot of folks aren't happy with.

I assume this implies that common processor architectures (x86_64, aarch64) lack trap-on-overflow variants of their integer arithmetic instructions? If the explanation really is that simple, it's pretty disappointing.

tialaramex 4 days ago | parent | next [-]

You can tell the compiler that you want your crate to perform the checks regardless.

https://doc.rust-lang.org/cargo/reference/profiles.html#over...

You can also either (in nightly Rust) use the strict APIs which make it explicit that you want the overflow panics, or, (stably) use the checked APIs and then do whatever makes sense, which could include explicitly panic when overflow would happen unexpectedly.

This would let you have e.g. code where most arithmetic is checked, but a tight inner loop you're pretty sure won't overflow only has checks in debug (in release it will wrap, but you should not rely on that for correctness, unintended overflow is a bug)

kbolino 4 days ago | parent | prev [-]

> I assume this implies that common processor architectures (x86_64, aarch64) lack trap-on-overflow variants of their integer arithmetic instructions?

Yes*. But all modern instruction sets have condition flags and conditional instructions, so it's still very much possible to implement the checks robustly in machine code. However, doing so would generally require injecting at least one additional conditional-branch instruction, and in some cases, switching from non-flag-setting instructions to flag-setting instructions (which can be slower).

* = true "trap on overflow" existed in 32-bit x86 but was tricky to use and got removed when going to 64-bit