2013-04-03 5 views
2

Я столкнулся с каким-то странным округлением с поплавками. Приведенный ниже код демонстрирует проблему. Каков наилучший способ решить эту проблему? Я искал решения, но не имел большой удачи.Округление с плавающей точкой в ​​C

#include<stdio.h> 

int main(void) 
{ 
    float t; 
    t = 5592411; 
    printf("%f\n", 1.5*t); 
    t *= 1.5; 
    printf("%f\n", t); 
    return 0; 
} 

Код выше должен распечатать такое же значение, но я получаю это на моей установке с помощью GCC 4.7.2:

8388616,500000

8388616.000000

If I используйте калькулятор, я получаю первое значение, поэтому я предполагаю, что второе округляется каким-то образом. У меня идентичный код Fortran, который не округляет значение (имеет значение 0.5).

+6

'1.5 * t' - выражение' double', а 't * = 1.5' - это выражение' float'. –

ответ

8

1.5 является double постоянным, а не float, а C имеет автоматические правила продвижения. Поэтому, когда вы выполняете 1.5*t, произойдет следующее: (i) t преобразуется в double; (ii), что double умножается на double1.5; и (iii) печатается double (как %f является форматированием для double).

И наоборот, способствует t *= 1.5t к двойному, выполняет двойное умножение, а затем обрежет результат, чтобы сохранить его обратно в [одинарной точности] float.

Для доказательства, попробуйте либо:

float t; 
t = 5592411; 
printf("%f\n", 1.5f*t); // multiply a float by a float, for no promotion 
t *= 1.5; 
printf("%f\n", t); 
return 0; 

Или:

double t; // store our intermediate results in a double 
t = 5592411; 
printf("%f\n", 1.5f*t); 
t *= 1.5; 
printf("%f\n", t); 
return 0; 
+0

Это, по-видимому, очень малое количество точности для поплавка. Есть ли другое решение, которое позволяет мне продолжать использовать поплавки вместо удвоений (как это требование)? – CalumMcCall

+2

Общее правило для одноточечных поплавков - от шести до девяти значащих десятичных цифр, поэтому семь цифр «8388616» находятся в пределах ожиданий. Будучи более конкретным, я считаю 20 двоичных цифр во фракции (после предполагаемого ведущего 1) и IEEE 754 допускает 23; вам понадобится 24 для 8388616.5. Поэтому стандартные поплавки IEEE не будут достаточно точными. – Tommy

+0

Спасибо за ваш ответ, это действительно полезно. Ясно, что мне нужно читать цифры с плавающей запятой. – CalumMcCall

2

Первый расчет выполняется с двойной точностью, второй рассчитывается одинаково, но усечен до одной точности при присвоении float.

Если вы используете double для своей переменной, вы получите тот же результат. Рекомендуется использовать этот тип по float всякий раз, когда точность может быть проблемой.

1

В первом случае результатом является двойной, который может точно представлять желаемое значение.

Во втором случае результатом является поплавок, который не может точно представить желаемое значение.

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

#include<stdio.h> 

int main(void) 
{ 
    double t; 
    t = 5592411; 
    printf("%f\n", 1.5*t); 
    t *= 1.5; 
    printf("%f\n", t); 
    return 0; 
} 
0

Письмо 1.5 в коде C интерпретируется как двойной, который имеет более высокую точность, чем тип float.

Первый случай,

printf("%f\n", 1.5*t); 

приводит к т быть неявно преобразован в двойной (с большей точностью), а затем умножается.Функция printf, которая в любом случае вводит входной сигнал, соответствующий %f, выводит результат, который также является double.

Второй случай имеет 1.5, преобразованный в тип float, который имеет меньшую точность и не может хранить как мелкие детали.

Если вы хотите, чтобы избежать этого эффекта, используйте 1.5f вместо этого на 1.5 использовать floats или изменить тип t к double.

-1

Независимо от того, будет ли это работать вообще, зависит от машинного представления поплавков и парных. Передача поплавка на типичной 32-битной архитектуре подталкивает 4 байта в стек аргументов. Передача double приведет к 8 байтам. Передача double, но с использованием% f просит обработать его как float, который будет смотреть на первые 4 байта, введенные в нашем типичном случае. В зависимости от представления машины это может быть близко к предполагаемому результату или может быть выходным в левом поле.

Смежные вопросы