2015-11-25 4 views
-4

Я знаю, что эти вопросы с плавающей запятой, вероятно, являются наиболее часто задаваемыми вопросами при переполнении стека, но я не могу найти что-то похожее на мое. В окнах (Visual Studio), собранных в 32 бита, если я:Почему не добавляются эти поплавки?

double lnA = 1448481410.0; 
double lnB = 0.75599998235702515; 
double lnC = lnA + lnB; 

я ЛНК = 1448481408.0000000. Я могу понять небольшую разницу из-за представления с плавающей запятой, но я не понимаю, почему lnA - lnC == 2?

ОБНОВЛЕНО: Итак, вот фактический вывод с помощью Visual Studio 2010: это приложение MFC, поэтому я использую TRACE.

double lnA = 1448481410.0; 
double lnB = 0.75599998235702515; 
double lnC = lnA + lnB; 

TRACE("A = %f B = %f C = %f A - C = %f\n",lnA, lnB, lnC, lnA - lnC); 

A = 1448481410,000000 B = 0,756000 C = 1448481408,000000 A - C = 2,000000

UPDATE2: В попытке сделать минимальный полный пример, например, как показано ниже, я не вижу с той же проблемой. Только когда это часть моего большого приложения. Есть идеи?

#include <iostream> 

int main() { 
    double lnA = 1448481410.0; 
    double lnB = 0.75599998235702515; 
    double lnC = lnA + lnB; 

    std::cout << "A: " << lnA << "B: " << lnB << "C: " << "Diff: " << lnA - lnC << std::endl; 
    return 0; 
} 
+0

введите полный код –

+0

Опубликуйте [MCVE], который воспроизводит поведение, о котором вы говорите. –

+0

[lnA - lnC is -0.756, not 2] (http://cpp.sh/2l3z). – emlai

ответ

5

После некоторого исследования, я пришел к выводу, что ваш код является НАВЕРНОЕ запутанным float и double (или просмотр выходного кода, который отличается от того, что вы на самом деле писали).

Это работает для меня:

#include <iostream> 

int main() 
{ 
    double lnA = 1448481410.0; 
    double lnB = 0.75599998235702515; 
    double lnC = lnA + lnB; 

    std::cout << std::fixed << "A:" << lnA << " B:" << lnB << " C:" << lnC << std::endl; 
} 

Производит:

$ ./a.out 
A:1448481410.000000 B:0.756000 C:1448481410.756000 

Теперь, как сказал Soulsabr в комментарии, если мы будем использовать float вместо double, результат отличается:

Только эти линии изменены:

float lnA = 1448481410.0; 
float lnB = 0.75599998235702515; 
float lnC = lnA + lnB; 


$ ./a.out 
A:1448481408.000000 B:0.756000 C:1448481408.000000 

Это связано с тем, что количество бит в float, в типичных системах, составляет 32 бита, который делится на 8 бит экспоненты, один бит знака и 23 бита [плюс один скрытый] для мантиссы. поэтому значение равно S * M * 2^E, где S - знак, M - это мантисса, а E - показатель степени. Размер M составляет 23 бита, поэтому его можно использовать для описания значений до 8 миллионов. Мы можем сдвинуть значение с помощью E, но независимо от того, какое значение мы выберем, наименьшее значение, которое может меняться внутри числа, - это следующее большее целое число x/8 миллионов от фактического значения. Таким образом, 14 миллионов становятся +/- 2 в наименьшем значении, которое «имеет значение». Добавление 1 или менее не будет иметь никакого эффекта.

Код double «работает», потому что 64-битный double имеет 53-битную мантиссу, которая позволяет использовать значение +/- 1/2^53 от фактического значения, которое является БОЛЬШИМ большим значением и позволяет более точно вычислять. Но возьмите достаточно большое и достаточно маленькое значение, и мы получим ту же проблему, если они достаточно далеко друг от друга. Это всего лишь вопрос о том, как работают значения с плавающей запятой. У вас так много бит. Существуют «большие математические» библиотеки, которые позволяют большее количество бит («бесконечно», при условии доступности памяти), но, конечно, с большими значениями скорость вычисления уменьшается, а для большинства вещей 1/2^53 из значение «достаточно хорошо».

Edit (на основе комментариев по OP):

Аналогичный эффект на «использовании поплавок» может произойти, если при использовании инструкций x87, ФП устанавливаются на «круглый до 32 бит», что означает, что даже если вычисление производится с 64-битными значениями с плавающей запятой, промежуточные результаты округляются до 32-битной точности. Для комментариев выше, это, кажется, определенный программный продукт, который делает некоторые «Магические» для достижения этого, и есть простой обходной путь.

+0

Это не помогает ему, если вы не объясните, почему двойники работают на месте поплавка. – soulsabr

+0

Я не вижу никакой информации, где используется 'float'? [кроме родового термина «с плавающей запятой»] –

+0

И я сказал, что это не ответ, так что не делайте его вниз по этой причине, пожалуйста! –

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