6

I found a very strange behavior using the modulo operator.

Given the following code:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

int main()
{   
    uint8_t x = 2;
    uint8_t i;

    for(i=0; i<5; i++)
    {
        x = (x - 1) % 10;
        printf("%d ", x);
    }

    printf("\n");
}

I expect as a result 1 0 3 4 2, but instead I get 1 0 255 4 3.

I think it has something to do with the integral promotion, but I don't understand how the conversion is done.

gomatteo
  • 63
  • 2
  • 4

2 Answers2

9

First of all, in C, % is not the modulo operator. It is the remainder operator.

Otherwise, you're right that integer promotion happens. uint8_t is implicitly converted to an int when it appears as the argument of an arithmetic operator.

So when x reaches 0, then x - 1 will become -1. Then, -1 % 10 is -1 (and not 9), and -1 assigned to uint8_t yields 255 (since unsigned integer overflow is defined in terms of modulo arithmetic).

  • 1
    `unsigned char` is irrelevant here; the type is `uint8_t`. It's true that `uint8_t` *cannot exist* unless `unsigned char` is exactly 8 bits, but that's a complex consequence of other requirements of the C standard, and IMO it's outside the scope of this question. – R.. GitHub STOP HELPING ICE Feb 01 '14 at 22:15
  • @R.. But one can't say that "`uint8_t` is always promoted to `int`", right? Usual arithmetic conversions are defined in terms of built-in types, which `uint8_t` is not. We **have** to know the underlying type in order to be able to say anything wise. –  Feb 01 '14 at 22:18
  • Yes one can. `int` is required to have at least the range -32767 to 32767, and `uint8_t` is required to have the range exactly 0 to 255, so it has to have lower conversion rank than `int`. – R.. GitHub STOP HELPING ICE Feb 01 '14 at 22:30
  • @R.. Updated my answer accordingly, is it OK now? –  Feb 01 '14 at 22:33
  • the remainder operator *IS* the modulo operator. – Chris Sep 30 '18 at 16:01
  • @Chris remainder **IS NOT** modulo -> https://stackoverflow.com/a/13683709/2295964 – Yan Foto Sep 22 '21 at 09:10
  • @YanFoto It is absolutely the remainder operator, restricted to positive numbers. Playing with the scope or context of a discussion is not grounds for a comment like that. There is a valid argument that the modulo absolutely is the remainder operator, and restricting the modulus to the remainder(abs(number)) (positive numbers) is an implementation decision. – Chris Sep 22 '21 at 12:29
  • @Chris I'm not sure if I get your point: the question is about negative numbers and if `%` was the modulo operator it would yield a different result as the remainder operator. Or do you mean that in C modulo is defined as remainder and has nothing to do with modular arithmetic? – Yan Foto Sep 22 '21 at 12:35
  • @YanFoto In "math" the "modulus" is an alternative way to refer to the absolute value of a number **outside of this field**, and the modulus of one number by another is defined as the remainder of the one divided by the other. If you combine both flavors of the term in implementation, you might arrive at the definition you are familiar with (absolute remainder) or just the the remainder. There is no referee (other than yourself) running around enforcing this stuff. – Chris Sep 22 '21 at 12:35
  • @YanFoto in general, across different implementations, you have to abs your number prior to applying modulus because you don't know how the implementor applied his implementation. That doesn't mean it isn't the modulus... and this artificial differentiation between modulus and remainder is novel, AFAIK and after brief googling for confirmation. – Chris Sep 22 '21 at 12:38
  • @Chris my second comment came a few seconds after your second comment that answers the question in my second comment. Technicality aside, those who stumble upon this question have assumed `%` to be the mathematical *modulus* in C, which obviously is not the case! – Yan Foto Sep 22 '21 at 12:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/237365/discussion-between-yan-foto-and-chris). – Yan Foto Sep 22 '21 at 12:40
  • @YanFoto It *is* the mathematical modulus. The modulus would be defined, in a math textbook as: the remainder on Z+ (the positive Natural numbers). Putting this on a computer, where there are a variety of number types) yields an opportunity to innovate and define modulus as the remainder of the norm on all Z (positive and negative numbers). If you increase the range of the math modulus to include negative numbers without modifying it, it **is** the remainder. – Chris Sep 22 '21 at 12:44
  • @Chris again: I'm not getting on this train of technicality. Again, people are looking for the **common residue** and not remainders (thus the question here!) because they know it from other programming languages. Pointing out that they are not familiar with mathematical congruence or specific implementation of a programming language is far from helpful. – Yan Foto Sep 22 '21 at 20:16
  • @YanFoto Absolutely every element of this discussion is hair splitting technicalities, regardless of the side you fall out on. – Chris Sep 22 '21 at 21:56
  • @YanFoto the question you've referenced is an incorrect algorithmic definition of a math concept. Math starts from zero and moves right to left in the negative direction and left to right in the positive. Mod, for the domain of all natural numbers, is remainder(abs(x), abs(y)). In math the modulus is just another word for abs(.) and the whole remainder thing is a CS innovation. – Chris Sep 22 '21 at 22:18
  • @Chris you want authoritative sources, [here's one](https://mathworld.wolfram.com/Congruence.html), you want acknowledgment: there's none! – Yan Foto Sep 23 '21 at 08:01
  • @YanFoto I don't know what you think you have proven, did you read it? My favorite is property # 7: "a - b is congruent to a' - b'mod m". If we are to agree with your original source on SO, and supposing b were -21, m were 4 and a were 2, then 23 mod 4 is most definitely not equal to -1 = 2 - 3. – Chris Sep 23 '21 at 13:16
  • @Chris I don't have to prove anything. I told you what people are looking for. Again: people don't want the remainder, they want the common residue as [this source](https://mathworld.wolfram.com/Modulus.html) also states. Because they know it from other programming languages. You are stuck on technicality that helps absolutely no one. – Yan Foto Sep 23 '21 at 13:32
  • @YanFoto The common residue is the absolute remainder bud, and it has nothing to do with that up voted (and completely wrong) stack overflow question you referenced. – Chris Sep 23 '21 at 15:29
  • @YanFoto the point is that by "people" you are referring to a specific sub culture of people. The definition of remainder is the "common residue or remainder", and the difference between remainder(a/b) and |remainder(a/b)| is what we are talking about: this is not "remainder" and "something else". It is all one concept. And, with the C version of the modulo the congruency rules are still satisfied. – Chris Sep 23 '21 at 15:36
  • @YanFoto This is an old comment. It is correct. You came onto this thread uninvited, unprompted, with a hostile, authoritative tone; you've referenced a completely incorrect misunderstanding of the modulus. Another fine example of a violation of the authoritative definition of modulus can be found in the scripting language `python3`: `2 - (-1) = 3 is not congruent to 2 - (-1%5) = -2` as calculated by `python3`. So the python3 modulus is neither the remainder nor the modulus operator. I think you are saving face, as I am; and will continue to fight on here with increasing issues. Flagged – Chris Sep 23 '21 at 20:34
  • @YanFoto and another example why the non-C style (Fortran style) **interpretation** of the modulus is arbitrary, consider that `1mod(5) != 1mod(-5)`, `1mod(5) == -1mod(-5)`. This is another contradiction, since `mod(5) === mod(-5)` by definition, whereas neither the incongruency nor the definition is violated in the C99-interpretation. Bear in mind Donald Knuth is the one that coined the `a - n * floor(a / n)` definition, which is creating this confusion. – Chris Sep 23 '21 at 21:19
  • @YanFoto and the discussion is summarized [here](https://torstencurdt.com/tech/posts/modulo-of-negative-numbers/) where the vast majority of implemented languages have agreed that Knuth's definition is problematic. – Chris Sep 23 '21 at 21:26
  • 1
    @Chris I really appreciate your time and effort in explaining and digging deep in this matter. – Yan Foto Sep 23 '21 at 21:27
  • 1
    @YanFoto Thank you for saying that; you too -- ultimately I learned a lot. – Chris Sep 23 '21 at 21:28
7

I'm not clear on why you expected a 3 after the 0; mathematically, 0-1 yields -1, and the remainder of -1 divided by 10 is either -1 or 9 depending on your definition of remainder.

With that said, what happens in your step where x transitions from 0 to 255 is that 0-1 yields -1, and C's % operator defines remainder such that you get a result of -1. The key is what happens next: you assign -1 into a variable of type uint8_t. Since -1 is outside the range of representable values (0 to 255) and the type is unsigned, the conversion takes place by reducing the value modulo 256 (one plus the max value) into the range, thus yielding 255.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711