Checking if a double (or float) is NaN in C++

后端 未结 21 1902
北恋
北恋 2020-11-22 05:10

Is there an isnan() function?

PS.: I\'m in MinGW (if that makes a difference).

I had this solved by using isnan() from , which doe

21条回答
  •  失恋的感觉
    2020-11-22 05:50

    On x86-64 you can have extremely fast methods for checking for NaN and infinity, which work regardless of -ffast-math compiler option. (f != f, std::isnan, std::isinf always yield false with -ffast-math).


    Testing for NaN, infinity and finite numbers can easily be done by checking for maximum exponent. infinity is maximum exponent with zero mantissa, NaN is maximum exponent and non-zero mantissa. The exponent is stored in the next bits after the topmost sign bit, so that we can just left shift to get rid of the sign bit and make the exponent the topmost bits, no masking (operator&) is necessary:

    static inline uint64_t load_ieee754_rep(double a) {
        uint64_t r;
        static_assert(sizeof r == sizeof a, "Unexpected sizes.");
        std::memcpy(&r, &a, sizeof a); // Generates movq instruction.
        return r;
    }
    
    static inline uint32_t load_ieee754_rep(float a) {
        uint32_t r;
        static_assert(sizeof r == sizeof a, "Unexpected sizes.");
        std::memcpy(&r, &a, sizeof a); // Generates movd instruction.
        return r;
    }
    
    constexpr uint64_t inf_double_shl1 = UINT64_C(0xffe0000000000000);
    constexpr uint32_t inf_float_shl1 = UINT32_C(0xff000000);
    
    // The shift left removes the sign bit. The exponent moves into the topmost bits,
    // so that plain unsigned comparison is enough.
    static inline bool isnan2(double a)    { return load_ieee754_rep(a) << 1  > inf_double_shl1; }
    static inline bool isinf2(double a)    { return load_ieee754_rep(a) << 1 == inf_double_shl1; }
    static inline bool isfinite2(double a) { return load_ieee754_rep(a) << 1  < inf_double_shl1; }
    static inline bool isnan2(float a)     { return load_ieee754_rep(a) << 1  > inf_float_shl1; }
    static inline bool isinf2(float a)     { return load_ieee754_rep(a) << 1 == inf_float_shl1; }
    static inline bool isfinite2(float a)  { return load_ieee754_rep(a) << 1  < inf_float_shl1; }
    

    The std versions of isinf and isfinite load 2 double/float constants from .data segment and in the worst case scenario they can cause 2 data cache misses. The above versions do not load any data, inf_double_shl1 and inf_float_shl1 constants get encoded as immediate operands into the assembly instructions.


    Faster isnan2 is just 2 assembly instructions:

    bool isnan2(double a) {
        bool r;
        asm(".intel_syntax noprefix"
            "\n\t ucomisd %1, %1"
            "\n\t setp %b0"
            "\n\t .att_syntax prefix"
            : "=g" (r)
            : "x" (a)
            : "cc"
            );
        return r;
    }
    

    Uses the fact that ucomisd instruction sets parity flag if any argument is NaN. This is how std::isnan works when no -ffast-math options is specified.

提交回复
热议问题