Почему printf() продвигает float в double?

Из предыдущего question:

Если вы попытаетесь передать float в printf, оно будет увеличено до doubleдо printf получает его

printf() является вариационной функцией справа? Таким образом, вариационная функция поддерживает аргумент float до double перед его передачей?

Ответ 1

Да, аргументы float для вариационной функции повышаются до double.

черновик стандарта C99 раздел 6.5.2.2 Вызов функций говорит:

[...] и аргументы, которые имеют тип float, которые удваиваются. Они называются аргументом по умолчанию акции. [...]

из черновик проекта С++ раздел 5.2.2 Вызов функции:

[...] тип с плавающей точкой, который подчиняется плавающей запятой promotion (4.6), значение аргумента преобразуется в продвинутого типа перед вызовом. [...]

и раздел 4.6:

Значение значения типа float может быть преобразовано в prvalue типа double. Значение не изменяется

cppreference охватывает конверсии по умолчанию для переменных функции в С++ хорошо:

  • std:: nullptr_t преобразуется в void *
  • аргументы float преобразуются в double, как при продвижении с плавающей запятой
  • bool, char, короткие и не облачные перечисления преобразуются в int или более широкие целые типы, как при целых рекламных кампаниях

Мы можем видеть в C и предположительно в С++ это преобразование поддерживалось для совместимости с K & RC, от Обоснование для языков международного стандартного программирования-C (акцент мой):

Для совместимости с прошлой практикой все рекламные акты аргументов происходят как описанный в K & R в отсутствие декларации прототипа, , включая не всегда желательно продвижение поплавка в double.

Ответ 2

Что касается части почему, она проста: стандарты C (и С++) считают double "типом с плавающей запятой по умолчанию". Не float (это то, что многие из нас программисты по умолчанию используют при использовании чисел с плавающей запятой).

Это можно увидеть, наблюдая:

  • 3.14 - это double (если вы хотите float, вам нужно сделать дополнительный шаг и добавить f)
  • Стандартные математические функции принимают по умолчанию double (например, sin() принимает double, если вы хотите float использовать sinf())

При этом представляется более "естественным", что float был бы повышен до double в вызове вариационной функции, учитывая, что double является "естественным" значением по умолчанию на языке.

Ответ 3

Для прототипа функции тип float только автоматически продвигается 1 при использовании в трейлинг-аргументах. Функция print использует следующие функции:

int printf(const char * restrict format, ...);

1 (Цитируется по: ISO/IEC 9899: 201x 6.5.2.2 Функциональные вызовы)
6. целые рекламные акции выполняются для каждого аргумента и аргументы, которые имеют тип float, которые удваиваются. Они называются аргументом по умолчанию акции.
7. Аргумент по умолчанию рекламные акции выполняются по завершающим аргументам.

Ответ 4

Потому что стандарт (C99 или C11) так говорит. См. ответ на 2501.

Для этого существует несколько прагматических причин: история (первые реализации C были использованы для системного программирования, где операции с плавающей запятой не имеют значения) и тот факт, что на текущем (планшет, рабочий стол, сервер...) процессоры, арифметические операции на double примерно так же эффективны, как float (но некоторые дешевые микроконтроллеры не имеют FPU или могут добавлять только float по аппаратным средствам и требуют библиотеки для каждой операции на double), Наконец, я предполагаю, что такое правило допускает несколько более простые вызывающие соглашения и ABI с.

Подумайте о float как о типе short double (который, конечно же, является незаконным в C). A float полезен, главным образом, когда вам требуется компактная память (и может позволить себе потеря точности). Подробнее см. http://floating-point-gui.de/.