这是一个有效的浮动比较,它占了一定数量的小数位吗?

时间:2012-02-07 16:54:03

标签: c# floating-point floating-point-precision

我正在编写一个扩展方法,使用一组小数点(有效数字)来比较两个浮点数,以确定它们是否相等而不是容差或百分比差异。通过关于浮点数比较的其他问题,我看到了复杂的实现。我是否过度简化或是否有效?

/// <summary>
/// Determines if the float value is equal to (==) the float parameter according to the defined precision.
/// </summary>
/// <param name="float1">The float1.</param>
/// <param name="float2">The float2.</param>
/// <param name="precision">The precision.  The number of digits after the decimal that will be considered when comparing.</param>
/// <returns></returns>
public static bool AlmostEquals(this float float1, float float2, int precision = 2)
{
    return (Math.Round(float1 - float2, precision) == 0);
}

注意:我正在寻找小数位的比较,而不是容差。我不希望1,000,000等于1,000,001。

2 个答案:

答案 0 :(得分:2)

基于@infact的回答以及我提出的问题和答案中的一些评论

public static bool AlmostEquals(this float float1, float float2, int precision = 2) 
{ 
    float epsilon = Math.Pow(10.0, -precision) 
    return (Math.Abs(float1-float2) <= epsilon);   
} 

这有接受任何整数的好处,你可以检查&gt;使用precision = -x精度为1.0,其中x是要检查的10的幂。

我还建议使用默认的精度= 3,默认情况下,如果将此方法用于财务,则可以精确到精简到十分之一。

答案 1 :(得分:0)

如果用户想要“使用一组小数点(有效数字)比较两个浮点数”,这实际上意味着我们有一个函数

对于所有可能的XXX和YYY,AlmostEquals(14.3XXXXXXXX,14.3YYYYYYY,1)== true 最后一个参数是小数点后的小数位。

有一个简单但不幸的答案:

无法对符合此合同的此功能进行编程。有可能编写一些通常会给出正确结果的东西,但你无法预见何时会出现这种情况,因此该函数实际上毫无价值。

这里给出的解决方案已经使用AlmostEquals(0.06f,0.14f,1)= true但是0!= 1。

为什么? 第一个原因是极端的敏感性。例如:0.0999999999 ....和0.100000 ... 1 首先有不同的数字,但它们差别几乎无法区分,它们几乎完全相同。无论神话功能如何,它都不能允许计算中的微小差异。

第二个原因是我们想要用数字来实际计算。 我使用VC 2008和C#打印出Math.pow函数的正确值。第一个是精度参数,第二个是结果浮点数的十六进制值,第三个是精确十进制值。

1 3dcccccd 0.100000001490116119384765625

2 3c23d70a 0.00999999977648258209228515625

3 3a83126f 0.001000000047497451305389404296875

4 38d1b717 0.0000999999974737875163555145263671875

5 3727c5ac 0.00000999999974737875163555145263671875

6 358637bd 9.999999974752427078783512115478515625E-7

正如您所看到的,序列0.1,0.01,0.001等产生的数字非常接近,但要么略微过小或过大。

如果我们强制指定的地方必须有正确的数字怎么办? 让我们枚举4位的16个二进制值

0.0
0.0625
0.125
0.1875
0.25
0.3125
0.375
0.4375
0.5
0.5625
0.625
0.6875
0.75
0.8125
0.875
0.9375

如果我们只想在小数点后面的一个地方计算,那么16个不同的二进制数应该能够满足10个十进制数。虽然0.5完全相等,但强制使用相同的十进制数表示0.4需要0.4375而0.9需要0.9375,这会引入严重错误。

违反极端敏感的第一个条件意味着你不能对这些数字做任何合理的事情。如果你知道数字的小数位有一定值,你就不需要先计算。

C#文档甚至引用了一个例子: http://msdn.microsoft.com/en-us/library/75ks3aby.aspx

  

来电者须知

     

由于代表可能导致精度损失   十进制值作为浮点数或执行算术   对浮点值的操作,在某些情况下为Round(Double,   Int32)方法可能看起来不是将中点值舍入到最近的值   偶数值小数位数。这在图中说明   下面的示例,其中2.135舍入到2.13而不是2.14。   发生这种情况是因为内部方法将值乘以   10个数字,这种情况下的乘法运算受到a   失去精确度。