Ошибка округления C # банкира - PullRequest
8 голосов
/ 23 сентября 2010
double a = 18.565
return Math.Round(a,2)

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

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

Спасибо ..

Ответы [ 4 ]

16 голосов
/ 23 сентября 2010

Как сказал Матфей, ​​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
5 голосов
/ 23 сентября 2010

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

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

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

1 голос
/ 23 сентября 2010

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

Это прекрасно работает, хотя:

        decimal a = 18.565M; // <===== decimal
        var s = Math.Round(a, 2);
0 голосов
/ 23 сентября 2010

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

...