ошибочное визуальное преобразование c float / double?
В Visual C++ я написал следующий пример в программе C++:
float f1 = 42.48f;
double d1 = 42.48;
double d2 = f1;
Я скомпилировал программу с помощью Visual Studio 2005. В отладчике я вижу следующие значения:
f1 42.480000 float
d1 42.479999999999997 double
d2 42.479999542236328 double
D1, насколько я знаю, в порядке, но d2 ошибается.
Данная проблема возникает также с /ФП=точно как с /ФП=строгими, с /ФП=быстро.
В чем тут проблема? Есть подсказка, как избежать этой проблемы? Это приводит к серьезным численным проблемам.
3 ответа:
Это не проблема с VC++ или чем - то подобным-это фундаментальная проблема с тем, как числа с плавающей запятой хранятся на компьютере. Дополнительную информацию смотрите в разделеIEEE-754 .
Проблема заключается в том, что преобразование из float в double выполняется таким образом, что обратное преобразование из double в float приводит к точно такому же значению float, с которого вы начали. Я не знаю никакого способа обойти потерю точности, за исключением использования только двойников, когда вам нужна более высокая точность. Это может быть будь то попытка
round
преобразовать float в два знака после запятой установит его в правильное значение, но я не уверен в этом.
Значение в
f1
и значение вd2
представляют собой одно и то же число. Это число не является точно 42.480000, как и не является точно 42.479999542236328, хотя оно имеет десятичное представление, которое завершается. При отображении поплавков ваше представление отладки округляется с точностью до поплавка, а при отображении двойников-с точностью до двойника. Таким образом, вы видите примерно в два раза больше значимых фигур таинственного значения, когда вы преобразуете и отображаете, как двойник.Способ избежать этого зависит от того, какие серьезные численные проблемы вы имеете в виду. Если проблема в том, что d1 и d2 не делают сравните равные, и вы считаете, что они должны, тогда ответ заключается в том, чтобы включить небольшой допуск в ваши сравнения, например, заменить
d1
содержит лучшее приближение к 4.48, чем значение mystery, так какd1
содержит ближайший двойник к 4.48, тогда какf1
иd2
содержат только ближайшее значение float к 4.48. Что вы ожидали, чтоd2
будет содержать? f1 не может "вспомнить", что это" действительно должно быть "4.48, поэтому, когда он преобразуется в double, он становится"более точным".d1 == d2
на:Это всего лишь пример, хотя я не проверил, имеет ли он отношение к этому делу. Вы должны выбрать допуск, который работает для вас, и вы также можете беспокоиться о множестве крайних случаев-d2 может быть нулем, любое значение может быть Бесконечностью или NaN, возможно, другие.fabs(d1 - d2) <= (d2 * FLT_EPSILON)
Если проблема заключается в том, что d2 не является достаточно точное значение для вашего алгоритма, чтобы получить точные результаты, вы должны избегать значений
float
и/или использовать более численно стабильный алгоритм.
В том, что здесь происходит, нет ничего плохого.
Из-за способа представления чисел с плавающей запятой в памяти, 42.479999999999997 является наиболее близким представлением 42.48, которое может иметь двойник.
Прочитайте эту статью: http://docs.sun.com/source/806-3568/ncg_goldberg.html
Это объясняет, что там происходит. К сожалению, вы ничего не можете сделать с его хранением.