如何检查 C++11 constexpr 中的双精度位模式是否为 0x0?
我想检查给定的双精度/浮点变量是否具有实际的位模式 0x0。不要问为什么,它用在 Qt 中的一个函数 (qIsNull()
) 中,我希望将其用作 constexpr
。
原始代码使用了联合:
union { double d; int64_t i; } u;
u.d = d;
return u.i == 0;
当然,这不能作为 constexpr
工作。
下一个尝试是使用reinterpret_cast
:
return *reinterpret_cast<int64_t*>(&d) == 0;
但是,虽然它在 GCC 4.7 中作为 constexpr
工作,但在 Clang 3.1 中却失败了(理所当然,是指针操作的 b/c)。
最终的想法是采用 Alexandrescuesque 的方式来做到这一点:
template <typename T1, typename T2>
union Converter {
T1 t1;
T2 t2;
explicit constexpr Converter( T1 t1 ) : t1(t1) {}
constexpr operator T2() const { return t2; }
};
// in qIsNull():
return Converter<double,int64_t>(d);
但这对于 Clang 来说也不够聪明:
note: read of member 't2' of union with active member 't1' is not allowed in a constant expression
constexpr operator T2() const { return t2; }
^
还有其他人有好主意吗?
I want to check that a given double/float variable has the actual bit pattern 0x0. Don't ask why, it's used in a function in Qt (qIsNull()
) that I'd like to be constexpr
.
The original code used a union:
union { double d; int64_t i; } u;
u.d = d;
return u.i == 0;
This doesn't work as a constexpr
of course.
The next try was with reinterpret_cast
:
return *reinterpret_cast<int64_t*>(&d) == 0;
But while that works as a constexpr
in GCC 4.7, it fails (rightfully, b/c of pointer manipulation) in Clang 3.1.
The final idea was to go Alexandrescuesque and do this:
template <typename T1, typename T2>
union Converter {
T1 t1;
T2 t2;
explicit constexpr Converter( T1 t1 ) : t1(t1) {}
constexpr operator T2() const { return t2; }
};
// in qIsNull():
return Converter<double,int64_t>(d);
But that's not clever enough for Clang, either:
note: read of member 't2' of union with active member 't1' is not allowed in a constant expression
constexpr operator T2() const { return t2; }
^
Does anyone else have a good idea?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
但如果它是 constexpr 那么它不会检查任何变量,而是检查值< /em> 该变量是静态确定保存的。这就是为什么你不应该使用指针和联合技巧,“正式”没有任何内存可以指向。
如果您可以说服您的实现执行非陷阱 IEEE 除以零,那么您可以执行以下操作:
只有
+/-0
等于 0。1/-0< /code> 是
-Inf
,不大于 0。1/+0
是+Inf
,不大于 0。但我不知道如何使非陷阱算术发生。But if it's
constexpr
then it's not checking any variable, it's checking the value that this variable is statically determined to hold. That's why you aren't supposed to pull pointer and union tricks, "officially" there isn't any memory to point at.If you can persuade your implementation to do non-trapping IEEE division-by-zero, then you could do something like:
Only
+/-0
are equal to 0.1/-0
is-Inf
, which isn't greater than 0.1/+0
is+Inf
, which is. But I don't know how to make that non-trapping arithmetic happen.似乎 clang++ 3.0 和 g++ 4.7(但不是 4.6)都将 std::signbit 视为 constexpr。
It seems both clang++ 3.0 and g++ 4.7 (but not 4.6) treats
std::signbit
asconstexpr
.无法从常量表达式中查看 double 的基础位模式。 C++11 标准中存在一个缺陷,允许通过
void*
进行强制转换进行此类检查,但已由 C++ 核心问题 1312。作为“证明”,clang 的 constexpr 实现(被认为是完整的)没有用于提取常量 double 值表示的机制(除了通过非标准向量)操作,即使这样,目前也无法检查结果)。
正如其他人所建议的,如果您知道您的目标平台使用 IEEE-754 浮点,则
0x0
对应于正零值。我相信检测这一点的唯一方法(在 clang 和 g++ 中的常量表达式中都有效)是使用__builtin_copysign
:It is not possible to look at the underlying bit pattern of a
double
from within a constant expression. There was a defect in the C++11 standard which allowed such inspection by casting via avoid*
, but that was addressed by C++ core issue 1312.As "proof", clang's
constexpr
implementation (which is considered to be complete) has no mechanism for extracting the representation of a constantdouble
value (other than via non-standard vector operations, and even then there is currently no way to inspect the result).As others have suggested, if you know you will be targeting a platform which uses IEEE-754 floating point,
0x0
corresponds to the value positive zero. I believe the only way to detect this, that works inside a constant expression in both clang and g++, is to use__builtin_copysign
:在 c++20 中,可以使用 constexpr bit_cast,因此可以像这样检查无符号零:
std::bit_cast(d) == 0
In c++20 one can use constexpr bit_cast, and so the check for the unsigned zero can be done like this:
std::bit_cast<uint64_t, double>(d) == 0