四舍五入

时间:2015-06-25 14:50:07

标签: c# .net

我有这个“科学应用程序”,其中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

是否有一种或多或少“正确”的方式来处理这种情况,如果是这样,它会是什么?

3 个答案:

答案 0 :(得分:2)

我认为您现有的解决方案(使用Math会员的Decimal版本)是正确的。

潜在的问题是,您希望数字根据其基数10表示进行舍入,但您已将它们存储为基数2浮点数。所提供的2.135示例是其中基础2表示与基础10不完全匹配的边缘情况之一。

要获得预期的舍入行为,您必须将数字转换为基数10.最简单的方法就是您正在做的事情:暂时将数字转换为Decimal长足以拨打Math.Round

答案 1 :(得分:1)

Since floating point trades precision for range, the decimal value 2.135 can't be exactly represented in binary. The [closest] binary representation works out to be something like 0.1348876953125 decimal, so the rounding is correct (if not intuitively obvious). You should read Goldberg's paper, "What every computer scientist should know about floating-point arithmetic" (ACM Computing Surveys, Volume 23 Issue 1, March 1991, pp. 5-48) Abstract. Floating-point arithmetic is considered as esoteric subject by many people. This is rather surprising, because floating-point is ubiquitous in computer systems: Almost every language has a floating-point datatype; computers from PCs to supercomputers have floating-point accelerators; most compilers will be called upon to compile floating-point algorithms from time to time; and virtually every operating system must respond to floating-point exceptions such as overflow. This paper presents a tutorial on the aspects of floating-point that have a direct impact on designers of computer systems. It begins with background on floating-point representation and rounding error, continues with a discussion of the IEEE floating point standard, and concludes with examples of how computer system builders can better support floating point.

答案 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