考虑以下代码示例:
var tests = new List<double> { 131.505, 131.515, 131.525, 131.535, 131.545, 131.555, 131.565, 131.575, 131.585, 131.595 };
foreach (double n in tests)
{
Console.WriteLine("{0} => {1}", n, Math.Round(n, 2, MidpointRounding.ToEven));
}
And its output:
131.505 => 131.5
131.515 => 131.51 <- wt*
131.525 => 131.52
131.535 => 131.54
131.545 => 131.54
131.555 => 131.56
131.565 => 131.56
131.575 => 131.57 <- wt*
131.585 => 131.58
131.595 => 131.6
I was expecting:
131.515 => 131.52
131.575 => 131.58
为什么
MidpointRounding.ToEven
算法生成的数字的末尾有奇数;有什么我可以解决的办法吗?背景:我正在将相同的数字传递给PHP
round($n, 2, PHP_ROUND_HALF_EVEN)
函数。目的是使两个脚本产生相同的结果。我希望能对这个特定示例中的幕后发生的情况进行解释,而不要使用固定的“因为浮点数学运算已损坏”响应。我想知道为什么PHP能够产生预期的结果,而.NET却不能?我想知道.NET的浮点数是否被破坏,而不是浮点数本身。
最佳答案
使用浮点数时,会四舍五入。
在显示的代码中,源文本131.515
从十进制数字转换为double
值。由于不能在double
中精确表示131.515,因此会生成最接近的可表示值。这是131.5149999999999863575794734060764312744140625。
因此,当调用Math.round
时,将为其指定值131.5149999999999863575794734060764312744140625。由于该值小于131.515,因此四舍五入为131.51。
正如马克·迪金森(Mark Dickinson)在评论中指出的那样,Math.Round
本身不是一个完美的实现,并且包含内部舍入错误。对于源文本131.525
,转换为double
会产生131.525000000000005684341886080801486968994140625。将其四舍五入为小数点后的两位十进制数字应产生131.53。但是,Math.Round
显然是通过首先乘以100来计算结果的。由于乘以100的数学结果无法精确表示,因此将其舍入为最接近的可表示值13152.5。然后使用“四舍五入关系”将其四舍五入为整数,得出13152。然后将其除以并将其转换为十进制,得出“ 131.52”。
因此,我们不能期望Math.round
产生正确的结果。