Math.Round と MathF.Round は正しく丸められた結果を返します

Math.Round(Double, Int32)MathF.Round(Single, Int32)、およびそれらの MidpointRounding オーバーロードは、入力の 正確な 値に基づいて、要求された小数部の桁数に正しく丸められた値を返すようになりました。 一部の入力は、以前のリリースとは異なる (正しい) 結果に丸めるようになりました。 さらに、 digits 引数に上限が設定されなくなりました。

導入されたバージョン

.NET 11 Preview 7

以前の動作

以前は、計算されたRound(value * 10^digits, mode) / 10^digitsMath.Round(value, digits, mode)value * 10^digitsは一般的に正確に表現できないため、10 進中点をわずかに下回る (またはそれ以上) 値は、正確な中間点または丸め境界を越えてスケーリングされる可能性があり、結果が正しく丸められません。 大きな大きさの値は、スケーリングステップ中に小数部のビットを完全に失う可能性もあります。

digits引数は、doubleの場合は 0 から 15、floatの場合は 0 から 6 に制限されました。その範囲外の値は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

たとえば、 655.925655.924999999999954525…として格納され、 655.925 の中間点の下に格納されるため、正しい結果が 655.92されます。

新しい動作

.NET 11 以降では、任意の精度の算術演算を使用して入力の正確な値から結果が計算され、返される値は、正しく丸められた 10 進数の結果に最も近い表現可能な値になります。

また、負以外の digits 値も受け入れられます。負の値のみが ArgumentOutOfRangeExceptionスローされます。 型のラウンドトリップに必要な有効桁数以上の数字 ( doubleの場合は 17、 floatの場合は 9) は変更されず、正しい結果になります。

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)

破壊的変更の種類

この変更は 動作の変更です。

変更理由

前の結果は、入力の大部分が正しくありませんでした。サポートされている digits 範囲のランダムな値の約 5% は、正しく丸められた結果とは異なります。 以前の動作では、大きな有限入力も拒否または誤って処理されました。 新しい実装は IEEE 整合性です。入力の正確な値を丸め、最も近い表現可能な結果を返します。これは、既に生成 value.ToString("F{digits}") 値と一致します。

0-15 と 0-6 の digits キャップは、古いスケール バイ 10^digits アプローチに関連する人為的な制限でした。 正確な実装は任意の数字の数に対して正しいので、後で 2 回目の動作の中断を回避するために、同時に上限が解除されました。

詳細については、「 dotnet/runtime#130574」を参照してください。

ほとんどのコードは変更を必要とせず、修正された結果からメリットを得られます。

正確な前の (正しくない) 出力に依存している場合は、前の方法を明示的に使用して丸めます (たとえば、 Math.Round(value * pow10, mode) / pow10)。 または、値が通貨などの基準数量 10 を表す場合は、 decimal を使用して丸めを実行します。

doublefloat はバイナリ浮動小数点型であり、ほとんどの小数部を正確に表すことはできません。 10 進数の正確な 10 進数の丸めには、 Decimalを使用します。 <System.Numerics.Decimal32>、 <System.Numerics.Decimal64>、および <System.Numerics.Decimal128> は、範囲と機能が拡張された IEEE 754 の 10 進数ベースの型であり、この種類の作業にも適しています。

影響を受ける API

同じ修正された動作とリフトされた digits 範囲は、 double.Roundfloat.RoundHalf.RoundNFloat.Roundなど、これらのメソッドに委任される数値インターフェイス エントリ ポイントを通過します。 Math.Round(Decimal, Int32) および単一引数の Math.Round(Double) および MathF.Round(Single) オーバーロードは影響を受けません。