Почему целое число интерпретируется как двойной рендеринг ноль?
printf("%f", 20);
результаты в выводе 0.000000
не 20.000000
, Я предполагаю, что это связано с тем, как int представлен в памяти и как double представлен в памяти. Удивительно для меня, как бы я не перешивал 20
Например, увеличив число, вы получите 0.000000
, Может кто-нибудь объяснить, пожалуйста, основную механику этого?
3 ответа
Скорее всего, вы компилируете свой код на платформе /ABI, где даже для функций varargs данные передаются в регистры и, в частности, в разные регистры для целочисленных значений / значений с плавающей запятой. x86_64 в Linux/OS X ведет себя так.
Вызывающая сторона должна передать целое число, поэтому она помещает его в rsi
; на другой стороне, printf
ожидает значение с плавающей запятой, поэтому он пытается прочитать его из xmm0
, Независимо от того, как вы меняете целочисленный аргумент на любое другое значение printf
не будет затронут - если просто напечатать все, что происходит, чтобы остаться в xmm0
в момент звонка.
Вы можете проверить, так ли это на самом деле, изменив свой вызов на:
printf("%f", 20, 123.45);
если он работает так, как я описал, вы должны увидеть 123.45 (вызывающий здесь помещает 123.45 в xmm0
, поскольку это первый параметр с плавающей запятой, переданный функции; printf
ведет себя как и прежде, но на этот раз находит другое значение в xmm0
).
Прежде всего, это неопределенное поведение. %f
ожидает аргумент типа float
/ double
, Проходя int
делает несовместимый тип и, следовательно, вызывает UB.
квотирование C11
Глава § 7.21.6.1, fprintf()
f
,F
double
аргумент, представляющий число с плавающей точкой [...]
float
также допускается, так как из-за правила продвижения аргумента по умолчанию, он будет повышен до double
который является ожидаемым типом там, так что либо double
или float
приемлемо, но не int
,
...и это все. UB, это, ну, UB. Вы не можете попытаться оправдать что-либо с помощью кода, создающего UB.
Тем не менее, с включенными надлежащими уровнями предупреждения код вообще не должен компилироваться. Тем не менее, если вы решите сделать код компилирующим и сгенерировать двоичный / ассемблерный код, вы можете увидеть разные коды, сгенерированные для разных платформ. Один из таких случаев объясняется в другом ответе Matteo Italia с учетом арки x86_64 в Linux/OS X.
Проблема в том, что ваш компилятор предполагает 20
является int
, Вы можете либо объявить float
переменную и введите ее здесь ИЛИ добавьте приведение типа.
например
printf("%f", (double)20);