我正在从Brainteasers:
测试此代码 double d1 = 1.000001;
double d2 = 0.000001;
Console.WriteLine((d1 - d2) == 1.0);
结果是“假”。当我更改数据类型时:
decimal d1 = 1.000001M;
decimal d2 = 0.000001M;
decimal d3 = d1-d2;
Console.WriteLine(d3 == 1);
程序写出正确的答案:“True”。
此问题仅在浮点后使用6位数。 15位数的精度怎么样?
答案 0 :(得分:28)
这与精度无关 - 它与表示性舍入误差有关。
System.Decimal
能够表示大浮点数,并且可以显着降低发生任何舍入错误的风险,例如您所看到的错误。 System.Single
和System.Double
无法解决此问题,并会将这些数字四舍五入,并产生类似于您在示例中看到的问题。
System.Decimal
使用缩放因子来保存小数位的位置,从而允许精确表示给定的浮点数,而System.Single
和System.Double
仅接近您的值最好的。
有关详细信息,请参阅System.Double
:
记住一个浮点数 只能近似一个十进制数, 并且精确度 浮点数决定了如何 准确地说,这个数字近似于a 十进制数。默认情况下,为Double value包含15位十进制数字 精度,虽然最多17 数字在内部维护。该 浮点数的精度 有几个后果:
两个浮点数对于特定的看似相等 精度可能无法相等 因为他们的最低有效数字 是不同的。
使用浮点数的数学或比较运算 数字可能不会产生相同的结果 如果使用十进制数,因为 浮点数可能不会 完全接近小数 号。
答案 1 :(得分:8)
通常,检查浮点值是否相等的方法是检查 near -equality,即检查接近最小值的差值(称为 epsilon) )用于该数据类型。例如,
if (Math.Abs(d1 - d2) <= Double.Epsilon) ...
此测试用于查看d1
和d2
是否由相同的位模式表示给出或取最低有效位。
更正 (2015年3月2日新增)
经过进一步检查,代码应该更像这样:
// Assumes that d1 and d2 are not both zero
if (Math.Abs(d1 - d2) / Math.Max(Math.Abs(d1), Math.Abs(d2)) <= Double.Epsilon) ...
换句话说,取d1
和d2
之间的绝对差值,然后按d1
和d2
的最大值进行缩放,然后 em>将其与Epsilon
进行比较。
<强>参考强>
•http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx
•http://msdn.microsoft.com/en-us/library/system.double.aspx#Precision
答案 2 :(得分:6)
十进制类型实现十进制浮点,而 double 是二进制浮点。
十进制的优点在于它在舍入方面表现得像人类一样,如果用十进制值初始化它,那么该值按照您的指定存储精确。这仅适用于有限长度的十进制数,并且在可表示的范围和精度内。如果你用1.0M / 3.0M初始化它,那么就不会像在纸上写0.33333一样精确地存储它。
如果初始化带有小数的二进制FP值,它将从人类可读的十进制形式转换为很少完全相同的二进制表示。
十进制类型的主要目的是实现财务应用程序,在.NET实现中它的精度也比double高得多,但二进制FP直接由硬件支持,因此显着比十进制FP操作快。
请注意,double精确到大约15 有效数字而不是15 小数位。 d1 初始化为7有效数字值而非6,而 d2 仅有1位有效数字。它们具有显着不同的幅度这一事实也无济于事。
答案 3 :(得分:3)
浮点数字的概念是它们对于特定数字的数字并不精确。如果您需要这种功能,则应查看decimal
数据类型。
答案 4 :(得分:3)
精度不是绝对的,因为不可能精确地在十进制和二进制数之间进行转换。
在这种情况下,当以二进制表示时,.1十进制将永远重复。它转换为.000110011001100110011 ...并永远重复。没有多少精确度可以准确存储。
答案 5 :(得分:2)
避免比较浮点数的相等性。