Это не Undefined Behavior, это просто критическое изменение стандарта языка C между C89 и C99.
В C89 целочисленные константы, такие как 4008636143, которые не помещаются в int
или long int
, но помещаются в unsigned int
, являются беззнаковыми, но в C99 они либо long int
, либо long long int
(в зависимости от того, какая из них является наименьшей, которая может содержать значение ). В результате все выражения оцениваются с использованием 64 бит, что приводит к неправильному ответу.
Visual Studio является компилятором C89 и поэтому приводит к поведению C89, но эта ссылка Ideone компилируется в режиме C99.
Это становится более очевидным, если вы скомпилируете с помощью GCC, используя -Wall
:
test.c: In function ‘divisible15’:
test.c:8:3: warning: this decimal constant is unsigned only in ISO C90
Из C89 §3.1.3.2:
Тип целочисленной константы является первым из соответствующего списка, в котором может быть представлено ее значение. Десятичное без суффикса: int, long int, unsigned long int; восьмеричное или шестнадцатеричное без суффикса: int, unsigned int, long int, unsigned long int; [...]
C99 §6.4.4.1/5-6:
5) Тип целочисленной константы является первым из соответствующего списка, в котором может быть представлено ее значение.
Suffix | Decimal Constant | Octal or Hexadecimal Constant
-------+------------------+------------------------------
none | int | int
| long int | unsigned int
| long long int | long int
| | unsigned long int
| | long long int
| | unsigned long long int
-------+------------------+------------------------------
[...]
6) Если целочисленная константа не может быть представлена ни одним типом в своем списке, она может иметь расширенный целочисленный тип, если расширенный целочисленный тип может представлять ее значение. Если все типы в списке для константы подписаны, то расширенный целочисленный тип должен быть подписан. [...]
Для полноты картины C++03 на самом деле имеет Undefined Behavior, когда целочисленная константа слишком велика, чтобы поместиться в long int
. Из С++ 03 §2.13.1/2:
Тип целочисленного литерала зависит от его формы, значения и суффикса. Если он десятичный и не имеет суффикса, он имеет первый из этих типов, в которых может быть представлено его значение: int
, long int
; если значение не может быть представлено как long int
, поведение не определено. Если оно восьмеричное или шестнадцатеричное и не имеет суффикса, оно имеет первый из этих типов, в которых может быть представлено его значение: int
, unsigned int
, long int
, unsigned
long int
. [...]
Поведение C++11 идентично C99, см. C++11 §2.14.2/3.
Чтобы гарантировать согласованное поведение кода при компиляции как C89, C99, C++03 и C++11, простое исправление состоит в том, чтобы сделать константу 4008636143 беззнаковой, добавив к ней суффикс u
как 4008636143u
.
person
Adam Rosenfield
schedule
09.09.2013
(x % 15) == 0
? - person asveikau   schedule 10.09.2013int
, умноженное наint
, явно выходит за пределыint
. Кажется, что любой компилятор не пытается предотвратить переполнение, используяlong
для этого шага вычисления. - person millimoose   schedule 10.09.2013x * 4008636143
внутри int? - person andre   schedule 10.09.2013c99 strick
- person Logan Murphy   schedule 10.09.2013int
наint
должен снова бытьint
. Выполнение вычисления с использованиемlong
s не имеет особого смысла, если результат все равно будет переполнять тип самого выражения. Похоже, VS здесь более оптимистичен и старается сохранить информацию на всякий случай. - person millimoose   schedule 10.09.2013-m32
)? - person ninjalj   schedule 10.09.2013return x * (unsigned int)(4008636143) <= (unsigned int)(286331153);
- person lapk   schedule 10.09.2013return x * -(UINT_MAX / 15) <= (UINT_MAX / 15);
, что позволяет избежать всей проблемы. Я запутался, пытаясь понять это, понял, что это можно переписать так, а затем понял, почему это работает :) (Редактировать: это, как и ваш код, очевидно, предполагает определенное значение дляUINT_MAX
.) - person   schedule 10.09.2013x
divisible by 15
, если кому-то интересно) - person Aurélien Ooms   schedule 10.09.2013