Почему тип long double переменной производит абсурдный вывод, в то время как тип float и double работает нормально?

Все, что я хочу, чтобы следующая программа делала, это печатала произведение всех положительных чисел меньше 30 (как в экспоненциальной, так и в неэкспоненциальной формах). Он отлично работает, когда переменная product объявлена ​​как float или double, но дает совершенно абсурдный (отрицательный) результат, когда тип long double. Поэтому, пожалуйста, ответьте на эти два вопроса, вытекающие из него:

  1. Почему long double дает совершенно абсурдные (даже отрицательные) результаты, в то время как float и double вводят переменную product, выдавая правильные результаты?

    У меня такое представление, что long double есть не что иное, как версия с большой емкостью double, которая сама является версией с большой емкостью типа float!!

  2. Теперь, что касается типов, которые дают правильный результат для product, то есть float и double, почему они дают одинаковый результат в экспоненциальной форме (%e), но заметно различаются для неэкспоненциальной формы (%f)?


#include<stdio.h>

int main(void)
{
    int j;
    float product=1;   //Works fine
    //double product=1;  //Works fine
    //long double product=1; //Produces absurd output.
    
    
    for(j=2;j<=30;j=j+2)
    product=product*j;
    
    printf("The product of even numbers <30 is %e \n",product);
    printf("The product in non-exponential form is %f",product);
}

Вывод для product как число с плавающей запятой

The product of even numbers <30 is 4.284987e+16 
The product in non-exponential form is 42849875099910144.000000

Вывод для product как двойной

The product of even numbers <30 is 4.284987e+16 
The product in non-exponential form is 42849873690624000.000000

Вывод для product как long double

The product of even numbers <30 is -6.078565e-192 
The product in non-exponential form is -0.000000

person Rüppell's Vulture    schedule 01.05.2013    source источник


Ответы (2)


Потому что вы используете неправильный спецификатор формата для его вывода. float и double передаются как double в функцию printf() (из-за того, что к ней применяются правила продвижения по умолчанию, поскольку она вариативна), но long double передается как long double, и вы должны использовать для него спецификатор преобразования %Lf, иначе ваша программа вызовет неопределенное поведение. .

person Community    schedule 01.05.2013
comment
Венгр спешит на помощь!! H2CO3, пожалуйста, добавьте в свой ответ больше деталей. - person Rüppell's Vulture; 01.05.2013
comment
@SheerFish Пожалуйста, но какие подробности вам нужны? Это почти весь ответ. - person ; 01.05.2013
comment
Ответ на первую часть вопроса. Теперь вторая часть. Почему неэкспоненциальная форма вывода заметно отличается для float и double? - person Rüppell's Vulture; 01.05.2013
comment
Вы заметили мой комментарий выше? - person Rüppell's Vulture; 01.05.2013
comment
@SheerFish А, так вы тем временем задали еще один вопрос. Ну, они отличаются, потому что арифметика с плавающей запятой не является точной. - person ; 01.05.2013
comment
@R.. Действительно!! %llf показывает ту же ошибку, в то время как %Lf работает. Я проверил с cplusplus.com/reference/cstdio/ scanf, что %lld,%llu,%llx и т. д. есть, но ничего похожего на %llf. - person Rüppell's Vulture; 01.05.2013

Для второго вопроса: float имеет только 24 бита значимости и может содержать только около ~ 6-7 цифр точности (реальная точность не является целым числом, потому что типы с плавающей запятой в компьютере обычно используют двоичный код, тогда как мы, люди, используем десятичный), остальное просто "мусор".

                  float error
precision: 1234567↓90ABCDEF digits
float:     42849875099910144.000000
double:    42849873690624000.000000

Как видите, разница в 8й цифре. double имеет 53 бита значимости и может быть точным примерно до ~ 15 десятичных цифр, что, очевидно, означает, что оно будет более точным. Но если округлить до 6й цифры (что является значением по умолчанию для %e), то оба результата дают 4,284987e+16, что приводит к неправильному представлению о том, что результат в float и double одинаков, даже если они нет. Если вы напечатаете больше десятичных цифр, например, с помощью %.20e, вы увидите ту же разницу, что и %f.

Если вы исправите спецификатор формата long double и сделаете больше итераций, то double столкнется с той же проблемой, если long double будет более точным. Например, если вы увеличите лимит в своем коде с 30 до 50, вы получите это

float:       5.204699e+32
             520469877396214593413323729928192.000000
double:      5.204698e+32
             520469842636666625085618390040576.000000
long double: 5.204698e+32
             520469842636666622693081088000000.000000

https://ideone.com/L6P9d6

person phuclv    schedule 27.07.2013