检查C中的double(或float)是否为NaN

新手上路,请多包涵

有 isnan() 函数吗?

PS.:我在 MinGW (如果这有什么不同的话)。

我通过使用 <math.h> 中的 isnan() 解决了这个问题,这在 <cmath> 中不存在,我是 #include 首先。

原文由 hasen 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

根据 IEEE 标准,NaN 值具有奇数特性,即涉及它们的比较 总是 错误的。也就是说,对于浮点数 f, 只有 当 f 为 NaN 时, f != f 才会为真。

请注意,正如下面的一些评论所指出的,并非所有编译器在优化代码时都尊重这一点。

对于任何声称使用 IEEE 浮点的编译器,这个技巧 应该可以 工作。但我不能保证它 在实践中发挥作用。如果有疑问,请检查您的编译器。

原文由 jalf 发布,翻译遵循 CC BY-SA 3.0 许可协议

在 x86-64 上,您可以使用非常快速的方法来检查 NaN 和无穷大,无论 -ffast-math 编译器选项如何,这些方法都可以工作。 ( f != f , std::isnan , std::isinf always yield false with -ffast-math ).


通过检查最大指数可以很容易地测试 NaN、无穷大和有限数。无穷大是尾数为零的最大指数,NaN 是最大指数和非零尾数。指数存储在最高符号位之后的下一位中,因此我们只需左移即可摆脱符号位并使指数成为最高位,无需屏蔽( operator& ):

 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它们可能导致 2 次数据缓存未命中。上述版本不加载任何数据, inf_double_shl1inf_float_shl1 常量被编码为立即操作数到汇编指令中。


Faster isnan2 只是两条汇编指令:

 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;
}

如果任何参数为 NaN,则使用 ucomisd 指令设置奇偶校验标志这一事实。当没有指定 -ffast-math 选项时,这就是 std::isnan 的工作方式。

原文由 Maxim Egorushkin 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题