Подтвердить что ты не робот

Является ли С# десятичным округлением несогласованным?

Я сражался с десятичной точностью в С#, исходящей из SQL Decimal (38,30), и я, наконец, сделал все возможное для округления странности. Я знаю, что я, вероятно, не замечаю здесь очевидного, но мне нужно немного понять.

Проблема, с которой я сталкиваюсь, заключается в том, что С# не создает то, что я считаю совместимым.

decimal a = 0.387518769125m;
decimal b = 0.3875187691250002636113061835m;

Console.WriteLine(Math.Round(a, 11));
Console.WriteLine(Math.Round(b, 11));
Console.WriteLine(Math.Round(a, 11) == Math.Round(b, 11));

Урожайность

0.38751876912
0.38751876913
False

Uhh, 0.38751876913? В самом деле? Что мне здесь не хватает?

От MSDN:

Если цифра в позиции десятичного разряда нечетна, она изменяется на четную цифру. В противном случае он остается неизменным.

Почему я вижу противоречивые результаты? Дополнительная точность не меняет "цифру в десятичном положении"...

4b9b3361

Ответ 1

Из MSDN:

Если в d есть нулевая цифра вправо десятичной позиции decimals , а ее значение 5, цифра в десятичной запятой округляется, если она нечетная, или остается неизменной, если она даже. Если d имеет меньше дробных цифр, чем decimals, d возвращается без изменений.

В вашем первом случае

decimal a = 0.387518769125m;
Console.WriteLine(Math.Round(a, 11));

там - одна цифра справа 11-го места, и это число 5. Поэтому , поскольку позиция 11 четная, она остается неизменной. Таким образом, вы получаете

0.38751876912

В вашем втором случае

decimal b = 0.3875187691250002636113061835m;
Console.WriteLine(Math.Round(b, 11));

там нет ни одной цифры справа 11-го места. Поэтому это прямое округление округления; вы округлите, если следующая цифра больше 4, иначе вы округлите вниз. Поскольку цифра справа от 11-го места больше 4 (это 5), мы округливаем, чтобы вы видели

0.38751876913

Почему я вижу непоследовательные результаты?

Ты не. Результаты полностью соответствуют документации.

Ответ 2

Из MSDN - Метод Math.Round(десятичный, Int32):

Если есть одна ненулевая цифра в d справа от десятичной позиции десятичного знака, а ее значение равно 5, цифра в десятичной запятой округляется, если она нечетная, или остается неизменной, если она четная. Если d имеет меньше дробных цифр, чем десятичные числа, d возвращается без изменений.

Поведение этого метода следует за стандартом IEEE 754, раздел 4. Этот вид округления иногда называют округлением до ближайшего или округления банкира. Он минимизирует ошибки округления, возникающие из-за постоянного округления среднего значения в одном направлении.

Обратите внимание на использование одиночной ненулевой цифры. Это соответствует вашим первым примерам, но не второй.

и

Чтобы управлять типом округления, используемым методом Round (Decimal, Int32), вызовите перегрузку Decimal.Round(десятичное, Int32, MidpointRounding).

Ответ 3

Часть "одиночная ненулевая цифра в d справа от десятичной позиции десятичного знака и ее значение равна 5" объясняет результат. Только тогда, когда часть раунда равна 0,5, правило округления входит в игру.

Ответ 4

Сдвиньте оба числа на 11 цифр влево:

+38751876912,5
+38751876912,50002636113061835

Используя округление банкира, мы обходим первый. Под каждой системой округления окружности мы округляем второе число (потому что оно не находится в средней точке).

.Net делает именно то, что мы ожидаем от него.