Позвольте мне сначала попытаться обосновать причину добавления еще одного ответа на этот вопрос. В идеальном мире округление не имеет большого значения. Однако в реальных системах вам, возможно, придется столкнуться с несколькими проблемами, которые могут привести к округлению, которое может не соответствовать вашим ожиданиям. Например, вы можете выполнять финансовые расчеты, где окончательные результаты округляются и отображаются для пользователей в виде 2 десятичных знаков; эти же значения хранятся с фиксированной точностью в базе данных, которая может содержать более 2 десятичных разрядов (по разным причинам; оптимального количества мест для хранения не существует ... зависит от конкретных ситуаций, которые должна поддерживать каждая система, например, крошечные предметы, цены на которые являются доли копейки на единицу); и вычисления с плавающей запятой, выполняемые для значений, где результаты плюс / минус эпсилон. Я сталкиваюсь с этими проблемами и разрабатываю свою собственную стратегию на протяжении многих лет. Я не буду утверждать, что сталкивался с каждым сценарием или имею лучший ответ, но ниже приведен пример моего подхода, который преодолевает эти проблемы:
Предположим, что 6 десятичных знаков считается достаточной точностью для вычислений с плавающей запятой / двойными числами (произвольное решение для конкретного приложения) с использованием следующей функции / метода округления:
double Round(double x, int p)
{
if (x != 0.0) {
return ((floor((fabs(x)*pow(double(10.0),p))+0.5))/pow(double(10.0),p))*(x/fabs(x));
} else {
return 0.0;
}
}
Округление до 2 десятичных знаков для представления результата может быть выполнено как:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,8),6),2));
Для val = 6.825
результат равен 6.83
, как и ожидалось.
Для val = 6.824999
результат равен 6.82
. Здесь предполагается, что в результате расчета получилось ровно 6.824999
, а седьмое десятичное место равно нулю.
Для val = 6.8249999
результат равен 6.83
. В этом случае седьмое десятичное число, равное 9
, заставляет функцию Round(val,6)
давать ожидаемый результат. В этом случае может быть любое количество конечных 9
с.
Для val = 6.824999499999
результат равен 6.83
. В качестве первого шага округление до восьмого знака после запятой, т. Е. Round(val,8)
, учитывает один неприятный случай, когда вычисленный результат с плавающей запятой вычисляется до 6.8249995
, но внутренне представляется как 6.824999499999...
.
Наконец, пример из вопроса ... val = 37.777779
приводит к 37.78
.
Этот подход можно обобщить следующим образом:
double val;
// ...perform calculations on val
String(Round(Round(Round(val,N+2),N),2));
где N - точность, которая должна поддерживаться для всех промежуточных вычислений с плавающей запятой / удваивается Это работает и на отрицательных значениях. Я не знаю, является ли этот подход математически правильным для всех возможностей.