потеря точности с плавающей запятой в С++:3015/0,00025298219406977296

StackOverflow https://stackoverflow.com/questions/2533728

Вопрос

Проблема.

Компилятор Microsoft Visual C++ 2005, 32-разрядная версия Windows XP SP3, процессор AMD 64 x2.

Код:

double a = 3015.0; 
double b = 0.00025298219406977296;
//*((unsigned __int64*)(&a)) == 0x40a78e0000000000  
//*((unsigned __int64*)(&b)) == 0x3f30945640000000  
double f = a/b;//3015/0.00025298219406977296;

результат расчета (т."f") равно 11917835.000000000 (((беззнаковый __int64)(&f)) == 0x4166bb4160000000), хотя должно быть 11917834.814763514 (т.е. ((беззнаковый __int64)(&f)) == 0x4166bb415a128aef).
Т.е.дробная часть теряется.
К сожалению, мне нужна дробная часть, чтобы быть правильным.

Вопросы:
1) Почему это происходит?
2) Как я могу решить проблему?

Дополнительная информация:
0) Результат принят напрямую из окна «смотреть» (оно не распечаталось, и я не забыл настроить точность печати).Я также предоставил шестнадцатеричный дамп переменной с плавающей запятой, поэтому я абсолютно уверен в результате расчета.
1) Разборка f = a/b такова:

fld         qword ptr [a]  
fdiv        qword ptr [b]  
fstp        qword ptr [f]  

2) f = 3015/0,00025298219406977296;дает правильный результат (f == 11917834.814763514 , ((беззнаковый __int64)(&f)) == 0x4166bb415a128aef ), но похоже, что в этом случае результат просто вычисляется во время компиляции:

fld         qword ptr [__real@4166bb415a128aef (828EA0h)]  
fstp        qword ptr [f]  

Итак, как я могу решить эту проблему?

P.S.Я нашел временное решение (мне нужна только дробная часть деления, поэтому в данный момент я просто использую f = fmod(a/b)/b), но мне все равно хотелось бы знать, как правильно решить эту проблему - double Предполагается, что точность составляет 16 десятичных цифр, поэтому такой расчет не должен вызывать проблем.

Это было полезно?

Решение

Используете ли вы DirectX в своей программе где-нибудь, поскольку это приводит к переключению модуля с плавающей запятой в режим одинарной точности, если вы специально не скажете ему не делать этого при создании устройства, и это приведет именно к этому?

Другие советы

Интересно, что если вы объявите и a, и b как числа с плавающей запятой, вы получите ровно 11917835.000000000.Поэтому я предполагаю, что где-то происходит преобразование к одинарной точности, либо в том, как интерпретируются константы, либо позже в вычислениях.

Однако в любом случае это немного удивительно, учитывая, насколько прост ваш код.Вы не используете какие-либо экзотические директивы компилятора, требующие одинарной точности для всех чисел с плавающей запятой?

Редактировать:Вы действительно подтвердили, что скомпилированная программа выдает неправильный результат?В противном случае наиболее вероятным кандидатом на (ошибочное) преобразование одинарной точности будет отладчик.

Если вам нужна точная математика, не используйте числа с плавающей запятой.

Сделайте себе одолжение и приобретите библиотеку BigNum с поддержкой рациональных чисел.

Я предполагаю, что вы распечатываете число без указания точности.Попробуй это:

#include <iostream>
#include <iomanip>

int main() { 
    double a = 3015.0; 
    double b = 0.00025298219406977296;
    double f = a/b;

    std::cout << std::fixed << std::setprecision(15) << f << std::endl;
    return 0;
}

Это производит:

11917834.814763514000000

Что мне кажется правильным.Я использую VC++ 2008 вместо 2005, но предполагаю, что разница в вашем коде, а не в компиляторе.

Вы уверены, что проверяете значение f сразу после инструкции fstp?Если у вас включена оптимизация, возможно, в окне просмотра может отображаться значение, полученное в какой-то более поздний момент (это кажется немного правдоподобным, поскольку вы говорите, что позже смотрите на дробную часть f - не маскирует ли его какая-то инструкция? вылезет как-нибудь?)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top