Очевидное несоответствие с использованием std::floor
Я заметил нечто очень странное при использовании std::floor. Я собираю C++ на Ubuntu. Я пытаюсь найти двойное с точностью до сотых, и я проверяю случаи, когда входное значение уже округлено до ближайшей сотой, и я получаю противоречивые результаты.
Мой код:
double hundredthFloor(double value){
double hundredth = 0.01;
return hundredth * std::floor( value / hundredth);
}
В большинстве случаев это работает,
value = 99.87 gives output 99.87
value = 99.879 gives output 99.87
value = 0.39 gives output 0.39
Тем не мение:
value = 0.29 gives value 0.28
value = 0.47 gives value 0.46
Я перепробовал много входных значений, и пока только 0,29 и 0,47, похоже, выводят значение на один десятичный знак ниже, чем я ожидал.
Кто-нибудь видел что-нибудь подобное?
3 ответа
Причина того, что вы видите, заключается в том, что некоторые значения в вашем примере, такие как 0.01
а также 0.29
, не может быть представлен точно с использованием float
или же double
,
Вы можете получить лучшее представление о том, что происходит, если вы напечатаете результат со многими значащими цифрами:
cout << fixed << setprecision(15) << (value / hundredth) << endl;
когда value
является 0.29
это печатает
28.999999999999996
Это число очень близко к 29, но floor
все равно обрезает до 28.
Одним из способов решения этой проблемы является добавление эпсилона к результату деления:
return hundredth * std::floor( value / hundredth + numeric_limits<double>::epsilon());
Попробуйте следующее:
cout.precision(17);
cout << fixed << 0.29 << endl;
cout << hundredthFloor(0.29) << endl;
Вы получите что-то вроде:
0.28999999999999998
0.28000000000000003
Это потому что double
не всегда может точно представлять десятичное число, подробности смотрите в этом ответе. Это означает, что иногда число будет немного больше или меньше, чем вы ожидаете; Пол не будет принимать это во внимание, и, следовательно, 0.29
будет внутренне представлен как 0.28999999...
и округляется до 0.28
,
Простое решение заключается в добавлении очень маленького числа, такого как numeric_limits<double>::epsilon()
перед звонком std::floor
:
double hundredthFloor(double value){
double hundredth = 0.01;
return hundredth * std::floor( value / hundredth + std::numeric_limits<double>::epsilon() );
}
Какова ваша стоимость EPS? Я всегда использую значения EPS, чтобы сначала убедиться, что мои вычисления неверны или неточности компьютера: