Для тех, кто не может воспроизвести ошибку: не раскомментируйте закомментированные stmts отладки, они влияют на результат.
Это означает, что проблема связана с операторами отладки.,И похоже, что есть ошибка округления, вызванная загрузкой значений в регистры во время операторов вывода, поэтому другие обнаружили, что вы можете исправить это с помощью -ffloat-store
. Следующий вопрос:
Мне было интересно, должен ли я всегда включать опцию -ffloat-store
?
Чтобы быть легкомысленным, должна быть причина, по которой некоторые программисты не включают -ffloat-store
, иначе эта опция нене существует (аналогично, должна быть причина, по которой некоторые программисты делают включающими -ffloat-store
).Я не рекомендовал бы всегда включать это или всегда выключать это.Включение этого параметра предотвращает некоторые оптимизации, но отключение учитывает тот тип поведения, который вы получаете.
Но, как правило, некоторое несоответствие между двоичными числами с плавающей запятой (например,компьютер использует) и десятичные числа с плавающей запятой (с которыми люди знакомы), и это несоответствие может привести к поведению, аналогичному тому, которое вы получаете (для ясности, поведение, которое вы получаете, не , вызванное этим несоответствием, но подобное поведение может быть).Дело в том, что, поскольку вы уже имеете некоторую неопределенность при работе с плавающей точкой, я не могу сказать, что -ffloat-store
делает ее лучше или хуже.
Вместо этого вы можете захотеть взглянуть на другие решения проблемы, которую вы пытаетесь решить (к сожалению, Кениг не указывает на реальную статью, и я не могу найти очевидное "каноническое" место для нее, поэтому мне придется отправить Google ).
Если вы не округлите для целей вывода, я бы, вероятно, посмотрел на std::modf()
(в cmath
) и std::numeric_limits<double>::epsilon()
(вlimits
).Подумав об исходной функции round()
, я считаю, что было бы чище заменить вызов на std::floor(d + .5)
вызовом этой функции:
// this still has the same problems as the original rounding function
int round_up(double d)
{
// return value will be coerced to int, and truncated as expected
// you can then assign the int to a double, if desired
return d + 0.5;
}
Думаю, это предполагает следующее улучшение:
// this won't work for negative d ...
// this may still round some numbers up when they should be rounded down
int round_up(double d)
{
double floor;
d = std::modf(d, &floor);
return floor + (d + .5 + std::numeric_limits<double>::epsilon());
}
Простое примечание: std::numeric_limits<T>::epsilon()
определяется как «наименьшее число, добавленное к 1, которое создает число, не равное 1».Обычно вам нужно использовать относительный эпсилон (то есть масштабировать эпсилон как-то, чтобы учесть тот факт, что вы работаете с числами, отличными от «1»).Сумма d
, .5
и std::numeric_limits<double>::epsilon()
должна быть близка к 1, поэтому группировка этого сложения означает, что std::numeric_limits<double>::epsilon()
будет примерно подходящего размера для того, что мы делаем.Во всяком случае, std::numeric_limits<double>::epsilon()
будет слишком большим (когда сумма всех трех меньше единицы) и может заставить нас округлять некоторые числа, а мы не должны.
В настоящее время вам следуетрассмотрим std::nearbyint()
.