5 ms·
Quote from their style guide: > The fact that unsigned arithmetic doesn't model the behavior of a simple integer, but is instead defined by the standard to mod
by dbdr 11mo ago
Quote from their style guide:
> The fact that unsigned arithmetic doesn't model the behavior of a simple integer, but is instead defined by the standard to model modular arithmetic (wrapping around on overflow/underflow), means that a significant class of bugs cannot be diagnosed by the compiler.
Fair enough, but signed arithmetic doesn't model the behavior of a "simple integer" (supposedly the mathematical concept) either. Instead, overflow in signed arithmetic is undefined behavior. Does that actually lead to the compiler being able to diagnose bugs? What's the claimed benefit exactly?
- gf000 11mo agoI believe some logic behind may be that you can't recognize an overflow has happened with unsigned, but with signed you can recognize over and underflows in certain cases by simply checking if it's a non-negative number. At least I believe Java decided on signed integers for similar reasons. But if it's indeed UB in C++, it doesn't make sense.
- alpinisme 11mo agoIt’s the opposite in cpp: unsigned integer overflow is undefined but signed overflow is defined as wrapping
- debugnik 11mo agoDid you mix up unsigned and signed by mistake? Because in C and C++, the wrapping one is unsigned and the here-be-dragons-on-overflow one is signed.
- alpinisme 11mo agoOh jeeze. I can’t believe I flipped that. I find myself wishing I could delete my comment.
- sltkr 11mo agoNo, it's the opposite. UNSIGNED overflow wraps around. SIGNED overflow is undefined behavior. This leads to fun behavior. Consider these functions which differ only in the type of the loop variable: int foo() { for (int i = 1; i > 0; ++i) {} return 42; } int bar() { for (unsigned i = 1; i > 0; ++i) {} return 42; } If you compile these with GCC with optimization enabled, the result is: foo(): .L2: jmp .L2 bar(): mov eax, 42 ret That is, foo() gets compiled into an infinite loop, while the loop in bar() is eliminated instead. This is because the compiler may assume only in the first case that i will never overflow.
- pjmlp 11mo agoGosling on the matter, > One of the little experiments I tried was asking people about the rules for unsigned arithmetic in C. It turns out nobody understands how unsigned arithmetic in C works. There are a few obvious things that people understand, but many people don't understand it. -- https://www.artima.com/articles/james-gosling-on-java-may-2001 https://www.artima.com/articles/james-gosling-on-java-may-20...
- mzs 11mo agoFor C23 at least: https://gustedt.wordpress.com/2022/12/18/checked-integer-arithmetic-in-the-prospect-of-c23/ https://gustedt.wordpress.com/2022/12/18/checked-integer-ari... #include <stdckdint.h>
- sltkr 11mo agoTools like UBsan [1] can detect integer overflow in debug builds, and are used internally at Google to run automated tests. So if you use a signed integer, there is a chance that overflows are caught in tests. 1. https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
- im3w1l 11mo agoI feel like there should be a "sign-queer" integer. It's range is the intersection of unsigned and signed integers, in other words the high bit must be unset. Wrapping around on either end is illegal and should be diagnosed in debug builds. In production builds it may either be silently treated as an unsigned integer or generate a diagnostic. Implicitly casting to either signed or unsigned integer is allowed and does not generate a warning.
- sltkr 11mo agoThat's the behavior you get in Zig, if you declare a variable of type `u31`: an unsigned 31-bit integer that's implicitly convertible to `u32` or `i32`. Also in Zig overflow is illegal for both signed and unsigned integers, and guaranteed to be detected when building in Safe mode (but not in Fast mode). There is a separate set of operators for wrapping arithmetic.
- dzaima 11mo agoA sanitizer or static analysis or any other tool can unconditionally give you a warning/error on signed integer overflow. Whereas that's invalid for unsigned integers as they have well-defined behavior, and things depend on said overflow (hashing, bitwise magic, temporary wrapping that unwraps later, etc). Ideally there'd be a third type for unsigned-non-wrapping-integer (and llvm even supports a UB-on-unsigned-wrapping flag for arith ops in its IR that largely goes unused for C/C++), but alas such doesn't exist. Half-relatedly, this previously appeared as a discussion point on Linux (though Linus really did not like the concept of multiple unsigned types and as such it didn't go anywhere iirc).