ошибочное преобразование с плавающей запятой/двойное преобразование Visual C?

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

Вопрос

В 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 неправильный.

Проблема возникает и с /fp=precision, и с /fp=strict, и с /fp=fast.

В чем здесь проблема?Любая подсказка, как избежать этой проблемы?Это приводит к серьезным численным проблемам.

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

Решение

Это не проблема с VC ++ или что-то в этом роде - это фундаментальная проблема с тем, как на компьютере хранятся цифровые номера. Для получения дополнительной информации см. IEEE-754..

Вопрос состоит в том, что преобразование из Float в двойной сделан так, что преобразование обратно из Double, чтобы поплавок приводит к тому же значение поплавка, с которой вы начали. Я не знаю ни о потере точности, за исключением использования только удваивает, когда вам нужна более длительная точность. Может быть, что пытаться round Преобразованный поплавок до двух десятичных знаков настроит его на правильное значение, но я не уверен в этом.

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

Ценность в f1 и значение в d2 Оба представляют то же самое число. Это число не ровно 42,480000, ни один не имеет ровно 42,479999542236328, хотя у него есть десятичное представление, которое заканчивается. При отображении плавающихся, ваш вид отладки разумно округляется при точности поплавка, а при отображении удваивает его округление при точности двойного. Таким образом, вы видите в два раза больше значимых фигур загадочного значения при преобразовании и отображении в виде двойного.

d1 содержит лучшее приближение до 4,48, чем значение тайны, поскольку d1 Содержит ближайший двойной до 4.48, тогда как f1 а также d2 содержат только ближайшее значение поплавка до 4,48. Что ты ожидал d2 содержать? F1 не может «запомнить», что «действительно должен быть» 4.48, так что когда он преобразует, чтобы удвоить, он становится «более точным».

Способ избежать этого зависит, какие серьезные численные проблемы вы имеете в виду. Если проблема в том, что D1 и D2 не сравнивают равные, и вы думаете, что они должны, то ответ должен включать небольшую допуску в ваших сравнениях, например, заменить d1 == d2 с участием:

fabs(d1 - d2) <= (d2 * FLT_EPSILON)

Это просто пример, хотя я не проверил, касается ли он с этим случаем. Вы должны выбрать допуску, который работает для вас, и вам также придется беспокоиться о многих случаях края - D2 может быть нулевым, либо значение может быть бесконечность или NAN, возможно, другие.

Если проблема в том, что D2 не является достаточно точным значением для вашего алгоритма для получения точных результатов, то вам нужно избегать float значения и / или используют более численно стабильный алгоритм.

В том, что здесь происходит, нет ничего плохого.

Из-за того, как числа с плавающей запятой представлены в памяти, 42,479999999999997 является ближайшим представлением 42,48, которое может иметь тип double.

Прочтите эту статью:http://docs.sun.com/source/806-3568/ncg_goldberg.html

Это объясняет, что там происходит.К сожалению, с его хранением ничего не поделаешь.

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