将浮点数与整数进行比较
Compare floating point numbers as integers
两个浮点值(IEEE 754 binary64)可以作为整数进行比较吗?例如
long long a = * (long long *) ptr_to_double1,
b = * (long long *) ptr_to_double2;
if (a < b) {...}
假设 long long
和 double
的大小相同。
您的问题分为两部分:
两个浮点数可以比较吗?这个问题的答案是可以。比较浮点数的大小是完全有效的。通常,由于截断问题,您希望避免相等比较,请参阅 here,但
if (a < b)
会很好用。
两个浮点数可以作为整数进行比较吗?这个答案也是可以的,但这需要强制转换。这个问题应该有助于回答这个问题:convert from long long to int and the other way back in c++
如果您严格地将浮点数的位值转换为其相应大小的有符号整数(正如您所做的那样),那么结果的有符号整数比较将与原始浮点数的比较相同-点值,不包括 NaN 值。换句话说,这种比较对于所有可表示的有限和无限数值都是合法的。
换句话说,对于双精度(64位),如果通过以下测试,则此比较有效:
long long exponentMask = 0x7ff0000000000000;
long long mantissaMask = 0x000fffffffffffff;
bool isNumber = ((x & exponentMask) != exponentMask) // Not exp 0x7ff
|| ((x & mantissaMask) == 0); // Infinities
对于每个操作数 x。
当然,如果您可以预先限定浮点值,那么快速的 isNaN() 测试会更加清晰。您必须分析才能了解性能影响。
没有。两个浮点值 (IEEE 754 binary64) 无法将 简单地 作为整数与 if (a < b)
.
进行比较
double
的值的顺序与整数的顺序不同(除非您使用的是罕见的符号量级机器)。思考正数与负数。
double
具有类似于 0.0
和 -0.0
的值,它们具有相同的值但不同的位模式。
double
有 "Not-a-number" 与它们的二进制等效整数表示不同。
如果 double
的值都是 x > 0
而不是 "Not-a-number",endian、aliasing 和 alignment 等都不是问题,OP 的想法就可以实现。
或者,更复杂的 if() ...
条件也行 - 见下文
[非 IEEE 754 binary64]
一些 double
使用一种编码,其中有相同值的多种表示形式。这与 "integer" 比较不同。
测试代码:需要 2 的补码,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);
}
感谢 @David C. Rankin 更正。
测试代码
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("!");
}
是 - 比较两个浮点数的位模式,就好像它们是整数(又名 "type-punning")在某些受限情况下会产生有意义的结果...
在以下情况下与浮点比较相同:
- 两个数都是正数、正零或正无穷大。
- 一个正数和一个负数,并且您使用的是有符号整数比较。
浮点比较的逆运算,当:
- 两个数都是负数、负零或负无穷大。
- 一个正数和一个负数,并且您使用的是无符号整数比较。
在以下情况下无法与浮点数比较相比:
- 任一数字都是 NaN 值之一 - 与 NaN 的浮点比较总是 returns 假,并且这根本无法在整数运算中建模,其中恰好以下其中一项始终为真: (A < B), (A == B), (B < A).
负浮点数有点古怪 b/c 它们的处理方式与用于整数的 2 的补码算法非常不同。在负浮点数的表示上做一个整数 +1 会使它成为一个更大的负数。
通过一点点操作,您可以使正浮点数和负浮点数都与整数运算相当(这对于某些优化可能会派上用场):
int32 float_to_comparable_integer(float f) {
const uint32 bits = *reinterpret_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 值起作用,NaN 值在比较中总是 return 错误,并且有多个有效位表示:
- 发信号 NaN(带符号位):0xFF800001 和 0xFFBFFFFF 之间的任何值。
- 信号 NaN(w/o 符号位):0x7F800001 和 0x7FBFFFFF 之间的任何值。
- Quiet NaN(带符号位):0xFFC00000 和 0xFFFFFFFF 之间的任何值。
- Quiet NaN(w/o 符号位):0x7FC00000 和 0x7FFFFFFF 之间的任何值。
IEEE-754 位格式:http://www.puntoflotante.net/FLOATING-POINT-FORMAT-IEEE-754.htm
关于打字双关的更多信息:https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/
两个浮点值(IEEE 754 binary64)可以作为整数进行比较吗?例如
long long a = * (long long *) ptr_to_double1,
b = * (long long *) ptr_to_double2;
if (a < b) {...}
假设 long long
和 double
的大小相同。
您的问题分为两部分:
两个浮点数可以比较吗?这个问题的答案是可以。比较浮点数的大小是完全有效的。通常,由于截断问题,您希望避免相等比较,请参阅 here,但
if (a < b)
会很好用。
两个浮点数可以作为整数进行比较吗?这个答案也是可以的,但这需要强制转换。这个问题应该有助于回答这个问题:convert from long long to int and the other way back in c++
如果您严格地将浮点数的位值转换为其相应大小的有符号整数(正如您所做的那样),那么结果的有符号整数比较将与原始浮点数的比较相同-点值,不包括 NaN 值。换句话说,这种比较对于所有可表示的有限和无限数值都是合法的。
换句话说,对于双精度(64位),如果通过以下测试,则此比较有效:
long long exponentMask = 0x7ff0000000000000;
long long mantissaMask = 0x000fffffffffffff;
bool isNumber = ((x & exponentMask) != exponentMask) // Not exp 0x7ff
|| ((x & mantissaMask) == 0); // Infinities
对于每个操作数 x。
当然,如果您可以预先限定浮点值,那么快速的 isNaN() 测试会更加清晰。您必须分析才能了解性能影响。
没有。两个浮点值 (IEEE 754 binary64) 无法将 简单地 作为整数与 if (a < b)
.
double
的值的顺序与整数的顺序不同(除非您使用的是罕见的符号量级机器)。思考正数与负数。
double
具有类似于 0.0
和 -0.0
的值,它们具有相同的值但不同的位模式。
double
有 "Not-a-number" 与它们的二进制等效整数表示不同。
如果 double
的值都是 x > 0
而不是 "Not-a-number",endian、aliasing 和 alignment 等都不是问题,OP 的想法就可以实现。
或者,更复杂的 if() ...
条件也行 - 见下文
[非 IEEE 754 binary64]
一些 double
使用一种编码,其中有相同值的多种表示形式。这与 "integer" 比较不同。
测试代码:需要 2 的补码,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);
}
感谢 @David C. Rankin 更正。
测试代码
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("!");
}
是 - 比较两个浮点数的位模式,就好像它们是整数(又名 "type-punning")在某些受限情况下会产生有意义的结果...
在以下情况下与浮点比较相同:
- 两个数都是正数、正零或正无穷大。
- 一个正数和一个负数,并且您使用的是有符号整数比较。
浮点比较的逆运算,当:
- 两个数都是负数、负零或负无穷大。
- 一个正数和一个负数,并且您使用的是无符号整数比较。
在以下情况下无法与浮点数比较相比:
- 任一数字都是 NaN 值之一 - 与 NaN 的浮点比较总是 returns 假,并且这根本无法在整数运算中建模,其中恰好以下其中一项始终为真: (A < B), (A == B), (B < A).
负浮点数有点古怪 b/c 它们的处理方式与用于整数的 2 的补码算法非常不同。在负浮点数的表示上做一个整数 +1 会使它成为一个更大的负数。
通过一点点操作,您可以使正浮点数和负浮点数都与整数运算相当(这对于某些优化可能会派上用场):
int32 float_to_comparable_integer(float f) {
const uint32 bits = *reinterpret_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 值起作用,NaN 值在比较中总是 return 错误,并且有多个有效位表示:
- 发信号 NaN(带符号位):0xFF800001 和 0xFFBFFFFF 之间的任何值。
- 信号 NaN(w/o 符号位):0x7F800001 和 0x7FBFFFFF 之间的任何值。
- Quiet NaN(带符号位):0xFFC00000 和 0xFFFFFFFF 之间的任何值。
- Quiet NaN(w/o 符号位):0x7FC00000 和 0x7FFFFFFF 之间的任何值。
IEEE-754 位格式:http://www.puntoflotante.net/FLOATING-POINT-FORMAT-IEEE-754.htm
关于打字双关的更多信息:https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/