将浮点数与整数进行比较

3 c floating-point comparison

两个浮点值(IEEE 754 binary64)可以作为整数进行比较吗?例如。

long long a = * (long long *) ptr_to_double1,
          b = * (long long *) ptr_to_double2;
if (a < b) {...}
Run Code Online (Sandbox Code Playgroud)

假设的大小long longdouble是相同的。

Dav*_*son 10

是 - 比较两个浮点数的位模式,就好像它们是整数一样(又名“类型双关”)在某些受限情况下会产生有意义的结果......

在以下情况下与浮点比较相同:

  • 这两个数字都是正数、正零或正无穷大。
  • 一个正数和一个负数,并且您正在使用有符号整数比较。

浮点比较的逆运算:

  • 这两个数字都是负数、负零或负无穷大。
  • 一个正数和一个负数,并且您正在使用无符号整数比较。

在以下情况下无法与浮点比较进行比较:

  • 任何一个数字都是 NaN 值之一 - 与 NaN 的浮点比较总是返回 false,并且这根本无法在整数运算中建模,其中以下之一始终为真:(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);
}
Run Code Online (Sandbox Code Playgroud)

同样,这确实不是工作NaN值,它始终返回从比较假,并且有多个有效位表示:

  • 信令 NaN(带符号位):介于 0xFF800001 和 0xFFBFFFFF 之间的任何值。
  • 信令 NaN(无符号位):介于 0x7F800001 和 0x7FBFFFFFF 之间的任何值。
  • Quiet NaN(带符号位):0xFFC00000 和 0xFFFFFFFF 之间的任何值。
  • Quiet NaN(无符号位):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/

  • `*reinterpret_cast&lt;uint32*&gt;(&amp;f)` 具有严格别名未定义的行为。使用 memcpy 或 C++20 `std::bit_cast&lt;uint32_t&gt;(f)`。或者因为这是一个 C 问题而不是 C++,所以联合也是明确定义的。 (2认同)

chu*_*ica 4

不能。两个浮点值(IEEE 754 二进制 64)不能简单地作为整数进行比较if (a < b)

IEEE 754 二进制64

的值的顺序double与整数的顺序不同(除非您使用的是罕见的符号数值机器)。思考正数和负数。

double具有类似于0.0和 的值-0.0,它们具有相同的值但不同的位模式。

double具有“非数字”,不能像其二进制等效整数表示那样进行比较。

如果两个double值都是x > 0“Not-a-number”,并且不是“Not-a-number”,则字节序、别名和对齐等都不是问题,那么OP的想法就会起作用。

if() ...或者,可以使用更复杂的条件 - 见下文

[非 IEEE 754 二进制 64]

有些double使用相同值有多种表示形式的编码。这与“整数”比较不同。


测试代码:需要 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);
}
Run Code Online (Sandbox Code Playgroud)

感谢@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("!");
}
Run Code Online (Sandbox Code Playgroud)