以最有效的方式使用 64 位浮点类型时可以在一个范围内表示多少个值[重复]

问题描述 投票:0回答:2

给定 64 位浮点类型 (ieee-754) 和闭范围 [R0,R1] 假设 R0 和 R1 都在可表示范围内并且不是 NaN+/-inf 等.

如何计算范围内可表示值的数量,这比简单地循环范围更好?

std::uint64_t count = 0;
while (r0 <= r1) 
{
    count++;
    r0 = std::nextafterf(r0,std::numeric_limits<double>infinity());
}
floating-point ieee-754
2个回答
2
投票

使用浮点类型时,一个范围内可以表示多少个值

作弊假设

double
匹配常见的 IEEE 64 位布局,字节序和大小类似于
(un)signed long long
(u)int64_t

映射(通过

union
)从 -INF 到 +INF 的每个连续
double
,然后每个递增的整数值我们得到以下结果。 请注意,+0 和 -0 被认为是相同的。 如果您希望 +0 和 -0 对此计数不同,请将
if (y.ll < 0) { y.ll = LLONG_MIN - y.ll; }
更改为
if (y.ll < 0) { y.ll = LLONG_MIN - y.ll - 1; }
(或类似的内容)。

下面还在

ULP_diff(x,x)
上返回 0,因此您可能需要添加 1 以捕获两个端点。

#include <assert.h>
#include <limits.h>

unsigned long long ULP_diff(double x, double y) {
  assert(sizeof(double) == sizeof(long long));
  union {
    double d;
    long long ll;
    unsigned long long ull;
  } ux, uy;
  ux.d = x;
  uy.d = y;
  if (ux.ll < 0) {
    ux.ll = LLONG_MIN - ux.ll;
  }
  if (uy.ll < 0) {
    uy.ll = LLONG_MIN - uy.ll;
  }
  unsigned long long diff;
  if (ux.ll >= uy.ll) {
    diff = ux.ull - uy.ull;
  } else {
    diff = uy.ull - ux.ull;
  }
  return diff;
}

上述函数在评估 2 个 double 值彼此之间的接近程度的评级时

非常有用


一些补充意见

不是通常意义上的数值转换。它更像是双精度到 64 位有符号整数的一对一位映射。 2^52 与此无关。对于 64 位双精度数,大约有 2^64 位有限值。这里的并集将升序双精度值映射到 int64_t 值,从非常负的值到接近

INT64_MAX
。考虑 0.0 --> 0、
next_after(0, 1)
--> 1 等等,直到
DBL_MAX
--> INT64_MAX 附近的整数值。负数 double 映射到负数 int64_t。

正双编码干净地映射到正 int64_t。负双精度更像是符号数值编码,因此需要一个

x.ll = LLONG_MIN - x.ll;
来翻转 int64_t 2 的补码编码。

.d (in code)  .d (decimal FP) .d (hex FP)             .ull               .ll
-INFINITY     -inf           -inf                     0x8010000000000000 -9218868437227405312
-DBL_MAX      -1.79769e+308  -0x1.fffffffffffffp+1023 0x8010000000000001 -9218868437227405311
-1.0          -1             -0x1p+0                  0xC010000000000000 -4607182418800017408
-DBL_MIN      -2.22507e-308  -0x1p-1022               0xFFF0000000000000    -4503599627370496
-DBL_TRUE_MIN -4.94066e-324  -0x1p-1074               0xFFFFFFFFFFFFFFFF                   -1
-0.0          -0             -0x0p+0                  0x0000000000000000                    0
+0.0           0              0x0p+0                  0x0000000000000000                    0
 DBL_TRUE_MIN  4.94066e-324   0x1p-1074               0x0000000000000001                    1
 DBL_MIN       2.22507e-308   0x1p-1022               0x0010000000000000     4503599627370496
 1.0           1              0x1p+0                  0x3FF0000000000000  4607182418800017408
 DBL_MAX       1.79769e+308   0x1.fffffffffffffp+1023 0x7FEFFFFFFFFFFFFF  9218868437227405311
 INFINITY      inf            inf                     0x7FF0000000000000  9218868437227405312

1
投票

二进制浮点格式(例如 IEEE-754)可以被认为是由许多 binades 组成。 Binade 是一组完整的有效数字值,所有数字都具有相同的二次方指数。 因此,半开区间 [1.0, 2.0) 是一个二元,[2.0, 4.0) 也是如此,[4.0, 8.0) 也是如此。

对于双精度,每个二进制数由 252 有效数值组成。

因此,要计算 R0R1 之间的值的数量,您可以分三部分进行:

  1. R0与大于R0的下一个二的幂之间的值的数量(称为下一个二的幂值x
  2. R1和之前小于R1的2的幂之间的值的数量(称这个之前的2的幂值y
  3. xy 之间完整二进制值的数量
R0

x 之间的值的数量为 (x - R0) / res1,其中 res1 是包含 R0 的二进制组中的分辨率,它是 epsilon 乘以前一个幂小于 R0 的两个。 R1

y 之间的值的数量为 (R1 - y) / res2,其中 res2 是包含 R1 的二进制文件中的分辨率,它是 epsilon 乘以 y . 然后第 3 部分是二进制数的 252

倍,并且二进制数比

R0R1 之间的 2 的精确幂数少 1。 例如,如果R0

是12.34,

R1是345.678,那么R0R1之间的2的幂分别是16、32、64、128和256。有5个,所以有4 个完整的组合。 x 为 16,y 为 256。 我提到的“epsilon”是浮点格式的一个属性,并且(根据定义)是二进制数的分辨率[1.0, 2.0)。 对于双精度,epsilon 为 2.22045e-16。

显然仍有相当多的细节需要正确处理,以及一些边缘情况 - 特别是

R0

R1 之间存在 1 或 0 的 2 的幂的情况。 但这应该可以帮助您开始。

© www.soinside.com 2019 - 2024. All rights reserved.