我一直在研究这个问题的答案,但找不到答案,所以我想在这里问一下。
.Net(C#)提供了两种舍入double值的方法,它们都调用了Math.Round(),但是指定了一个不同的MidPointRounding选项,ToEven或AwayFromZero。到现在为止还挺好。
事实证明,AwayFromZero方法可能并不总是按预期执行某些值,例如将2.135舍入到2位小数的情况,其中当预期结果为2.14时结果为2.13。在“调用者注释”部分的.Net文档中详细记录了这个“错误”:http://msdn.microsoft.com/en-us/library/f5898377.aspx
使用ToEven方法舍入相同的值也会将其舍入为2.13。
但是,在ToString("C")
方法的帮助下将原始值显示到屏幕(2.135)时,该方法将值转换为字符串舍入为2位小数,显示的值为2.14。
事实证明,在没有Math.Round中发现的错误的情况下,使用ToString("C")
舍入值正确地舍入零,唯一的问题是结果是字符串,我不想要解析结果再次得到一个正确的数字。
有谁知道哪种舍入方法在内部使用ToString("C")
方法?
有没有人对此行为有任何解释?
答案 0 :(得分:3)
嗯,就你的问题而言......
是的,它对你没有帮助。它并没有真正"对数字进行四舍五入 - 它使用数字的字符串表示。浮点数和双打数是不是十进制数 - 它们是二进制数;它们具有二进制的固定精度,而不是十进制。只有当你将它转换为字符串(或者说,例如decimal
)时,它才真正是小数。
是的,它就在MSDN页面上。舍入操作涉及将数字乘以10乘以digits
的幂。在某些情况下,这会导致精确损失(与" double不是小数"。
处理这个问题的正确方法当然是使用正确的工具来完成工作。双打不是小数,所以不要指望它们。任何将double转换为十进制(字符串或数字,它都无关紧要)将意味着潜在的精度损失。
解决方法是先将double转换为十进制,进行舍入,然后再将其转换为double:
(double)Math.Round((decimal)2.135d, 2);
然而,只是 - 一个变通方法,一个骗子,一个黑客。它并不能保证任何事情,因为您仍在使用不是十进制的数字。就像您无法在不丢失十进制数精度的情况下代表1 / 3
一样,您无法在二进制数中表示1 / 10
。看起来你可以使用0.1D
的事实是因为每次你将数字显示为字符串时,它实际上都是舍入的 - 这给浮动提供了"小数"的外观,即使它们& #39;不是。如果它没有给你你所期望的,我们称之为精确错误,但它不是浮动本身的错误 - 它只是如果你期望事情表现出一种他们的方式不是为了表现得像,你做错了。
这只是你真正应该使用decimal
进行金钱操作的原因之一 - 它并不能保证无限的精确度,但它保证的是你得到的与人类会计师(在实践中也使用有限精度的十进制数字)相同的结果。
答案 1 :(得分:0)
欢迎使用浮点数学。简单的浮点类型没有您认为的准确性。虽然你可以抱怨你认为“2.135”没有正确舍入 - 实际上这可能在内存中表示为更像2.135000001 - 应该正确舍入为2.14 - 其中你引用的文件提到了。
我不打算重复或引用关于C#或其他许多语言的大量浮点信息,而不是说你的研究。看看Stackoverflow,看看关于这个主题的众多问题。有问题和答案。
简短回答:为您想要的工作使用正确的类型。