Как printf% f работает на 32-битном float - PullRequest
0 голосов
/ 10 декабря 2018

Код формата %f printf указан как работающий со значением типа double [ source ].Однако простая тестовая программа демонстрирует, что она также может использоваться со значениями типа float.Как это работает?

Эквивалентный случай с целочисленными типами (например, int и long long int) "работает", потому что на машинах с прямым порядком байтов младшие байты 32-битного целого числа случаются сперекрывают младшие байты 64-разрядного целого числа, поэтому, если верхние биты равны 0, вы получите «правильный» ответ.

Но это не может быть в случае floatи double, потому что форматы с плавающей запятой не являются взаимозаменяемыми, как это.Вы просто не можете напечатать значение с плавающей запятой как двойное без выполнения (довольно сложного) преобразования в другой формат.Попытка сделать это с помощью ввода типов будет просто выводить мусор.

Кроме того, printf является вариативным.Компилятор не обязательно знает во время компиляции, какие спецификаторы формата будут использоваться, только типы аргументов.Поэтому единственное, что я могу предположить, это то, что все float значения, передаваемые в функцию с переменным числом, будут безоговорочно обновлены до double.Но меня поражает, что я мог так долго программировать на C и не знать об этом.

Как C делает здесь неявное принуждение?

Источник:

#include <stdio.h>
#include <math.h>

int main() {
  float x[2] = {M_PI, 0.0};
  printf("value of x: %.16e\n", x[0]);
  printf("size of x: %lu\n", sizeof(x[0]));

  double *xp = (double *)&x[0];
  printf("value of *xp: %.16e\n", *xp);
  printf("size of *xp: %lu\n", sizeof(*xp));

  double y = M_PI;
  printf("value of y: %.16e\n", y);
  printf("size of y: %lu\n", sizeof(y));

  int i[2] = {1234, 0};
  printf("value of i: %lld\n", i[0]);
  printf("sizeof of i: %lu\n", sizeof(i[0]));

  long long *ip = (long long *)&i[0];
  printf("value of i: %lld\n", *ip);
  printf("sizeof of i: %lu\n", sizeof(*ip));

  return 0;
}

Вывод:

value of x: 3.1415927410125732e+00
size of x: 4
value of *xp: 5.3286462644388174e-315
size of *xp: 8
value of y: 3.1415926535897931e+00
size of y: 8
value of i: 1234
sizeof of i: 4
value of i: 1234
sizeof of i: 8

Команда и версия компиляции:

$ gcc test_float.c -o test_float
$ gcc --version
gcc (Ubuntu 5.5.0-12ubuntu1~16.04) 5.5.0 20171010
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Ответы [ 2 ]

0 голосов
/ 10 декабря 2018

См. Вариативные аргументы и Повышение аргументов по умолчанию :

При вызове функции каждый аргумент, являющийся частью списка переменных аргументов, подвергается специальной обработке.неявные преобразования, известные как продвижение аргументов по умолчанию .

.

Каждый аргумент целочисленного типа подвергается целочисленному продвижению (см. ниже), а каждый аргумент типа float неявно преобразуется в тип double .

0 голосов
/ 10 декабря 2018

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

Да - это точно.

Из стандарта C;

6.5.2.2.7 Нотация многоточия в деклараторе прототипа функции приводит к остановке преобразования типа аргумента после последнего объявленного параметра.Повышение аргументов по умолчанию выполняется на конечных аргументах.

А правила «продвижения аргументов по умолчанию» будут повышать float до double, соответствующая часть:

6.5.2.2.6 Если выражение, обозначающее вызываемую функцию, имеет тип, который не включает в себя прототип, целочисленные преобразования выполняются для каждого аргумента, а аргументы с типом float повышаются до двойного.

...