Почему операция modulo возвращает неожиданное значение
Почему следующая печать кода 255
?
#include <stdint.h>
#include <stdio.h>
int main(void) {
uint8_t i = 0;
i = (i - 1) % 16;
printf("i: %d\n", i);
return 0;
}
Я предположил 15
, хотя i - 1
оценивает целое число.
Ответы
Ответ 1
Из-за целых рекламных акций в стандарте C. Вкратце: любой тип "меньше", чем int
, преобразуется в int
перед использованием. Вы не можете избежать этого вообще.
Итак, что происходит: i
продвигается до int
. Выражение оценивается как int
(константы, которые вы используете, также int
). Модуль равен -1
. Затем это преобразование преобразуется в uint8_t
: 255
посредством присваивания.
Для printf
, тогда i
с целым числом увеличивается до int
(снова): (int)255
. Однако это не наносит вреда.
Обратите внимание, что в C89 для a < 0
, a % b
необязательно является отрицательным. Он был определен с реализацией и мог быть 15
. Однако, поскольку C99, -1 % 16
гарантированно -1
, поскольку деление должно давать алгебраическое отношение.
Если вы хотите убедиться, что модуль дает положительный результат, вы должны оценить все выражение unsigned
путем литья i
:
i = ((unsigned)i - 1) % 16;
Рекомендация: Включить предупреждения компилятора. По крайней мере, преобразование для назначения должно давать предупреждение об усечении.
Ответ 2
Это связано с тем, что -1 % n
вернет -1
и NOT n - 1
1. Так как i
в этом случае является беззнаковым 8-битным int, он становится 255.
1См. этот вопрос для более подробной информации о том, как modulo для отрицательных целых чисел работает в C/С++.
Ответ 3
Это работает (отображает 15) с компилятором Microsoft C (без stdint.h, поэтому я использовал typedef):
#include <stdio.h>
typedef unsigned char uint8_t;
int main(void) {
uint8_t i = 0;
i = (uint8_t)(i - 1) % 16;
printf("i: %d\n", i);
return 0;
}
Причиной для 255 является то, что (i-1) продвигается до целого числа, а целочисленное деление, используемое для% in C раундов к нулю вместо отрицательной бесконечности (округление к отрицательной бесконечности - это то, как это делается в математике, науке, и другие языки программирования). Таким образом, для C% равен нулю или имеет тот же знак, что и дивиденд (в этом случае -1% 16 == -1), тогда как в математическом модуле равен нулю или имеет тот же знак, что и делитель.