【问题标题】:Compare two floats比较两个浮点数
【发布时间】:2011-08-24 18:08:02
【问题描述】:
#include <stdbool.h>

bool Equality(double a, double b, double epsilon)
{
  if (fabs(a-b) < epsilon) return true;
  return false;
}

我尝试过这种方法来比较两个双打,但我总是遇到问题,因为我不知道如何选择epsilon,实际上我想比较小数(小数点后的 6 个 6 位),如 0.000001。我尝试了一些数字,有时我得到0.000001 != 0.000001,有时我得到0.000001 == 0.000002 除了与 epsilon 进行比较之外,还有其他方法吗?

我的目的是比较两个双打(在我的情况下代表时间)。以毫秒为单位表示时间的变量 t 是双精度值。它由另一个函数 0.000001 然后 0.000002 等递增。每次 t 变化时,我想检查它是否等于另一个双 tt 类型的变量,以防 tt == t,我有一些指令要执行..
感谢您的帮助

【问题讨论】:

  • “厄普西隆”,而不是厄普塞隆。 en.wikipedia.org/wiki/Epsilon
  • 0.000001 和 0.000002 都没有精确的浮点表示,两者都是带有循环小数的无限二进制分数。此外,fabs(a-b) 容易发生灾难性的取消。
  • 如果你不知道这到底是怎么一回事,你至少可以通过使用 epsilon 来避免第一个问题,它可以精确地表示为 float,比如 0.00000095367431640625,即 2^-20并接近你想要的 10^-6
  • 你为什么首先使用float?对于其他浮点类型,首选不带 very strongdouble。当然,无论是floats 还是doubles,您的问题仍然存在。

标签: c comparison


【解决方案1】:

看这里:http://floating-point-gui.de/errors/comparison/

由于舍入误差,大多数浮点数最终会变成 稍微不精确。只要这种不精确性保持很小,它就可以 通常会被忽略。 但是,这也意味着预期的数字是 相等(例如,当通过不同的正确计算相同的结果时 方法)通常略有不同,简单的相等性测试失败

当然还有What Every Computer Scientist Should Know About Floating-Point Arithmetic

【讨论】:

    【解决方案2】:

    首先:计算布尔值(使用&lt; 运算符)然后将其包装在另一个布尔值中是没有意义的。就这样写吧:

    bool Equality(float a, float b, float epsilon)
    {
      return fabs(a - b) < epsilon;
    }
    

    其次,您的 epsilon 本身可能没有很好地表示为 float,因此看起来不像您期望的那样。尝试使用 2 的负幂,例如 1/1048576。

    【讨论】:

    • 感谢您的回复。当 eps = 0.000001 时,我得到一个错误的结果(0.000001 == 0.000002),但是当我使用类似 1/1048576 的东西时,结果总是!=(即使我将 0.000001 与 0.000001 进行比较):(
    【解决方案3】:

    或者,您可以比较两个整数。只需将您的两个浮点数乘以所需的精度并将它们转换为整数。 确保正确向上/向下舍入。这是它的样子:

    BOOL floatcmp(float float1, float float2, unsigned int precision){
       int int1, int2;
    
       if (float1 > 0)
          int1 = (int)(float1 * precision + .5);
       else
          int1 = (int)(float1 * precision - .5);
    
       if (float2 > 0)
          int2 = (int)(float2 * precision + .5);
       else
          int2 = (int)(float2 * precision - .5);
    
       return (int1 == int2);
    }
    

    【讨论】:

      【解决方案4】:

      请记住,当 float a = +2^(254-127) * 1.___22 zeros___1float b = +2^(254-127) * 1.___23 zeros___ 时,我们期望 abs(a-b) &lt; epsilon 而不是比 epsilon 大得多的 a - b = +2^(254-127-23) * 1.___23 zeros___ = 20282409603651670423947251286000...

      【讨论】:

        【解决方案5】:

        我编写并测试了这段代码。它似乎有效。

        public static boolean equal(double a, double b) {
            final long fm = 0xFFFFFFFFFFFFFL;       // fraction mask
            final long sm = 0x8000000000000000L;    // sign mask
            final long cm = 0x8000000000000L;       // most significant decimal bit mask
            long c = Double.doubleToLongBits(a), d = Double.doubleToLongBits(b);        
            int ea = (int) (c >> 52 & 2047), eb = (int) (d >> 52 & 2047);
            if (ea == 2047 && (c & fm) != 0 || eb == 2047 && (d & fm) != 0) return false;   // NaN 
            if (c == d) return true;                            // identical - fast check
            if (ea == 0 && eb == 0) return true;                // ±0 or subnormals
            if ((c & sm) != (d & sm)) return false;             // different signs
            if (abs(ea - eb) > 1) return false;                 // b > 2*a or a > 2*b
            d <<= 12; c <<= 12;
            if (ea < eb) c = c >> 1 | sm;
            else if (ea > eb) d = d >> 1 | sm;
            c -= d;
            return c < 65536 && c > -65536;     // don't use abs(), because:
            // There is a posibility c=0x8000000000000000 which cannot be converted to positive
        }
        public static boolean zero(double a) { return (Double.doubleToLongBits(a) >> 52 & 2047) < 3; }
        
        • 如果任何数字为 NaN,则数字不相等。
        • 如果两个数相同,则相等。这是一个快速的初始检查。
        • 如果两个数字都是 +0、-0 或次正规中的任何一个,则数字相等。
        • 如果数字有不同的符号,数字就不相等。如果两个数字几乎都为 0(但不是 ±0 或次正规数)且符号不同,这看起来是一种错误的方法。但是如果你将这些数字与另一个相乘呢?一个结果是负面的,另一个是正面的。所以我们很严格,这是正确的。
        • 如果指数相差 2 或更大,则数字不相等,因为一个数字至少是另一个数字的 2 倍。
        • 如果指数的差正好为 1,则正确移动其中一个数字的分数。
        • 如果两个分数的差很小,则数字相等。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-01-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-10
          • 1970-01-01
          • 2020-02-23
          相关资源
          最近更新 更多