Существует ли способ сделать 'корректное' арифметическое округление в.NET? / C#

Я пробую к раунду число к, он - первый десятичный разряд и, рассматривая различные возможности MidpointRounding, который, кажется, работает хорошо. Проблема возникает хотя, когда то число имеет sunsequent десятичные разряды, которые арифметически влияли бы на округление.

Пример:

С 0.1, 0.11..0.19 и 0.141..0.44 работает:

Math.Round(0.1, 1) == 0.1
Math.Round(0.11, 1) == 0.1
Math.Round(0.14, 1) == 0.1
Math.Round(0.15, 1) == 0.2
Math.Round(0.141, 1) == 0.1

Но с 0.141..0.149 это всегда возвращается 0.1, хотя 0.146..0.149 если вокруг к 0.2:

Math.Round(0.145, 1, MidpointRounding.AwayFromZero) == 0.1
Math.Round(0.146, 1, MidpointRounding.AwayFromZero) == 0.1
Math.Round(0.146, 1, MidpointRounding.ToEven) == 0.1
Math.Round(0.146M, 1, MidpointRounding.ToEven) == 0.1M
Math.Round(0.146M, 1, MidpointRounding.AwayFromZero) == 0.1M

Я пытался придумать функцию, которая решает эту проблему, и она работает хорошо на этот случай, но конечно она гламурным образом перестала работать, если Вы пробуете к раунду т.е. 0.144449 к он - первая десятичная цифра (который должен быть 0.2, но результаты 0.1.) (Который не работает с Математикой. Вокруг () также.)

private double "round"(double value, int digit)
{
    // basically the old "add 0.5, then truncate to integer" trick
    double fix = 0.5D/( Math.Pow(10D, digit+1) )*( value >= 0 ? 1D : -1D );
    double fixedValue = value + fix;

    // 'truncate to integer' - shift left, round, shift right
    return Math.Round(fixedValue * Math.Pow(10D, digit)) / Math.Pow(10D, digit);
}

Я предполагаю, что решение состояло бы в том, чтобы перечислить все цифры, найти первое значение больше, чем 4 и затем окружить или иначе округлить в меньшую сторону. Проблема 1: Это кажется глупым, проблема 2: Я понятия не имею, как перечислить цифры без огромного количества умножения и subtractios.

Длинная короткая история: Что лучший способ состоит в том, чтобы сделать это?

6
задан Charles 7 September 2011 в 00:32
поделиться

4 ответа

Math.Round () работает правильно.

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

Идея округления до середины состоит в том, что половина промежуточных чисел должна округляться в большую сторону, а половина - в меньшую. Таким образом, для чисел от 0,1 до 0,2 половина из них должна округляться до 0,1, а половина - до 0,2. Среднее значение между этими двумя числами составляет 0,15, так что это порог для округления в большую сторону. 0,146 меньше 0,15, поэтому необходимо округлить до 0,1.

                    Midpoint
0.10                  0.15                  0.20
 |----------------|----|---------------------|
                0.146
       <---- Rounds Down
20
ответ дан 8 December 2019 в 02:09
поделиться

Я не понимаю, чего вы здесь пытаетесь достичь. 0,149 с округлением до одного десятичного знака равно 0,1, а не 0,2

15
ответ дан 8 December 2019 в 02:09
поделиться

Округление не является итерационным процессом, вы округляете только один раз.

Поэтому 0.146, округленное до 1 десятичной цифры, равно 0.1.

Вы не делаете этого:

0.146 --> 0.15
0.15 -->  0.2

Вы делаете только это:

0.146 --> 0.1

Иначе следующее:

0.14444444444444446

также округлилось бы до 0.2, но этого не происходит, и не должно происходить.

6
ответ дан 8 December 2019 в 02:09
поделиться

Не пытайтесь усугубить "ошибки" округления. Что вы и пытаетесь сделать.

.146 следует округлить до .1, если вы собираетесь округлить до одного десятичного знака.

Округляя сначала до .15, а затем снова до .2, вы просто вводите большую ошибку округления, а не меньшую.

5
ответ дан 8 December 2019 в 02:09
поделиться