Ошибка округления C# банкира

double a = 18.565
return Math.Round(a,2)

... возвращается 18.57.
Для каждого другого числа, которое я пробовал, банковское округление работало, как и ожидалось, например, Math.Round(2.565,2) вернул 2,56.

Любая подсказка, почему и когда это происходит? Это ошибка или я что-то упускаю из-за банковского округления?

Спасибо..

4 ответа

Решение

Как сказал Мэтью, 18.565 не могут быть точно представлены. Фактическое используемое значение составляет 18,565000000000001278976924368180334568023681640625 (найдено с использованием DoubleConverter), что явно находится за пределами середины пути. Теперь у меня есть ощущение, что иногда Math.Round будет рассматривать значение, которое на самом деле находится за пределами промежуточной точки, но которое настолько близко к половине промежуточной точки, насколько это может быть точно представлено, как находящееся именно в этой точке. Однако я не видел никакой документации, описывающей ситуации, в которых это применяется, и, очевидно, этого не происходит в этом случае. Я бы не хотел на это полагаться.

Конечно, даже округленное значение не совсем 18,57. Это на самом деле 18.57000000000000028421709430404007434844970703125.

По сути, если вы действительно, действительно хотите точно представлять десятичные значения, вы должны использовать decimal, Это не только с точки зрения Math.Round - это касается каждого аспекта обработки значений с плавающей запятой.

Это дает правильное значение для Math.Round, конечно:

decimal m = 18.565m;
Console.WriteLine(Math.Round(m, 2)); // Prints 18.56

18.565 не может быть точно представлен как двойной. Таким образом, двоичное представление немного выше, поэтому оно округляется. Если вы используете десятичную:

decimal a = 18.565m;
return Math.Round(a,2)

это может быть точно представлено, и у вас не будет этой проблемы.

Я предполагаю, что представление FP означает, что на самом деле это не трейлинг 5; опасности FP!

Это работает хорошо, хотя:

        decimal a = 18.565M; // <===== decimal
        var s = Math.Round(a, 2);

Double - это значение с плавающей запятой, поэтому, возможно, если вы запишите его как 18.565, оно на самом деле будет в памяти что-то вроде 18.56500000000000000000000000000000001, и, следовательно, это больше, чем средняя точка.

Другие вопросы по тегам