4 ms·
Could you list some of the downsides & compromises in the case I made?
by virtualritz 5y ago
Could you list some of the downsides & compromises in the case I made?
- kstenerud 5y ago- silent unintended underflow behavior - dangers of mixed signed/unsigned code (signed values MUST exist, but unsigned values don't have to) - more cognitive load to read and understand unsigned underflow detection code + issues once you exceed the halfway point This is why Java offered signed integer types only (I think they should have allowed unsigned as well, but for different reasons)
- virtualritz 5y ago> silent unintended underflow behavior Please show me an example in the wild that could not be trivially caught/alleviated. With 'trivially' I mean the code that needs to be written to prevent it, not the thinking you need to do to consider it. As for the latter: this is what I get paid for. Writing the code out is necessary but not what I really get paid for (unless I were a very slow typist, that is). I find that most of the time I was simply too lazy. Or rather: I wrote C/C++ code for 20+ years. I used int and didn't think (unintended rhyme). Now I write (mostly) Rust I find myself considering the edge cases regularly and also covering them because the compiler forces me to. See also this comment [1]. I.e. a - 1 + b where a is always positive (incl. 0) and b is always > 1 'just' works with int (and unsigned [overflow] too!) in C/C++. In Rust you will get a panic when a = 0 which will then make you think what you're doing and simply reorder this to a + b - 1. I find that this is the 'cognitive load' that needs to be applied commonly and I'm pretty fine with that. > dangers of mixed signed/unsigned code Alleviated by simply not mixing. There is a reason languages like Rust simply do not allow this without explicit typecasts. And those, in cases where one type is signed and the other is not and they have equal width translate to "all bets are off". > more cognitive load to read and understand unsigned underflow detection code [...] See first point. IMHO this is simply the same point expressed differently. Underflow happens when you subtract. Ensure you do not subtract a larger value from a smaller. This is as easy as writing max(a, b) - min(a, b) or what author of [1] does. Same as abs(a - b) but works with unsigned. Cognitive load? I do not see any and I agree with the author of the article that this is easier to read. [1] https://news.ycombinator.com/item?id=29767877 https://news.ycombinator.com/item?id=29767877
- kstenerud 5y agoThis is one of those "If everyone would just do X" kinds of situations, which is why I'm against it. Things go wrong all the time - in fact it's a miracle anything works at all. So when I see "code that needs to be written to prevent it", I let out a sardonic laugh; people just don't work that way. People WILL forget to check, or implement the check wrong (and it won't be discovered until the next black swan event), or some invariant will change in future and nobody will know to update this code. The code doesn't care because the compiler doesn't enforce the invariants, so you're left with bare and likely confusing (and probably eventually undefined) behavior. I for one would LOVE it if popular languages could be instructed to inject invariant enforcement code (not asserts) at certain boundaries. It would help with a whole host of problems! When you cross zero with a signed integer calculation, it's easy to find out, and the code has low cognitive overhead. Also, with code generally structured this way, bad input propagation tends to get stopped earlier because of the number of functions that check against negative values. It's also possible to underflow check with unsigned, but the code is more complicated and can't stop propagation if it fails, and if you are allowing values > the signed positive limit (or unwittingly allowing it), your overflow detection code gets even more complicated (or wrong) due to the reduced overhead space. You COULD do a bunch of checks beforehand, but that's not always feasible due to the combination of operations being done on the operands, and once again people are people. Even though many modern languages block uncast type conversions, that still doesn't protect you from converting a negative integer to an unsigned integer. Yes, you SHOULD be checking before the cast, buuuuuuut... What we want to do is reduce the number of ways things can go wrong, reduce the fallout of an errant process, and minimize cognitive load to reduce bugs overall. Making that worse just to double the positive integer space doesn't make sense to me.
- whattheactualf3 5y agoHere's an example: unsigned int length_a = 10; unsigned int length_b = 20; if (length_a - length_b < 0) { printf("negative diff\n"); } else { printf("positive diff\n"); } I would argue that there is plenty of code like that in the wild. It looks innocent and harmless but it gives a completely surprising result.
- 5y ago