两个浮点数值 (IEEE 754 binary64) 可以被作为整数进行比较吗?例如:
long long a = * (long long *) ptr_to_double1,
b = * (long long *) ptr_to_double2;
if (a < b) {...}
假设
long long
和double
的大小相同。两个浮点数值 (IEEE 754 binary64) 可以被作为整数进行比较吗?例如:
long long a = * (long long *) ptr_to_double1,
b = * (long long *) ptr_to_double2;
if (a < b) {...}
long long
和double
的大小相同。是的 - 在某些受限制的情况下,将两个浮点数的位模式进行比较,就像它们是整数一样(也称为“类型转换”),会产生有意义的结果...
与浮点数比较相同的情况包括:
与浮点数比较相反的情况包括:
不可与浮点数比较的情况包括:
负浮点数处理方式与用于整数的二进制补码算术非常不同。对负浮点数的表示进行整数+1操作将使其变成一个更大的负数。
通过一些简单操作,您可以使用整数运算比较正数和负数浮点数(这对于某些优化很有用):
int32 float_to_comparable_integer(float f) {
uint32 bits = std::bit_cast<uint32>(f);
const uint32 sign_bit = bits & 0x80000000ul;
// Modern compilers turn this IF-statement into a conditional move (CMOV) on x86,
// which is much faster than a branch that the cpu might mis-predict.
if (sign_bit) {
bits = 0x7FFFFFF - bits;
}
return static_cast<int32>(bits);
}
需要注意的是,这种方法不适用于NaN值,因为它们始终从比较中返回false,并且有多个有效的二进制表示方式:
IEEE-754位格式:http://www.puntoflotante.net/FLOATING-POINT-FORMAT-IEEE-754.htm
更多关于类型转换的信息:https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/
const uint32 bits = *reinterpret_cast<uint32*>(&f); .... bits = 0x7FFFFFF - bits;
看起来是错误的,因为 bits
是 const
类型的,不能在后面被赋值。建议去掉 const
。 - chux - Reinstate Monicafloat_to_comparable_integer(+0.0f)
和float_to_comparable_integer(-0.0f)
,但结果并不相等。可能需要重新措辞来解决这个问题。 - chux - Reinstate Monicauint32, int32
不是标准类型。也许应该使用 uint32_t, int32_t
? (自 C++11 起) - chux - Reinstate Monica*reinterpret_cast<uint32*>(&f)
存在严格别名未定义行为。请使用memcpy,或C++20的std::bit_cast<uint32_t>(f)
。或者,由于这是一个C问题而不是C ++,所以联合体也是明确定义的。 - Peter Cordesif (a < b)
进行比较。
IEEE 754 binary64
双精度的排序顺序与整数不同(除非你在一个罕见的符号-幅值机器上)。想想正数和负数。0.0
和 -0.0
这样的值,它们具有相同的值但不同的位模式。x > 0
且都不是“不是数字”,则 OP 的想法可以实现,而字节顺序、别名和对齐等问题也不是问题。if() ...
条件也可以使用——请参见下面的内容double
使用一种编码方式,其中同一值有多个表示。这与“整数”比较不同。
测试代码:需要使用二进制补码,double
和整数的字节序相同,不考虑 NaN。
int compare(double a, double b) {
union {
double d;
int64_t i64;
uint64_t u64;
} ua, ub;
ua.d = a;
ub.d = b;
// Cope with -0.0 right away
if (ua.u64 == 0x8000000000000000) ua.u64 = 0;
if (ub.u64 == 0x8000000000000000) ub.u64 = 0;
// Signs differ?
if ((ua.i64 < 0) != (ub.i64 < 0)) {
return ua.i64 >= 0 ? 1 : -1;
}
// If numbers are negative
if (ua.i64 < 0) {
ua.u64 = -ua.u64;
ub.u64 = -ub.u64;
}
return (ua.u64 > ub.u64) - (ua.u64 < ub.u64);
}
void testcmp(double a, double b) {
int t1 = (a > b) - (a < b);
int t2 = compare(a, b);
if (t1 != t2) {
printf("%le %le %d %d\n", a, b, t1, t2);
}
}
#include <float.h>
void testcmps() {
// Various interesting `double`
static const double a[] = {
-1.0 / 0.0, -DBL_MAX, -1.0, -DBL_MIN, -0.0,
+0.0, DBL_MIN, 1.0, DBL_MAX, +1.0 / 0.0 };
int n = sizeof a / sizeof a[0];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
testcmp(a[i], a[j]);
}
}
puts("!");
}
如果您严格地将浮点数的位值转换为相应大小的有符号整数(就像您所做的那样),那么结果的有符号整数比较将与原始浮点值的比较相同,不包括NaN值。换句话说,这种比较对于所有可表示的有限和无限数值都是合法的。
换句话说,对于双精度(64位),如果以下测试通过,则此比较将有效:
long long exponentMask = 0x7ff0000000000000;
long long mantissaMask = 0x000fffffffffffff;
bool isNumber = ((x & exponentMask) != exponentMask) // Not exp 0x7ff
|| ((x & mantissaMask) == 0); // Infinities
对于每个操作数 x。
当然,如果您可以预先确定浮点值,则快速的 isNaN() 测试会更加清晰。您需要进行性能分析以了解其影响。
-fno-strict-aliasing
选项),编译器就会(应该会)发出警告,指出“解引用类型转换后的指针将违反严格别名规则”。 - David C. Rankin你的问题有两个部分:
两个浮点数可以进行比较吗? 答案是肯定的。比较浮点数的大小是完全有效的。通常,由于截断问题,您希望避免等于比较,请参见此处,但是
if (a < b)
将正常工作。
两个浮点数可以作为整数进行比较吗? 答案也是肯定的,但这需要进行强制转换。这个问题应该会帮助您回答:在C++中从long long转换为int,以及另一种方式