使用具有整数表示和ULP的别名来正确地比较双精度

时间:2016-12-18 22:17:57

标签: c++ double floating-point-comparison

我试图避免使用epsilon比较来比较浮点类型。我能想出的最佳解决方案是使用ULP中的差异(最后一个单位),尽管this article使用整数表示有更好的解决方案(///表示我自己的评论):

/* See
https://randomascii.wordpress.com/2012/01/11/tricks-with-the-floating-point-format/
for the potential portability problems with the union and bit-fields below.
*/

#include <stdint.h> // For int32_t, etc.

union Float_t
{
    Float_t(float num = 0.0f) : f(num) {}
    // Portable extraction of components.
    bool Negative() const { return i < 0; }
    int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
    int32_t RawExponent() const { return (i >> 23) & 0xFF; }

    int32_t i; /// Perhaps overflow when using doubles?
    float f;

    #ifdef _DEBUG
    struct
    {   // Bitfields for exploration. Do not use in production code.
        uint32_t mantissa : 23; /// 52 for double?
        uint32_t exponent : 8; /// 11 for double?
        uint32_t sign : 1;
    } parts;
    #endif
};

bool AlmostEqualUlps(float A, float B, int maxUlpsDiff)
{
    Float_t uA(A);
    Float_t uB(B);

    // Different signs means they do not match.
    if (uA.Negative() != uB.Negative())
    {
        // Check for equality to make sure +0==-0
        if (A == B)
            return true;
        return false;
    }

    // Find the difference in ULPs.
    int ulpsDiff = abs(uA.i - uB.i);
    if (ulpsDiff <= maxUlpsDiff)
        return true;

    return false;
}

但是,我似乎无法以支持双打的方式重新格式化代码。我甚至读到了解释,found here

有谁知道解决这个问题的最佳方法是什么?

在任何人决定将此标记为重复之前:不要,因为唯一类似的问题是针对javascript,而C ++的答案是:

bool IsAlmostEqual(double A, double B)
{
    //http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
    long long aInt = reinterpret_cast<long long&>(A);
    if (aInt < 0) aInt = -9223372036854775808LL - aInt;
    long long bInt = reinterpret_cast<long long&>(B);
    if (bInt < 0) bInt = -9223372036854775808LL - bInt;
    return (std::abs(aInt - bInt) <= 10000);
}

哪个不使用ULP,但是某种绑定,我不确定-9223372036854775808LL - aInt到底是什么(可能是int64溢出的地方)。

1 个答案:

答案 0 :(得分:1)

我认为你的代码根本不起作用。下面只是一个例子,它会弄错。 (这不是答案,但解释时间太长,不适合评论)

function validate() {
    var errorFlag = true;
    var userinput = $('#username').val();
    var mobilenumber=$('#mobnum').val();
    // var emailid=$('#mail').val();
    // var password=$('#pwd').val();
    var address1=$('#addr1').val();
    var address2=$('#addr2').val();

    var characterReg = /^([a-zA-Z]{2,30})$/;
    var numericReg=/^\d*[0-9](|.\d*[0-9]|,\d*[0-9])?$/;
    // var emailReg=/^([\w-\.]+@([\w-]+\.)+[\w-]{2,4})?$/;
    // var passReg=/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$/;
    var addrReg = /^\s*[a-zA-Z0-9,\s]+\s*$/;

    var inputData = [{
        id : "username",
        regex : characterReg
    }, {
        id : "mobnum",
        regex : numericReg
    }, 
    // {
    //  id:"mail"
    //  regex: emailReg
    // },{
    //  id:"pwd"
    //  regex:passReg
    // },
    {
        id : "addr1",
        regex : addrReg
    }, {
        id : "addr2",
        regex : characterReg
    }];

    for(var index=0; index < inputData.length; index++) {

        var data = inputData[index];
        var regex = data.regex;
        if(!regex.test($('#' + data.id).val())) {
            errorFlag = false;
            $('#' + data.id).addClass('alert');
        } else {
            $('#' + data.id).removeClass('alert');
        }
    }
    return errorFlag;
}

但是,int main() { Float_t x; Float_t y; x.i = 0x7F800000; y.i = 0x7F7FFFFF; AlmostEqualUlps(x.f, y.f, 1); // return true } 实际上是无穷大,x.fy.f。任何定义的差异都是无穷大。除非这是你的预期行为,否则认为有限数和无穷大几乎相等。您对ULP的实施完全不合适。事实上,对于上述两个数字,ULP甚至没有明确定义。

另一个例子是0x7F800000(或任何接近此值的数字,具体取决于您的FLT_MAX)和0x7F800001(NaN)。与上面的例子不同,甚至没有一个论据要求他们应该考虑&#34;几乎相等&#34;。

另一方面,你拒绝任何具有不同标志的对不够接近,而事实上maxULP-FLT_MIN之间有很多次正规,其中一个可能被视为&#34几乎相等&#34;。例如,0x80000001和0x1相差2ULP,但如果在函数中将FLT_MIN设置为2,则返回false。

如果您可以排除非规范,不足,NaN,然后​​处理双倍,您只需将maxULP替换为uint32_t,如其他人所述&#39;评价。