Почему целое число интерпретируется как двойной рендеринг ноль?

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);
Другие вопросы по тегам