9

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

/// <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。

4

2 回答 2

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);   
} 

这具有接受任何整数的好处,您可以使用精度 = -x 检查 > 1.0 的精度,其中 x 是要检查的 10 的幂。

我还建议将默认精度设置为 3,如果此方法用于财务,则默认情况下,这将使您的精度降至十分之一美分。

于 2012-02-07T18:27:55.803 回答
1

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

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

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

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

这里给出的解决方案已经与AlmostEquals(0.06f, 0.14f, 1) = true but 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 位,并且这种情况下的乘法运算会损失精度。

于 2012-02-09T13:45:59.850 回答