如何检查std::frexp的结果是否在当前编译器下指定?

NO_*_*AME 1 c++ floating-point unspecified-behavior

std::frexp是一个从浮点变量中删除指数并将其放入exp类型变量中的函数intint无论指数真正需要多少位,它都会根据链接页面使用:

如果要存储的值*exp超出 的范围int,则行为未指定。

那么,如何检查“是否超出”*exp的范围呢?int

我正在考虑static_assert在我的代码中添加 a 来比较FLT_MIN_EXP&FLT_MAX_EXPINT_MIN&INT_MAX。但是,我担心会犯一个相差一的错误,因为我不完全理解这些常量的描述。

FLT_MIN_EXP

最小负整数,使得 FLT_RADIX 乘以比该整数小一倍的幂是归一化的float...

FLT_MAX_EXP

最大正整数,使得 FLT_RADIX 乘以该整数减一的幂是可表示的有限float...

(我已经static_assert(FLT_RADIX == 2);在代码中包含了,所以在我的例子中基数等于 10 不是问题。)

Joh*_*eau 5

断言1:此操作通常运行良好,您无需担心任何事情。

断言 2:为了安心,如果每个系统都无法正常工作,我们希望了解它。当我们找到其中一个系统时,我们将重新审视解决方案。

断言3:如果操作是可逆的,则返回的最大和最小指数std::frexp可以用系统的类型表示。int

std::frexp通过它的补码来反转std::ldexp

请注意,如果int无法保存浮点的指数,则结果不是未定义的(它只是未指定),因此我们可以依赖于格式良好的测试。

编写一个单元测试来测试这是否可逆

TEST_CASE("The largest exponent returned by std::frexp can be represented by int") {
    long double largest_ld = std::numeric_limits<long double>::max();

    int exponent;
    long double fraction = std::frexp(largest_ld, &exponent);

    errno = 0;
    REQUIRE(std::ldexp(fraction, exponent) == largest_ld);
    REQUIRE(errno == 0); // just in case?
}

TEST_CASE("The smallest exponent returned by std::frexp can be represented by int") {
    long double smallest_ld = std::numeric_limits<long double>::min();

    int exponent;
    long double fraction = std::frexp(smallest_ld, &exponent);

    errno = 0;
    REQUIRE(std::ldexp(fraction, exponent) == smallest_ld);
    REQUIRE(errno == 0); // just in case?
}
Run Code Online (Sandbox Code Playgroud)

你能在编译时检查方面做得更好吗?

可能吧,但我无法理解您遇到问题的语言的同一部分,因此我正在通过编译时间检查来优化开发人员的速度。

作为一阶近似,我可以建议:

static_assert(sizeof(int) >= 32);
static_assert(std::numeric_limits<long double>::is_eec559);
Run Code Online (Sandbox Code Playgroud)

当然,当您到达一个不正确的平台时,请重新访问。