Ответ 1
std::round
введен в С++ 11. До этого было доступно только std::floor
, поэтому программисты использовали его.
Различия находятся в возвращаемом значении, дающем входные данные вокруг tie-break, которые я считаю, например этот код:
int main()
{
std::cout.precision(100);
double input = std::nextafter(0.05, 0.0) / 0.1;
double x1 = floor(0.5 + input);
double x2 = round(input);
std::cout << x1 << std::endl;
std::cout << x2 << std::endl;
}
который выводит:
1
0
Но в конце концов, это просто разные результаты, один выбирает свой предпочтительный. Я вижу множество "старых" программ на C/С++, используя floor(0.5 + input)
вместо round(input)
.
Есть ли историческая причина? Самый дешевый на процессоре?
std::round
введен в С++ 11. До этого было доступно только std::floor
, поэтому программисты использовали его.
Нет никакой исторической причины. Такое отвращение существует с года. Народ делает это, когда они чувствуют себя очень, очень непослушными. Это серьезное злоупотребление арифметикой с плавающей запятой, и многие опытные профессиональные программисты падают за нее. Даже Java-устройства делали до версии 1.7. Смешные ребята.
Моя гипотеза заключается в том, что достойная функциональная функция округления немецкого округления официально не была доступна до С++ 11 (несмотря на то, что C получила их на C99), но это действительно не оправдание для принятия так называемой альтернативной.
Здесь вещь: floor(0.5 + input)
не всегда возвращает тот же результат, что и соответствующий вызов std::round
!
Причина довольно тонкая: точка отсечения для немецкого округления, a.5
для целого числа a
, является случайным свойством вселенной диадическим рациональным. Поскольку это может быть представлено точно в плавающей точке IEEE754 до 52-й степени 2, а после этого округление в любом случае равно нулю, std::round
всегда работает правильно. Для других схем с плавающей точкой обратитесь к документации.
Но добавление 0.5
в double
может привести к неточности, вызвав небольшое недочет или превышение для некоторых значений. Если вы подумаете об этом, добавив два значения double
вместе - это начало невольных преобразований денонарей - и применение функции, которая является очень сильной функцией ввода (например, функция округления), неизбежно заканчивается слезами.
Не делайте этого.
Я думаю, что здесь вы ошибаетесь:
Но в конце они просто разные результаты, один выбирает предпочтительнее. Я вижу множество "старых" программ на C/С++, используя пол (0,5 + ввод) вместо раунда (ввод).
Это не так. Вы должны выбрать схему правильного округления для домена. В финансовом приложении вы будете использовать правила банкира (не используя поплавок, кстати). Однако при выборке, округление с использованием static_cast<int>(floor(f + .5))
дает меньше шума выборки, это увеличивает динамический диапазон. Когда выравнивание пикселей, то есть преобразование положения в координаты экрана, использование любого другого метода округления даст отверстия, зазоры и другие артефакты.
Простая причина может заключаться в том, что существуют разные методы округления чисел, поэтому, если вы не знаете метод, который вы использовали, вы можете получить разные результаты.
С floor() вы можете соответствовать результатам. Если поплавок равен .5 или больше, добавление его будет увеличиваться до следующего значения int. Но .49999 просто отбросит десятичное число.
Многие программисты адаптируют идиомы, которые они изучали при программировании на других языках. Не все языки имеют функцию round()
, и в этих языках нормально использовать floor(x + 0.5)
в качестве замены. Когда эти программисты начинают использовать С++, они не всегда понимают, что есть встроенный round()
, они продолжают использовать стиль, к которому они привыкли.
Другими словами, только потому, что вы видите много кода, который что-то делает, это не значит, что для этого есть веская причина. Вы можете найти примеры этого на каждом языке программирования. Помните Закон о осетроводе:
Девяносто процентов всего дерьмо