四舍五入到131.575会导致奇数而不是偶数

时间:2018-08-29 11:38:57

标签: c# .net floating-point numbers rounding

请考虑以下代码示例:

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的浮点数是否被破坏,而不是浮点数本身。

1 个答案:

答案 0 :(得分:4)

使用浮点数时,会四舍五入。

在显示的代码中,源文本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会得出正确的结果。