我有这个“科学应用程序”,其中Single
值应该在UI中呈现之前进行舍入。根据{{3}},由于“精度损失”,Math.Round(Double, Int32)
方法有时会“意外地”表现,例如舍入2.135至2.13而不是2.14。
据我所知,这个问题与“银行家的四舍五入”无关(例如参见this MSDN article)。
在应用程序中,有人显然选择通过在舍入(Single
之前)将Decimal
显式转换为Math.Round((Decimal)mySingle, 2)
来调用Math.Round(Decimal, Int32)
重载来解决此问题。除了可能出现的二进制到十进制转换问题之外,如果OverflowException
值太小或大到不适合Single
,此“解决方案”也可能导致Decimal
抛出类型。
如果转换失败,捕获此类错误以返回Math.Round(Double, Int32)
的结果,并不会让我成为完美的解决方案。也不会重写应用程序以便一直使用Decimal
。
是否有一种或多或少“正确”的方式来处理这种情况,如果是这样,它会是什么?
答案 0 :(得分:2)
我认为您现有的解决方案(使用Math会员的Decimal
版本)是正确的。
潜在的问题是,您希望数字根据其基数10表示进行舍入,但您已将它们存储为基数2浮点数。所提供的2.135示例是其中基础2表示与基础10不完全匹配的边缘情况之一。
要获得预期的舍入行为,您必须将数字转换为基数10.最简单的方法就是您正在做的事情:暂时将数字转换为Decimal
长足以拨打Math.Round
。
答案 1 :(得分:1)
答案 2 :(得分:0)
我只是查看了文档,似乎有一个枚举,您可以传入Math.Round()
。如果您更改为Math.Round(Double, Int32, MidpointRounding.AwayFromZero)
,则应获得所需的结果。
https://msdn.microsoft.com/en-us/library/vstudio/ef48waz8(v=vs.100).aspx
编辑:刚用这些数字测试过。更改了数字和
double abc = 2.335;
Console.WriteLine(Math.Round(abc, 2, System.MidpointRounding.AwayFromZero));
abc = 2.345;
Console.WriteLine(Math.Round(abc, 2, System.MidpointRounding.AwayFromZero));
abc = 2.335;
Console.WriteLine(Math.Round(abc, 2));
abc = 2.445;
Console.WriteLine(Math.Round(abc, 2));
并得到了这些结果。
2.34
2.35
2.34
2.44
编辑2:我使用了你给出的原始数字,它正在破碎。我认为通过使用AwayFromZero它可以解决双舍入(我认为它仅适用于银行家舍入),但事实并非如此。如果您确实需要从舍入中寻找的精度,则必须创建自己的函数,通过转换为double或其他方法为您提供所需的精度,但我一直在寻找一段时间而没有发现了什么,我会回来查看你是否想出一个解决方案。
double abc = 2.135;
Console.WriteLine(Math.Round(abc, 2, System.MidpointRounding.AwayFromZero));
abc = 2.145;
Console.WriteLine(Math.Round(abc, 2, System.MidpointRounding.AwayFromZero));
abc = 2.135;
Console.WriteLine(Math.Round(abc, 2));
abc = 2.145;
Console.WriteLine(Math.Round(abc, 2));
2.13
2.15
2.13
2.14