Pergunta

Estou tentando arredondar um número ao seu primeiro lugar decimal e, considerando as diferentes opções de meio, isso parece funcionar bem. Um problema surge quando esse número possui lugares decimais de sol que afetariam aritmeticamente o arredondamento.

Um exemplo:

Com 0.1, 0.11..0.19 e 0.141..0.44 funciona:

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

Mas com 0.141..0.149 sempre retorna 0.1, Apesar 0.146..0.149 deve arredondar para 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

Eu tentei inventar uma função que aborda esse problema, e funciona bem para este caso, mas é claro que falha glamorosamente se você tentar redondamente, ou seja, 0.144449 para seu primeiro dígito decimal (que deve ser 0.2, mas resultados 0.1.) (Isso também não funciona com Math.Round ().)

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);
}

Presumo que uma solução seria enumerar todos os dígitos, encontrar o primeiro valor maior que 4 e depois arredondar, ou então arredondado. Problema 1: Isso parece idiota, Problema 2: Não tenho idéia de como enumerar os dígitos sem um gazilhão de multiplicações e subttrios.

Para encurtar a história: qual é a melhor maneira de fazer isso?

Foi útil?

Solução

Math.Round() está se comportando corretamente.

Quando você está realizando arredondamento padrão do ponto médio, você nunca precisa olhar além de 1 dígito decimal além de onde está arredondando. Se você está arredondando para o décimo mais próximo, nunca precisará olhar além do segundo dígito após o ponto decimal.

A idéia com arredondamento de ponto médio é que metade dos números intermediários deve arredondar e metade deve terminar. Portanto, para números entre 0,1 e 0,2, metade deles deve arredondar para 0,1 e metade deve arredondar para 0,2. O ponto médio entre esses dois números é de 0,15, então esse é o limite para arredondar. 0,146 é menor que 0,15, portanto, deve diminuir para 0,1.

                    Midpoint
0.10                  0.15                  0.20
 |----------------|----|---------------------|
                0.146
       <---- Rounds Down

Outras dicas

Eu não entendo o que você está tentando realizar aqui. 0,149 arredondado para um lugar decimal é 0,1, não 0,2

O arredondamento não é um processo iterativo, você arredonda apenas uma vez.

Então 0,146 arredondado para 1 dígito decimal é 0.1.

Você não faz isso:

0.146 --> 0.15
0.15 -->  0.2

Você só faz isso:

0.146 --> 0.1

Caso contrário, o seguinte:

0.14444444444444446

também seria de 0,2, mas não deveria, e não deveria.

Não tente agravar os 'erros' de arredondamento. É isso que você está tentando fazer.

.146 deve Continuar para .1 se você estiver indo para um lugar decimal.

Ao arredondá -lo para .15 primeiro, depois novamente para .2 Você está apenas introduzindo mais erro de arredondamento, não menos.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top