Daniel Lemire's blog

, 11 min read

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

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

  1. Patrice Tremblay says:

    Sorry, but I miss the point of such code, then. If the value of -range % range is always 0, what’s the intent?

    1. It is only zero when you have signed types. It is non trivial when you have unsigned types. You should be able to test it out for yourself.

  2. Robert Godin says:

    Petite coquille : “just by examining just”

  3. Marco says:

    As I was telling a student of mine yesterday: you are not supposed to read new code and understand it right away all of the time.

    I reckon a whole series of posts could be inspired by that statement.

    1. Eden Segal says:

      That resonated with me, it’s the opposite of “software should be readable like proze” which always sounded odd to me.

  4. Alex JB says:

    Tiny amendment: % is the percent sign, not the ampersand (that’s &).

  5. Bert says:

    This is a neat trick! But I found something surprising with unsigned short: https://gcc.godbolt.org/z/4xWo1G. It looks like -n is type-promoted (probably to a signed integer? I haven’t checked the standard) so unless you re-cast it (I’m not sure that cast isn’t UB…) you don’t get the expected result.

  6. Nathan Kurz says:

    You do an excellent job of explaining what this seemingly odd construction does, but it might be good to mention in the intro why “-n %n” is a common construction in the first place. Stealing from an HN comment making the same suggestions, “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.”

    1. Yes, this fault in my blog post was pointed out to me on twitter.

  7. dan sullivan says:

    A nit: I believe you meant to write ‘percent’ (%) and not ‘ampersand’ (&). cheers.

  8. Michael Rademeyer says:

    I dont understand. I tried it in C# and printed out the result of -8%8 and i got 0 so i dont get what is supposed to be happening

    1. In C#, try (4294967295 - n + 1) % n if n is of type uint.

  9. Bert says:

    I noticed unexpected results with unsigned short and unsigned char. It’s worth noting that -n will be converted to a signed int if n is a narrower type.

  10. Derrick says:

    I’m finding out the hard way that gcc 10 seems to optimize this expression to 0 for unsigned values. I’m having to cast to signed to get the unary minus to take effect, and then cast back to unsigned for the modulo to work the way it should. YMMV.

    1. See my reference to the standard C++ library in my blog post. It is used in GCC 10.

  11. Antoine says:

    In programming languages (like Go and C++) where unsigned integers
    cannot overflow, then there is always one, and only one, additive
    inverse to every integer value.

    This sentence is a bit confusing to read. If unsigned integers cannot overflow (i.e. unsigned overflow doesn’t exist), then there can be no (unsigned) additive inverse.

    By the way, I expected this post to discuss something else entirely. In Python, you have the following:

    >>> (-3) % 5
    2
    >>> (-3) % (-5)
    -3
    >>> 3 % (-5)
    -2

    But in C (and presumably other C-like languages), the results are -3, -3, 3 respectively.

    1. This sentence is a bit confusing to read.

      I agree. I rephrased it as “wrap around”.

    2. By the way, I expected this post to discuss something else entirely. In Python, you have the following: (…) But in C (and presumably other C-like languages), the results are -3, -3, 3 respectively.

      The division and remainder (modulo) between signed integers are defined differently depending on the programming. E.g., we all agree that 4/3 is 1. But what is -4/3? It could be -2 or -1. You can round toward zero or toward minus infinity.