C# 这是一个有效的浮点比较,占一定的小数位数吗?

C# 这是一个有效的浮点比较,占一定的小数位数吗?,c#,floating-point,floating-point-precision,C#,Floating Point,Floating Point Precision,我正在编写一个扩展方法,使用一组小数点(有效数字)来比较两个浮点,以确定它们是否相等,而不是公差或百分比差。通过查看有关浮点比较的其他问题,我看到了复杂的实现。我是否过于简单化了,或者这是正确的 /// <summary> /// Determines if the float value is equal to (==) the float parameter according to the defined precision. /// </summary> ///

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

/// <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。
///花车2。
///精确性。比较时将考虑的小数点后的位数。
/// 
公共静态bool AlmostEquals(此浮点float1,float float2,int精度=2)
{
返回值(数学四舍五入(float1-float2,精度)=0);
}

注意:我要找的是小数位的比较,而不是公差。我不希望1000000等于1000001。

基于@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);   
} 
public static bool AlmostEquals(此浮点float1,float float2,int精度=2)
{ 
浮点ε=数学功率(10.0,-精度)
通过使用precision=-x返回(Math.Abs(float1-float2))1.0精度,其中x是10的幂来检查

我还建议将默认精度设置为3,如果此方法用于金融,默认情况下,精度将降至十分之一便士。

如果用户希望“使用设定数量的小数点(有效数字)比较两个浮点数”,这实际上意味着我们有一个函数

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

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

不可能对该函数进行编程以实现本合同。可能会对通常会给出正确结果的内容进行编程,但您无法预见何时会出现这种情况,因此该函数实际上毫无价值

这里给出的解已经与AlmostEquals(0.06f,0.14f,1)=真但0!=1决裂

为什么?? 第一个原因是极端敏感。例如:0.099999999…和0.100000…1 首先,它们有不同的数字,但它们的差异几乎无法区分,它们几乎完全相等。不管这个神秘的函数做什么,它都不允许在计算上有微小的差异

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

1 3DCCCD 0.1000000149011919384765625

2 3c23d70a 0.0099999977648258209228515625

3 3a83126f 0.001000000047497451305389404296875

4 38d1b717 0.00009999974737875163555145263671875

5 3727c5ac 0.0000099999747377875163555145263671875

6 358637bd 9.9999997747524270783512115478515625E-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#文档甚至引用了一个例子:

来电人士须知

因为表示结果可能会导致精度损失 十进制值作为浮点数或执行算术运算 对浮点值的操作,在某些情况下是四舍五入(Double, Int32)方法可能无法将中点值舍入到最接近的值 十进制位置的偶数值。如中所示 以下示例,其中2.135四舍五入为2.13,而不是2.14。 发生这种情况是因为该方法在内部将值乘以 10位,本例中的乘法运算会遇到 精度损失


我会编写一组单元测试来检查您的算法是否适合您的需要。我喜欢这个方法名称:AlmostEquals…我已经编写了一些单元测试,它们通过了我提供的值,但我希望读者能够更深入地理解浮点实现/行为。
Math.Round()
如果精度小于零或大于15,将引发异常。那么您如何说“我有两个浮点值,它们都在100万到200万之间;我希望它们等于最接近的千”?之所以选择这个作为答案,是因为它解决了关注小数位数的想法,并扩展了允许精度为负值的方法。Props to@infact for
return(Math.Abs(float1-float2)它不起作用,C部分甚至引用了一个例子:因为精度损失[…]整轮(双精度,Int32)方法可能不会将中点值四舍五入到小数位数位置中最接近的偶数值。下面的示例说明了这一点,其中2.135被四舍五入到2.13,而不是2.14。出现这种情况的原因是该方法在内部将值乘以10位,并且在这种情况下,乘法运算会损失精度n、 你的前提是不正确的。虽然浮点数是近似值,但它是肯定的