如何编写可移植的constexpr std::copysign()?

How to write a portable constexpr std::copysign()?

特别是,它必须像 std::copysign 那样处理 NaN。同样,我需要一个 constexpr std::signbit.

constexpr double copysign(double mag, double sgn)
{
    // how?
}

constexpr bool signbit(double arg)
{
    // how?
}

// produce the two types of NaNs
constexpr double nan_pos = copysign(std::numeric_limits<double>::quiet_NaN(), +1);
constexpr double nan_neg = copysign(std::numeric_limits<double>::quiet_NaN(), -1);

// must pass the checks
static_assert(signbit(nan_pos) == false);
static_assert(signbit(nan_neg) == true);

背后的故事是我在编译时需要两种类型的 NaN,以及区分它们的方法。我能想到的最直接的方法是操纵 NaN 的符号位。它确实在 运行 时间工作;现在我只想把一些计算移到编译时,这是最后一个障碍。

注意:目前,我依赖 GCC,因为它有这些函数的内置版本,而且它们确实是 constexpr,这很好。但我希望我的代码库能够在 Clang 和其他编译器上编译。

如果可以使用std::bit_cast,则可以操作转换为整数类型的浮点类型。可移植性仅限于 double 的表示,但如果您可以假定 IEEE 754 双精度二进制浮点格式,则转换为 uint64_t 并使用符号位应该可以。

__builtin... 的使用并不是真正的可移植,但在作为目标提到的编译器中有效。 __builtin_copysign 是 conexpr,但 __builtin_signbit 显然不在 clang 上,所以 signbit__builtin_copysign:

#include <limits>

constexpr double copysign(double mag, double sgn)
{
    return __builtin_copysign(mag, sgn);
}

constexpr bool signbit(double arg)
{
    return __builtin_copysign(1, arg) <  0;
}

// produce the two types of NaNs
constexpr double nan_pos = copysign(std::numeric_limits<double>::quiet_NaN(), +1);
constexpr double nan_neg = copysign(std::numeric_limits<double>::quiet_NaN(), -1);

// must pass the checks
static_assert(signbit(nan_pos) == false);
static_assert(signbit(nan_neg) == true);

int main() {}

https://godbolt.org/z/8Wafaj4a4

P0533:现在接受 <cmath><cstdlib> 的 constexpr。

从 C++23 开始,您可以只使用 std::copysignstd::syncbit