Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Math.Round(Double, Int32) e MathF.Round(Single, Int32), e as suas MidpointRounding sobrecargas, agora retornam o valor que está corretamente arredondado para o número solicitado de dígitos fracionários com base no valor exato da entrada. Algumas entradas agora apresentam um resultado diferente (e correto) do que nas versões anteriores. Além disso, o digits argumento já não tem um limite superior.
Versão introduzida
.NET 11 Pré-visualização 7
Comportamento anterior
Anteriormente, Math.Round(value, digits, mode) calculava Round(value * 10^digits, mode) / 10^digits. Como value * 10^digits geralmente não é exatamente representável, valores ligeiramente abaixo (ou acima) de um ponto médio decimal podem escalar para um ponto médio exato ou através de uma fronteira de arredondamento, o que produz um resultado incorretamente arredondado. Valores com grandes magnitudes também podem perder completamente os seus bits fracionários durante o passo de escala.
O digits argumento também se limitava a 0-15 para double e 0-6 para float; valores fora desse intervalo lançavam ArgumentOutOfRangeException.
Math.Round(655.925, 2, MidpointRounding.AwayFromZero); // 655.93 (incorrect)
Math.Round(1111111111111111.5, 1, MidpointRounding.AwayFromZero); // 1111111111111111.6 (incorrect)
Math.Round(1.5, 16, MidpointRounding.ToEven); // throws ArgumentOutOfRangeException
Por exemplo, 655.925 é armazenado como 655.924999999999954525…, que está abaixo do 655.925 ponto médio, pelo que o resultado correto é 655.92.
Novo comportamento
A partir do .NET 11, o resultado é calculado a partir do valor exato da entrada usando aritmética de precisão arbitrária, e o valor devolvido é o valor representável mais próximo do resultado decimal corretamente arredondado.
Além disso, qualquer valor não negativo digits é aceite; apenas valores negativos lançam ArgumentOutOfRangeException. Contagens de dígitos iguais ou além da precisão necessária para contornar o tipo (17 para double, 9 para float) deixam o valor inalterado, que é o resultado correto.
Math.Round(655.925, 2, MidpointRounding.AwayFromZero); // 655.92 (correct)
Math.Round(1111111111111111.5, 1, MidpointRounding.AwayFromZero); // 1111111111111111.5 (correct)
Math.Round(1.5, 16, MidpointRounding.ToEven); // 1.5 (no longer throws)
Tipo de mudança disruptiva
Esta mudança é uma mudança comportamental.
Motivo da mudança
Os resultados anteriores estavam incorretos para uma grande fração das entradas — cerca de 5% de valores aleatórios ao longo do intervalo suportado digits diferiam do resultado corretamente arredondado. O comportamento anterior também rejeitou ou manuseou mal entradas finitas grandes. A nova implementação é consistente com IEEE: arredonda o valor exato da entrada e devolve o resultado representável mais próximo, que corresponde ao valor já value.ToString("F{digits}") produzido.
Os caps 0-15 e 0-6 digits eram uma limitação artificial ligada à antiga abordagem escala a10^digits escala. Como a implementação exata é correta para qualquer contagem de dígitos, o limite foi levantado ao mesmo tempo para evitar uma segunda quebra comportamental mais tarde.
Para mais informações, veja dotnet/runtime#130574.
Ação recomendada
A maior parte do código não precisa de alterações e beneficia dos resultados corrigidos.
Se depender do resultado anterior exato (incorreto), arredonda usando explicitamente a abordagem anterior, por exemplo, Math.Round(value * pow10, mode) / pow10. Alternativamente, realiza-se o arredondamento usando decimal quando os valores representam quantidades de base 10, como moeda.
double e float são tipos binários de ponto flutuante e não podem representar exatamente a maioria das frações decimais. Para arredondamento decimal exato das quantidades decimais, prefira Decimal.
<System.Numerics.Decimal32>, <System.Numerics.Decimal64> e <System.Numerics.Decimal128> são tipos IEEE 754 baseados em decimais com intervalos e funcionalidades expandidos, sendo também adequados para este tipo de trabalho.
APIs afetadas
- System.Math.Round(Double, Int32)
- System.Math.Round(Double, Int32, MidpointRounding)
- System.MathF.Round(Single, Int32)
- System.MathF.Round(Single, Int32, MidpointRounding)
O mesmo comportamento corrigido e fluxo de intervalo digits elevado através dos pontos de entrada numéricos da interface que delegam a estes métodos, por exemplo, double.Round, float.Round, Half.Round, e NFloat.Round.
Math.Round(Decimal, Int32) e o argumento Math.Round(Double) único e MathF.Round(Single) as sobrecargas não são afetados.