В последнее время я играю с C, чтобы проверить разные вещи.
printf("%d", 0.4);
напечатает -1717986918.
Я понимаю, что это должно быть связано с двоичным представлением числа с плавающей запятой. Однако после использования некоторых онлайн-ресурсов для преобразования 0,4 в число с плавающей запятой двоичное представление не соответствует -171986918. Пожалуйста, объясните, что на самом деле произошло за этим фрагментом кода.
PS: я использую компилятор gcc и запускаю его в 64-битной Windows.
@FiddlingBits Спасибо, видимо, это преобразование пропускает первые 16 бит двойного значения. Один последующий вопрос: можно ли с уверенностью сказать, что тогда C использует double как тип float по умолчанию?
Константа 0.4
имеет тип double
. Таким образом, он не соответствует должным образом директиве %d
, в результате чего ваш вызов printf
имеет неопределенное поведение. То, что «на самом деле происходит», зависит от деталей вашей реализации printf
и, возможно, от других факторов. Это не очень полезная деталь для изучения, кроме извлечения урока о том, что вы должны правильно набирать текст, чтобы ваши аргументы printf
соответствовали строке формата. Какие бы детали вы ни выяснили, они не могут быть надежно переданы.
Обратите внимание, что если вы включили предупреждения (например, gcc -Wall
), оператор printf
будет помечен компилятором.
Некоторые системы — macOS Mojave 10.14.6 — одна из них, но я считаю, что Linux x86/64 — другая, имеют ABI, который передает целые числа и удваивает в разных наборах регистров. Это означает, что если компилятор не настроен на суетливость, вы получите «правильный» результат от printf("Integer: %4d; double: %13.6g\n", 3.141592654, 9876);
, то есть он выдаст «Целое число: 9876; целое число из целочисленных регистров, и когда он обрабатывает , give or take some spaces. That's because when
, он извлекает первое двойное значение из регистров с плавающей запятой.
На такое поведение нельзя полагаться. Большинство исторических систем не вели бы себя так. Но (по крайней мере, некоторые из) используемых ABI x86/64 поддаются такому злоупотреблению.
:)
Вывод -1717986918
, вероятно, означает, что регистр RSI
не инициализирован.
@FiddlingBits без суффиксов, тогда литерал с плавающей запятой всегда double
. f
и L
сделать это float
и long double
соответственно
В следующий раз компилируйте с gcc -Wall -Wextra -g
Я понимаю, что это должно быть связано с двоичным представлением
Нет, это неопределенное поведение (UB).
"%d"
с double
не совпадает.
Если спецификация преобразования недействительна, поведение не определено. C17dr § 7.21.6.1 9
Вывод и поведение не определяются (UB) языком. Все может случиться.
В некоторых системах UB может видеть часть двоичного шаблона, интерпретируемую как int
.
В других системах int
и double
передаются по-разному, и видимый вывод основан на мусоре.
В другой системе или просто в другой день случаются плохие вещи.
Это все неопределенное поведение (UB).
объясните что на самом деле произошло
Чтобы получить представление о том, что могло произойти на машине OP, используйте шестнадцатеричные выходные данные.
printf("%x\n", (unsigned) -1717986918); // UB to print a negative `int` with `"%x"`
printf("%a\n", 0.4);
9999999a
0x1.999999999999ap-2
Таким образом, похоже, что наименее значащие 4 байта значащей 0.4
были интерпретированы как int
.
Еще УБ. Все может случиться.
sizeof(int)
иsizeof(double)
(вероятно,0.4
) имеют разное количество байтов (скорее всего).