Работа с проблемами точности в номерах с плавающей точкой

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

Вопрос

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

Ради ясности проблема обобщена как:

// str is "4.600";   atof( str ) is 4.5999999999999996  
double mw = atof( str )  

// The variables used in the columns calculation below are:   
//  
//                    mw = 4.5999999999999996  
//                    p = 0.2  
//                    g = 0.2  
//                    h = 1 (integer)  

int columns = (int) ( ( mw - ( h * 11 * p ) ) / ( ( h * 11 * p ) + g ) ) + 1;

Перед катой до целочисленного типа результат расчета столбцов составляет 1.9999999999999996; так близко еще далеко от желаемого результата 2.0.

Любые предложения приветствуются.

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

Решение

Очень простой и эффективный способ окружить номер плавающего запястья на целое число:

int rounded = (int)(f + 0.5);

Примечание: это работает только если f всегда положительный. (Спасибо J случайный хакер)

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

Когда вы используете арифметическую арифметику, строгое равенство почти бессмысленно. Обычно вы хотите сравнить с диапазоном приемлемых значений.

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

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

Там нет проблемы с сопротивлением.

Результат, который вы получили (1.999999999999996), отличался от математического результата (2) по краю 1E-16. Это довольно точно, учитывая ваш вход «4.600».

У вас есть проблема с округлением, конечно. Округление по умолчанию в C ++ - усечение; Вы хотите что -то похожее на решение Кипа. Детали зависят от вашего точного домена, вы ожидаете round(-x)== - round(x) ?

Если вы не читали это, название Эта бумага действительно правильно. Пожалуйста, подумайте о том, чтобы прочитать его, узнать больше о основах арифметики с плавающей точкой на современных компьютерах, некоторых подводных камнях и объяснениях того, почему они ведут себя так, как они.

Если точность действительно важна, то вам следует рассмотреть возможность использования двойных чисел с плавающей запятой, а не просто плавающей запятой. Хотя из вашего вопроса кажется, что вы уже есть. Тем не менее, у вас все еще есть проблема с проверкой конкретных значений. Вам нужен код в соответствии с (при условии, что вы проверяете свое значение с нулем):

if (abs(value) < epsilon)
{
   // Do Stuff
}

Где «Эпсилон» - это какое -то маленькое, но не нулевое значение.

На компьютерах номера с плавающими точками никогда не бывают точными. Они всегда просто близкое приближение. (1E-16 близко.)

Иногда есть скрытые кусочки, которые вы не видите. Иногда основные правила алгебры больше не применяются: a*b! = B*a. Иногда сравнение регистра с памятью показывает эти тонкие различия. Или использование математической копроцессора против библиотеки плавания во время выполнения. (Я делаю это, что это много долго.)

C99 определяет: (посмотрите в математика)

double round(double x);
float roundf(float x);
long double roundl(long double x);

.

Или вы можете свернуть свой собственный:

template<class TYPE> inline int ROUND(const TYPE & x)
{ return int( (x > 0) ? (x + 0.5) : (x - 0.5) ); }

Для эквивалентности с плавающей запятой, попробуйте:

template<class TYPE> inline TYPE ABS(const TYPE & t)
{ return t>=0 ? t : - t; }

template<class TYPE> inline bool FLOAT_EQUIVALENT(
    const TYPE & x, const TYPE & y, const TYPE & epsilon )
{ return ABS(x-y) < epsilon; }

Используйте десятичные десятки: decnumber ++

Вы можете прочитать это бумага Чтобы найти то, что вы ищете.

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

x = 0.2;  
y = 0.3;  
equal = (Math.abs(x - y) < 0.000001)  
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top