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

Почему System.MidpointRounding.AwayFromZero не округляется в этом случае?

В .NET, почему System.Math.Round(1.035, 2, MidpointRounding.AwayFromZero) дает 1,03 вместо 1.04? Я чувствую, что ответ на мой вопрос лежит в разделе "Примечание для звонящих" в http://msdn.microsoft.com/en-us/library/ef48waz8.aspx, но я не могу завершить голова вокруг объяснения.

4b9b3361

Ответ 1

Ваше подозрение верно. Номера с дробной частью, выраженные как литералы в .NET, по умолчанию удваиваются. Двойной (как float) является приближение десятичного значения, а не точное десятичное значение. Это самое близкое значение, которое может быть выражено в base-2 (двоичном). В этом случае аппроксимация всегда исчезает на малой стороне 1,035. Если вы пишете его с помощью явного десятичного числа, он работает так, как вы ожидаете:

Console.WriteLine(Math.Round(1.035m, 2, MidpointRounding.AwayFromZero));
Console.ReadKey();

Чтобы понять, почему двойники и поплавки работают так, как они делают, представьте себе число 1/3 в десятичном (или двоичном, которое страдает от одной и той же проблемы). Вы не можете - это переводится в .3333333...., что означает, что для его точного представления потребуется бесконечное количество памяти.

Компьютеры обходятся с помощью приближений. Я бы точно объяснил, как, но я, вероятно, ошибаюсь. Вы можете прочитать все об этом здесь: http://en.wikipedia.org/wiki/IEEE_754-1985

Ответ 2

Бинарное представление 1.035d имеет значение 0x3FF08F5C28F5C28F, что на самом деле составляет 1.03499999999999992006394222699E0, поэтому System.Math.Round(1.035, 2, MidpointRounding.AwayFromZero) дает 1,03 вместо 1,04, поэтому он исправляется.

Однако двоичное представление 4.005d равно 0x4010051EB851EB85, что равно 4.00499999999999989341858963598, поэтому System.Math.Round(4.005, 2, MidpointRounding.AwayFromZero) должно давать 4.00, но оно дает 4.01, что является неправильным (или умным) исправлением "). Если вы проверите его в MS SQL, выберите ROUND (CAST (4.005 AS float), 2), он 4.00 Я не понимаю, почему .NET применяет это "умное исправление", которое ухудшает ситуацию.

Вы можете проверить двоичное представление double в: http://www.binaryconvert.com/convert_double.html

Ответ 3

Это потому, что представление BINARY 1.035 ближе к 1,03, чем 1,04

Для лучшего результата сделайте это так -

decimal result = decimal.Round(1.035m, 2, MidpointRounding.AwayFromZero);

Ответ 4

Я считаю, что пример, о котором вы говорите, - это другая проблема; Насколько я понимаю, они говорят, что 0.1 не хранится в float, как ровно 0,1, он фактически немного отключен из-за того, как float хранится в двоичном формате. Таким образом, предположим, что он на самом деле больше похож на 0.0999999999999 (или аналогичный), что-то очень, очень немного меньше 0,1 - настолько незначительно, что он не имеет особого значения. Ну, нет, они говорят: одна заметная разница заключалась бы в том, что добавление этого к вашему номеру и округлению на самом деле показалось бы неправильным, потому что, несмотря на то, что цифры очень близки, они все еще считаются "меньше".5 для округления.

Если я неправильно понял эту страницу, я надеюсь, что кто-то меня исправит:)

Я не понимаю, как это связано с вашим вызовом, потому что вы более явны. Возможно, он просто хранит ваш номер аналогичным образом.

Ответ 5

Угадаю, я бы сказал, что внутренне 1.035 не может быть представлено в двоичном формате точно как 1.035, и, вероятно, (под капотом) 1.0349999999999999, и именно поэтому оно округляется вниз.

Только предположение.