Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> (because of e.g. casting to unsigned and allowing the same overflow to happen anyway) only work correctly on twos-complement anyway

Unsigned arithmetic never overflows, and guarantees two's-complement behavior, because unsigned arithmetic is always carried out modulo 2^n:

> A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type. (6.2.5, Types)

Doing the computation in unsigned always does the "right thing"; the thing that one needs to be careful of with this approach is the conversion of the final result back to the desired signed type (which is very easy to get subtly wrong).



A quibble on wording: Unsigned overflow is not "twos-complement". It gives you the same bit patterns that typical two's-complement overflow gives you, but strictly speaking two's-complement is a representation for signed values.


Wrapping around the modulus to me is an "overflow", although maybe the spec doesn't use the word that way


There is also a difference in x86 assembly, and probably others.

For unsigned operations the carry flag is used, and for signed operations, the overflow flag is used.


Most compilers will translate unsigned (x + y < x) to CF usage.


Right, there are (at least) two ways to describe this.

One is that unsigned arithmetic can overflow, and the behavior on overflow is defined to wrap around.

Another is to say that unsigned arithmetic cannot overflow because the result wraps around.

Both correctly describe the way it works; they just use the word "overflow" in different ways.

The C standard chooses the second way of describing it.


And are there standard primitives to do this correctly (signed-unsigned-signed conversion) that never invoke undefined behavior?


Signed to unsigned conversion is fully defined (and does the two's complement thing):

> Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type (6.3.1.3 Signed and unsigned integers)

Unsigned to signed is the hard direction. If the result would be positive (i.e. in range for the signed type), then it just works, but if it would be negative, the result is implementation-defined (but note: not undefined). You can further work around this with various constructs that are ugly and verbose, but fully defined and compilers are able to optimize away. For example, `x <= INT_MAX ? (int)x : (int)(x + INT_MIN) + INT_MIN` works if int has a twos-complement representation (finally guaranteed in C2x, and already guaranteed well before then for the intN_t types), and is optimized away entirely by most compilers.


Interesting. I guess most/many arch's overflow flag is set when the sign bit changes and the carry flag when the result rollsover the word size.

I think most people colloquially call going A + 1 = B where B < A an overflow. Interesting. I knew they're different things, but never really thought about my word choice.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: