Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

printf and floating point types

41K
30 апреля 2011 года
kisssko
108 / / 28.10.2010
Итак, есть пример кода, который, вопреки предположениям, работает корректно с точки зрения некоего "здравого смысла", но вовсе не так, как предполагается, исходя из документации/стандартов.

Код:
#include <stdio.h>

#define PI 3.1415926535897932385

double dPI=PI;
float  fPI=PI;
char  *formstr="%sPI=%.18g(%d) \n";

int wmain(int argc, wchar_t **argv)
{
    printf(formstr, "d", dPI, (int)dPI);
    printf(formstr, "f", fPI, (int)fPI);
    return 0;
}


Собственно, выводит этот код вот что:
Цитата:

dPI=3.1415926535897931(3)
fPI=3.1415927410125732(3)



Хотя, в соответствии с документацией, вторая printf должна выдать некий бред хотя бы в одном значении, по предполагаемой причине интерпретации 32-х битного float как 64-х битного double, и последующего съезжания адресов параметров. Я что то не понял наверно... Такое ощущение, что в данной ситуации парсер printf определяет размер аргумента по контенту, а не только по строке формата.

278
30 апреля 2011 года
Alexander92
1.1K / / 04.08.2008
Сдается мне, что вы неправы. Бред получится, если вы, наоборот, попробуете вывести значение, которое выходит за пределы float, с флагом "%f".
41K
30 апреля 2011 года
kisssko
108 / / 28.10.2010
Цитата: Alexander92
Сдается мне, что вы неправы. Бред получится, если вы, наоборот, попробуете вывести значение, которое выходит за пределы float, с флагом "%f".



Что то не понял смысла. :( Пример кода можно?

278
30 апреля 2011 года
Alexander92
1.1K / / 04.08.2008
Перечитал еще раз спецификации, понял, что мы оба неправы. И %f, и %g - это double:

[QUOTE=MSDN]
%f - double. Signed value having the form [ – ]dddd.dddd, where dddd is one or more decimal digits. The number of digits before the decimal point depends on the magnitude of the number, and the number of digits after the decimal point depends on the requested precision.

%g - double. Signed value printed in f or e format, whichever is more compact for the given value and precision. The e format is used only when the exponent of the value is less than –4 or greater than or equal to the precision argument. Trailing zeros are truncated, and the decimal point appears only if one or more digits follow it.
[/QUOTE]

Соответственно, в вашем примере все правильно, никакого деления на 32- и 64-битные значения там нет.
41K
30 апреля 2011 года
kisssko
108 / / 28.10.2010
Цитата: Alexander92
Соответственно, в вашем примере все правильно, никакого деления на 32- и 64-битные значения там нет.



Как это нет деления? Цифры то разные в обоих строках вывода (точность разная).

Решил усложнить эксперимент. Теперь вот такой код:

Код:
#include <stdio.h>

#define PI 3.1415926535897932385

double dPI=PI;
float  fPI=PI;
char  *formstr="%s: PI=%.18g, PI*2=%.18g, PI*3=%.18g \n";

int wmain(int argc, wchar_t **argv)
{
    printf("sizeof(float) = %d, sizeof(double) = %d \n", sizeof(float), sizeof(double));
    printf("sizeof(fPI)   = %d, sizeof(dPI)    = %d \n", sizeof(fPI),   sizeof(dPI));
    printf("sizeof(fPI*2) = %d, sizeof(dPI*2)  = %d \n", sizeof(fPI*2), sizeof(dPI*2));
    printf("sizeof(fPI*3) = %d, sizeof(dPI*3)  = %d \n", sizeof(fPI*3), sizeof(dPI*3));
    printf(formstr, "type =  float", fPI, fPI*2, fPI*3);
    printf(formstr, "type = double", dPI, dPI*2, dPI*3);
    return 0;
}


Но результат...

 
Код:
sizeof(float) = 4, sizeof(double) = 8
sizeof(fPI)   = 4, sizeof(dPI)    = 8
sizeof(fPI*2) = 4, sizeof(dPI*2)  = 8
sizeof(fPI*3) = 4, sizeof(dPI*3)  = 8
type =  float: PI=3.1415927410125732, PI*2=6.2831854820251465, PI*3=9.4247782230377197
type = double: PI=3.1415926535897931, PI*2=6.2831853071795862, PI*3=9.4247779607693793


Ясно же видно, что данные разных размеров, но ничего не поехало. Мистика!!!

Вот если написать printf("%llu\n",0,1); -- выведется 4294967296.
Тут всё логично -- два 32-битных параметра обработались как одни 64-битный.
278
30 апреля 2011 года
Alexander92
1.1K / / 04.08.2008
Цифры разные, потому что у float и double разная точность. У float 6-7 значащих цифр, а у double - 15-16.
А обработка вывода все равно идет как double и для "%f", и для "%g", если верить MSDN.
41K
01 мая 2011 года
kisssko
108 / / 28.10.2010
Добавил к коду ещё вот такое:

 
Код:
int   *pfPI;
    pfPI=(int*)&fPI;
    printf(formstr, "type =  float", *pfPI, fPI*2, fPI*3);


...и вот оно, поехало.

Цитата:

sizeof(float) = 4, sizeof(double) = 8
sizeof(fPI) = 4, sizeof(dPI) = 8
sizeof(fPI*2) = 4, sizeof(dPI*2) = 8
sizeof(fPI*3) = 4, sizeof(dPI*3) = 8
type = double: PI=3.1415926535897931, PI*2=6.2831853071795862, PI*3=9.4247779607693793
type = float: PI=3.1415927410125732, PI*2=6.2831854820251465, PI*3=9.4247782230377197
type = float: PI=2.6815622281736349e+154, PI*2=-3.7857678997154335e-270, PI*3=8.8238517323821973e+175



Выходит, что компилятор, скрытно, конвертирует параметры для printf типа float в тип double.
А подсунь ему корректный float, но объявленный как int, он этого не сделает.

278
01 мая 2011 года
Alexander92
1.1K / / 04.08.2008
Я обращу ваше внимание (очевидный факт, но все же), что printf() получает в качестве параметров не указатели на переменные, а копии переменных. Учитывая, что в данном случае используется список параметров переменной длины, т.е. внутри функции содержатся конструкции вида
 
Код:
T var = va_arg(start, T);
,
лично я делаю вывод, что неравносильные преобразования типов внутри printf() вполне возможны, и доверять в этом вопросе нужно только и исключительно документации. А документация говорит, что и "%f", и "%g" трактуется как double, только в разном представлении.
14
01 мая 2011 года
Phodopus
3.3K / / 19.06.2008
Что за компилятор используется?
41K
01 мая 2011 года
kisssko
108 / / 28.10.2010
Цитата: Phodopus
Что за компилятор используется?



MSVC++ 2008

14
02 мая 2011 года
Phodopus
3.3K / / 19.06.2008
Default argument promotions (6.5.2.2. - 6)
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог