.NET的Math.Round与数学无关。没关系!

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mzl87/article/details/85332374

目录

介绍

背景

银行家舍入

从零舍入一半

舍入一半以上

Math≠数学

总结

参考


关于.NETMath.Round的意外行为

介绍

在开始之前,想象一下运行以下内容后“ a“ b会有什么值:

a = Math.Round(1.5);
b = Math.Round(2.5);

如果你的答案是“ a = 2b = 3,你就像我一样,错了。正确的答案是两者ab最终都是2。困惑吗?您可能想要坚持知道原因。

背景

大约一个星期前,我正在研究一些沿着x轴移动两个实体的代码。在库内部,它们的x值是double,但是public接口只允许以整数形式的米(这里应该是单位的意思)。我曾经使用Math.Round把这个double值转换成整数。

代码的要求之一是实体之间的距离绝不会超过一米。我使用舍入值来检查这个。

正如您现在可能已经猜到的那样,代码仍然失败。即使两个实体相距一米之外,具有相同的起始速度并且以相同的速度加速,使用相同的算法,整数值仍然偶尔返回相同的x值,因此代码保持失败。

起初,我认为这是使用double值导致的某种精度错误。但这没有多大意义,因为算法会导致两个值的精度损失相同。经过一些调试后,我终于发现它不是double而是Math.Round表现得与我预期的不同。

银行家舍入

在我弄清楚问题是在使用Math.Round之后,我很快发现了问题:在.NET中舍入的默认[ 1]实现是舍入一半到偶数” [ 2]又名银行家舍入。这意味着中点值向最接近的偶数舍入。在我在介绍中提供的示例中,这意味着两个值都是向着舍入2,最接近的偶数。

那么为什么它在.NET中实现这样呢?这是一个错误吗?

好吧,首次实施时,Microsoft遵循IEEE 754标准。这是默认Math.Round实现的原因。[3]:目前的IEEE 754标准包含五个舍入规则[4]

另一个很好的理由是,银行家的舍入没有遭受负向或正向的偏倚,就像舍弃零的舍入方法对于最合理的分布一样。

从零舍入一半

但一切都不会丢失。

如果您希望1.5要舍入2,并且2.5要舍入3(像我期望的),该Math.Round方法具有的覆盖方法,让您使用了从零舍入一半” [5]的方法来代替。

a = Math.Round(1.5, MidpointRounding.AwayFromZero);
b = Math.Round(2.5, MidpointRounding.AwayFromZero);

在上面的代码片段中,1.5舍入到22.5舍入到3

舍入一半以上

然而有趣的是,在做了一些研究之后,它看起来像从零舍入一半,也不是数学中常用的方法。相反,“舍入一半以上” [6]方法通常在数学中使用[7]。对于正数,没有差异,但对于负数,中点值向+∞舍入而不是从0舍入。

这样一来,有相同数量的分数被四舍五入到零,而在从零舍入一半的方法中,两者0.5-0.5没有舍入到零,使零成为所有其他数字的例外。

Math数学

事实证明,Math.Round方法甚至不支持数学中常用的实际舍入方法。

当我第一次发现这个时,我非常失望。毕竟,即使提供了很好的理由,库仍然会被Math调用。它确实似乎传达了某种意图。

但是写这篇文章确实让我思考:在研究之前,我甚至都没有意识到有这么多的舍入方法。每个都有自己的优点和缺点。每个都有自己的后果。如果我不知道所有这些方法,我当然不知道这些后果。所以也许别人为我做这件事并做出适当的默认决定并不是一件坏事。

毕竟,如果你的舍入的后果对你的代码至关重要,我希望你不做我曾经做过的假设,或者至少至少发现他们在一些好的老式测试中是错误的。

总结

即使这个主题不是真正的脑外科(或火箭科学),但我真的希望你能学到新的东西,或者至少享受阅读。

最后,我认为这是一个很好的提醒,即使我们每天使用的看似简单的事情都是多么复杂。这里也可能有一些关于假设的教训。;

快乐的舍入!

参考

  1. https://docs.microsoft.com/en-us/dotnet/api/system.math.round?view=netframework-4.7.2#midpoint-values-and-rounding-conventions
  2. https://en.wikipedia.org/wiki/Rounding#Round_half_to_even
  3. http://stackoverflow.com/questions/311696/why-does-net-use-bankers-rounding-as-default/6562018#6562018
  4. https://en.wikipedia.org/wiki/IEEE_754#Rounding_rules
  5. https://en.wikipedia.org/wiki/Rounding#Round_half_away_from_zero
  6. https://en.wikipedia.org/wiki/Rounding#Round_half_up
  7. https://www.mathsisfun.com/numbers/rounding-methods.html(不确定这个参考的可靠性,但我找不到很多关于数学中的舍入方法)

 

原文地址:https://www.codeproject.com/Tips/1272542/How-NETs-Math-Round-has-Nothing-to-do-with-Maths-A

猜你喜欢

转载自blog.csdn.net/mzl87/article/details/85332374