将浮点数作为整数进行比较

时间:2015-11-12 18:37:42

标签: c floating-point comparison

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

long long a = * (long long *) ptr_to_double1,
          b = * (long long *) ptr_to_double2;
if (a < b) {...}

假设long longdouble的大小相同。

4 个答案:

答案 0 :(得分:4)

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

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

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

在以下情况下进行浮点比较的反转:

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

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

  • 任何一个数字都是NaN值之一 - 与NaN的浮点比较总是返回false,并且这根本不能在整数运算中建模,其中恰好以下之一总是为真:(A&lt; B&lt; B&lt; B&lt; B&lt; B&lt; B&nbsp; ),(A == B),(B

负浮点数有点时髦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值总是从比较中返回false,并且有多个有效的位表示:

  • 信令NaN(带符号位):0xFF800001和0xFFBFFFFF之间的任何内容。
  • 信令NaN(无符号位):0x7F800001和0x7FBFFFFF之间的任何内容。
  • 安静的NaN(带符号位):0xFFC00000和0xFFFFFFFF之间的任何内容。
  • 安静的NaN(无符号位):0x7FC00000和0x7FFFFFFF之间的任何内容。

IEEE-754位格式:http://www.puntoflotante.net/FLOATING-POINT-FORMAT-IEEE-754.htm

有关Type-Punning的更多信息:https://randomascii.wordpress.com/2012/01/23/stupid-float-tricks-2/

答案 1 :(得分:2)

没有。两个浮点值(IEEE 754 binary64)无法将简单if (a < b)的整数进行比较。

IEEE 754 binary64

double的值的顺序与整数的顺序不同(除非您使用的是罕见的符号级机器)。想想积极与消极的数字。

double的值为0.0-0.0,它们具有相同的值,但位模式不同。

double有“Not-a-number”,它们不像二进制等效整数表示那样进行比较。

如果double值都是x > 0而不是“非数字”,则字节序,别名和对齐等不是问题,OP的想法会起作用。

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

[非IEEE 754 binary64]

有些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);
}

感谢@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("!");
}

答案 2 :(得分:0)

如果严格地将浮点数的位值转换为相应大小的有符号整数(就像你所做的那样),那么结果的有符号整数比较将与原始浮点值的比较相同,不包括NaN值。换句话说,这种比较对于所有可表示的有限和无限数值都是合法的。

换句话说,对于双精度(64位),如果以下测试通过,则此比较将有效:

long long exponentMask = 0x7ff0000000000000;
long long mantissaMask = 0x000fffffffffffff;

bool isNumber =  ((x & exponentMask) != exponentMask)  // Not exp 0x7ff
              || ((x & mantissaMask) == 0);            // Infinities

表示每个操作数x。

当然,如果您可以对浮点值进行预先鉴定,那么快速的isNaN()测试会更加清晰。您必须剖析以了解性能影响。

答案 3 :(得分:-2)

您的问题分为两部分:

  1. 可以比较两个浮点数吗?答案是肯定的。比较浮点数的大小是完全有效的。通常,您希望避免因截断问题而进行的等比较,请参阅here,但

    if (a < b)
    

    工作得很好。

  2. 可以将两个浮点数作为整数进行比较吗?这个答案也是肯定的,但这需要强制转换。这个问题应该有助于回答:convert from long long to int and the other way back in c++