Live data from Hacker News

What the heck is the value of “-n % n” in programming languages?

lemire.me

11–20 of 30 posts

Re: What the heck is the value of “-n % n” in programming languages?

#11
post #3

Did I miss something? I got to the end and still don't know what -n % n actually accomplishes in practice, nor why it's mainly used in high-performance code

It computes (2^b) % n, assuming n is an unsigned b-bit integer. You can't do this directly, since 2^b itself doesn't fit into a b-bit integer.

Re: What the heck is the value of “-n % n” in programming languages?

#12
post #4

First it's important to note that `n` is unsigned; if it's signed the value of `-n % n` is 0, intuitively. For unsigned n, the value is: MAX - n + 1 (where max is the maximum representable value in the type of n, e.g., UINT_MAX). The article explains this nicely. (I thought of 2's complement when reasoning through this, but you don't actually need to assume 2's complement to follow the reasoning). So, `-n % n` comput…

-n % n (where n is unsigned) suffers from the problem that -n calculates a two's complement. That value is implementation-defined, due to the implementation-defined width of the unsigned type. It's calculating ((-n) mod (2^bits)) mod n, where bits is compiler/platform-dependent. ;; TXR Lisp 1> (defun -n%n (n bits) (mod (mod (- n) (expt 2 bits)) n)) -n%n 2> (-n%n 7 8) 4 3> (-n%n 7 16) 2 4> (-n%n 7 17) 4 5> (-n%n 7 18)…

Well, it computes "(MAX - n + 1) % n", where MAX obviously depends on the type.

Re: What the heck is the value of “-n % n” in programming languages?

#13
post #7

Apart from the overall oddness of this, the result of applying the modulo operator to signed arguments is technically undefined behavior in C.

This isn't correct, section 6.5.5 states merely that "The operands of the % operator shall have integer type."

Re: What the heck is the value of “-n % n” in programming languages?

#16
post #13
post #7

Apart from the overall oddness of this, the result of applying the modulo operator to signed arguments is technically undefined behavior in C.

This isn't correct, section 6.5.5 states merely that "The operands of the % operator shall have integer type."

Ah, my bad, it seems this was the case for K&R C but the behavior was since standardized.

Re: What the heck is the value of “-n % n” in programming languages?

#18
post #4

First it's important to note that `n` is unsigned; if it's signed the value of `-n % n` is 0, intuitively. For unsigned n, the value is: MAX - n + 1 (where max is the maximum representable value in the type of n, e.g., UINT_MAX). The article explains this nicely. (I thought of 2's complement when reasoning through this, but you don't actually need to assume 2's complement to follow the reasoning). So, `-n % n` comput…

-n % n (where n is unsigned) suffers from the problem that -n calculates a two's complement. That value is implementation-defined, due to the implementation-defined width of the unsigned type. It's calculating ((-n) mod (2^bits)) mod n, where bits is compiler/platform-dependent. ;; TXR Lisp 1> (defun -n%n (n bits) (mod (mod (- n) (expt 2 bits)) n)) -n%n 2> (-n%n 7 8) 4 3> (-n%n 7 16) 2 4> (-n%n 7 17) 4 5> (-n%n 7 18)…

Woah, there's even a scary gotcha for types that aren't unsigned. E.g., check out what happens with "unsigned short": https://gcc.godbolt.org/z/4xWo1G. It looks like -n is implicitly converted (to signed int, maybe?) so unless you explicitly re-cast it to "unsigned short", you get something unexpected.

Re: What the heck is the value of “-n % n” in programming languages?

#19

> warning C4146: unary minus operator applied to unsigned type, result still unsigned #ifdef _MSVC x = ~x + 1; // "Manual" two's complement to avoid warning. #else x = -x; // Regular two's complement any good C coder knows #endif

If you're going to go to the effort of writing the complement version instead of pragma-suppressing the warning on that line, just remove the ifdef and use that with all compilers.

Re: What the heck is the value of “-n % n” in programming languages?

#20

Earlier quoted context omitted.

-n % n (where n is unsigned) suffers from the problem that -n calculates a two's complement. That value is implementation-defined, due to the implementation-defined width of the unsigned type. It's calculating ((-n) mod (2^bits)) mod n, where bits is compiler/platform-dependent. ;; TXR Lisp 1> (defun -n%n (n bits) (mod (mod (- n) (expt 2 bits)) n)) -n%n 2> (-n%n 7 8) 4 3> (-n%n 7 16) 2 4> (-n%n 7 17) 4 5> (-n%n 7 18)…

Woah, there's even a scary gotcha for types that aren't unsigned. E.g., check out what happens with "unsigned short": https://gcc.godbolt.org/z/4xWo1G . It looks like -n is implicitly converted (to signed int, maybe?) so unless you explicitly re-cast it to "unsigned short", you get something unexpected.

If n is unsigned short then -n means n is first promoted to either int, or unsigned int: the first of those two types which holds all values of unsigned short. Then the - operation is taking place on the resulting value in that promoted type.

An example where unsigned short promotes to unsigned int are platforms where sizeof(short) == sizeof(int). E.g. 16 bit systems where short and int is 16 bits, and long is 32: compilers for 8086 and such.

If the promotion goes to int, the subsequent - is safe; there is no way the resulting value can be that problematic most negative int of two's complement.

On two's complement systems, even if the - is signed, if you convert the value back to unsigned short, you will get the two's complement value, as if the calculation was done in the unsigned type all along.

For instance a 16 bit unsigned short value of 65535 (0xFFFF) will go to 65535 of type int, which will go to -65535, which will then map to 1 if converted to unsigned short.

Post reply on HN