Floating point 如何检测某个对象的大小是否因浮点精度不足而改变?

Floating point 如何检测某个对象的大小是否因浮点精度不足而改变?,floating-point,precision,Floating Point,Precision,假设我有一个矩形,边由左、右、上、下指定。然后: myrect.left = 5.5 myrect.right = 21.32 xSizeBeforeTranslation = myrect.right - myrect.left translation = 50.0 myrect.left += translation myrect.right += translation xSizeAfterTranslation == xSizeBeforeTranslation // This

假设我有一个矩形,边由左、右、上、下指定。然后:

myrect.left = 5.5
myrect.right = 21.32

xSizeBeforeTranslation = myrect.right - myrect.left

translation = 50.0

myrect.left += translation
myrect.right += translation

xSizeAfterTranslation == xSizeBeforeTranslation // This is false, though under what circumstances I don't know
我需要找出矩形的大小在函数出来后是否发生了变化。最简单的解决方案,即检测上/下或左/右转换是否相等,在我的情况下不起作用,因为我的函数没有真正执行转换。它所做的是计算一个矩形的布局和另一个矩形的布局,然后设置边值。有许多if语句是基于rect的大小调整规则及其与其他语句的关系的,所以我很确定没有办法做到这一点


我可以设置一个很小的阈值,这样,如果之前的大小在之后大小的公差范围内,则可以将其视为相同的大小,但这意味着我的程序不会对非常小的更改做出反应,这一点总结起来可能很重要。

欢迎来到浮点世界。你没有说你使用的是单精度还是双精度,所以我假设是双精度

粗略地说,你有三个选择

  • 使用绝对公差,例如比最大预期值小1e6的系数。如果数据的范围非常大,则绝对公差不好
  • 使用相对公差,例如1e3倍于比较的两个值的abs值。相对公差在处理大范围时很好,但如果值非常小,应将其视为零,则相对公差就很差
  • 以上两项
  • 您还可以访问任意精度计算,这也可能 这是一个解决办法

    无论您选择什么,都必须使其适应您的数据范围。
    然后不要盲目地将公差应用于每种比较。

    下面的代码显示了如何精确比较浮点数的差异

    #include <math.h>
    
    
    /*  Define a structure to hold a pair of numbers we use to represent a sum
        exactly.
    */
    typedef struct { double s, t; } SumPair;
    
    
    /*  If floating-point arithmetic with subnormals and round-to-nearest is used,
        Fast2Sum produces the exact sum of a and b; it produces s and t such that
        s+t (calculated with real-number arithmetic, not floating-point arithmetic)
        equals a+b and s = RN(a+b) (the sum of a and b computed with floating-point
        arithmetic using round-to-nearest).
    
        Jean-Michel Muller et al, Handbook of Floating-Point Arithmetic (Birkhäuser
        Boston, 2010), pages 125 to 129.
    */
    static SumPair Fast2Sum(double a, double b)
    {
        //  If needed, swap a and b to ensure a has larger or equal magnitude.
        if (fabs(b) > fabs(a))
        {
            double t = a;
            b = a;
            a = t;
        }
    
        double s = a+b; //  Find the representable value nearest the sum.
        double z = s-a; //  s-a is exact by Sterbenz' Lemma.
        double t = b-z; //  Find the difference between s and the exact sum.
        return (SumPair) {s, t};
    }
    
    
    /*  Return true if and only if the distance from l0 to r0 equals the distance
        from l1 to r1, with real-number mathematics regardless of floating-point
        errors.
    */
    static _Bool DistancesAreEqual(double l0, double r0, double l1, double r1)
    {
        //  Find the exact difference of r0 and l0.
        SumPair d0 = Fast2Sum(r0, -l0);
    
        //  Find the exact difference of r1 and l1.
        SumPair d1 = Fast2Sum(r1, -l1);
    
        //  Return true if and only if the two differences are equal.
        return d0.s == d1.s && d0.t == d1.t;
    }
    
    #包括
    /*定义一个结构来保存我们用来表示和的一对数字
    确切地
    */
    typedef结构{double s,t;}SumPair;
    /*如果使用了低于正常值且四舍五入到最近值的浮点算法,
    Fast2Sum产生a和b的精确和;它产生的s和t使得
    s+t(使用实数算法计算,而不是浮点算法)
    等于a+b和s=RN(a+b)(用浮点计算的a和b之和)
    使用四舍五入到最近值的算术运算)。
    Jean-Michel Muller等人,《浮点运算手册》(Birkhäuser)
    波士顿,2010),第125至129页。
    */
    静态SUM2SUM(双a、双b)
    {
    //如果需要,交换a和b以确保a具有更大或相等的幅值。
    if(晶圆厂(b)>晶圆厂(a))
    {
    双t=a;
    b=a;
    a=t;
    }
    double s=a+b;//查找最接近总和的可表示值。
    double z=s-a;//s-a由Sterbenz引理精确。
    double t=b-z;//求s和精确和之间的差。
    返回(SumPair){s,t};
    }
    /*当且仅当从l0到r0的距离等于距离时,返回true
    从l1到r1,使用实数数学,不考虑浮点
    错误。
    */
    静态布尔距离相等(双l0、双r0、双l1、双r1)
    {
    //找出r0和l0的精确差。
    SumPair d0=Fast2Sum(r0,-l0);
    //找出r1和l1的确切差异。
    sumpaird d1=Fast2Sum(r1,-l1);
    //当且仅当两个差值相等时返回true。
    返回d0.s==d1.s&&d0.t==d1.t;
    }
    
    假设可以确定坐标之间的差值是否相等(精确地使用实数运算,而不仅仅是浮点运算)。那是你想要的测试吗?如果您的例程试图计算具有相等差异的坐标(如果使用实数算术计算,但由于浮点算术而具有不等差异),则会生成false。例如,使用三位小数作为示例,假设理想的左右坐标在一个正方形中为1.005和8.015,在另一个正方形中为2.015和9.025。两对都相差7.01。但是,使用常用的四舍五入到最接近的偶数方法计算的三位十进制浮点结果,一对为1.00和8.02,另一对为2.02和9.02,差值为7.02和7.00。精确的测试将产生错误的结果。这就是你想要的吗?