Какова основная логика функции printf?

В последнее время я играю с C, чтобы проверить разные вещи.

printf("%d", 0.4);

напечатает -1717986918.

Я понимаю, что это должно быть связано с двоичным представлением числа с плавающей запятой. Однако после использования некоторых онлайн-ресурсов для преобразования 0,4 в число с плавающей запятой двоичное представление не соответствует -171986918. Пожалуйста, объясните, что на самом деле произошло за этим фрагментом кода.

PS: я использую компилятор gcc и запускаю его в 64-битной Windows.

sizeof(int) и sizeof(double) (вероятно, 0.4) имеют разное количество байтов (скорее всего).
Fiddling Bits 20.12.2020 02:33

@FiddlingBits Спасибо, видимо, это преобразование пропускает первые 16 бит двойного значения. Один последующий вопрос: можно ли с уверенностью сказать, что тогда C использует double как тип float по умолчанию?

CheeseS 20.12.2020 02:41

Константа 0.4 имеет тип double. Таким образом, он не соответствует должным образом директиве %d, в результате чего ваш вызов printf имеет неопределенное поведение. То, что «на самом деле происходит», зависит от деталей вашей реализации printf и, возможно, от других факторов. Это не очень полезная деталь для изучения, кроме извлечения урока о том, что вы должны правильно набирать текст, чтобы ваши аргументы printf соответствовали строке формата. Какие бы детали вы ни выяснили, они не могут быть надежно переданы.

John Bollinger 20.12.2020 02:54

Обратите внимание, что если вы включили предупреждения (например, gcc -Wall), оператор printf будет помечен компилятором.

Craig Estey 20.12.2020 03:12

Некоторые системы — 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 , он извлекает первое двойное значение из регистров с плавающей запятой.

Jonathan Leffler 20.12.2020 03:33

На такое поведение нельзя полагаться. Большинство исторических систем не вели бы себя так. Но (по крайней мере, некоторые из) используемых ABI x86/64 поддаются такому злоупотреблению.

Jonathan Leffler 20.12.2020 03:35
Стандарт C11 — 7.21.6.1 Функция fprintf (p9) также требует, чтобы спецификаторы преобразования были правильного типа для аргументов — поэтому просто потому, что компиляция с несложными параметрами может иногда позволить ей работать — не работает означает, что это правильно сформированный код :) Вывод -1717986918, вероятно, означает, что регистр RSI не инициализирован.
David C. Rankin 20.12.2020 05:31

@FiddlingBits без суффиксов, тогда литерал с плавающей запятой всегда double. f и L сделать это float и long double соответственно

phuclv 20.12.2020 07:27

В следующий раз компилируйте с gcc -Wall -Wextra -g

Basile Starynkevitch 20.12.2020 08:13
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
189
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я понимаю, что это должно быть связано с двоичным представлением

Нет, это неопределенное поведение (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.
Еще УБ. Все может случиться.

Другие вопросы по теме