当表示为binary64时,π和π/ 10如何看起来具有相同的相对误差?

Pas*_*uoq 6 floating-point

假设您很快想要确定当以IEEE 754二进制64格式表示时π或π/ 10中哪一个具有最大相对误差.此外,您只有一个C编译器.

您可以编写下面的C程序,或者更简洁的版本:

#include <stdio.h>
#include <math.h>

volatile long double pil = 3.14159265358979323846L;
volatile double pi = 3.14159265358979323846;

volatile long double tpil = 0.314159265358979323846L;
volatile double tpi = 0.314159265358979323846;

int main() {

  volatile long double abs = pil - pi;

  printf("%La\n%La\n%La\n", pil, (long double)pi, abs);

  printf("pi:   abs err %La -> rel %La\n", abs, abs / pil);

  volatile long double abst = tpil - tpi;
  printf("pi/10: abs err %La -> rel %La\n", abst, abst / tpil);

}
Run Code Online (Sandbox Code Playgroud)

有趣的是,这个程序显示π和π/ 10的相对误差是相同的:

0xc.90fdaa22168c235p-2
0xc.90fdaa22168cp-2
0x8.d4p-56
pi:   abs err 0x8.d4p-56 -> rel 0xb.3d85789395e215bp-58
pi/10: abs err 0xe.2p-60 -> rel 0xb.3d85789395e215bp-58

我添加了volatile限定符和中间计算来查看生成的程序集,并确保这不是编译器错误.显然不是.

这很奇怪,因为许多其他值v不具有v的相对误差与v/10的相对误差相同的属性.我用0.1和0.3检查.这也不是一个大小问题,因为3和3.5显然有不同的相对误差,而不是它们各自的十分之一.虽然5,10,15 ......确实与它们各自的十分之一具有相同的相对误差,但这些应被视为例外.

现在,程序不计算相对误差的确切值.它只有12位来表示它们(一个符号位加上64位有效数和53位有效数之间的11位差).因此,在4096中可能存在一个先验,例如π的绝对误差为π/ 10的绝对误差的10倍.

这看起来似乎不太巧合.一个真正的常数在3和3.5之间具有相同的相对误差是否比它的第十个具有相同的误差的概率是否double超出我的直觉所说的应该是?还是有另一种方法可以看到它,例如"这种情况一旦发生(双重)(π/ 10)的有效数就会有足够的尾随零,因为它的乘法乘以十来得到",这似乎更频繁(接近于1/8)?

Pas*_*uoq 6

查看这个问题的一个更好的方法是注意double最近的π的有效数是5的倍数:

#include <stdio.h>
#include <math.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>

volatile long double pil = 3.14159265358979323846L;
volatile double pi = 3.14159265358979323846;

volatile long double tpil = 0.314159265358979323846L;
volatile double tpi = 0.314159265358979323846;

void print_significand(double d) {
  uint64_t significand;

  memcpy(&significand, &d, 8);
  significand &= ((uint64_t)1<<52) - 1;
  significand |= (uint64_t)1<<52;

  printf("%" PRIx64 " %" PRIu64 "\n",
     significand, significand);
}

int main() {

  printf("Significand of (double)pi: ");
  print_significand(pi);

  printf("Significand of (double)(pi/10): ");
  print_significand(tpi);
  …
Run Code Online (Sandbox Code Playgroud)

由此可见:

Significand of (double)pi: 1921fb54442d18 7074237752028440
Significand of (double)(pi/10): 141b2f769cf0e0 5659390201622752

整数5659390201622752正好是7074237752028440/5*4.乘以4用于保持double表示π/ 10归一化.

因此,给定一个在2和4之间均匀选择的任意实数r,近似为最接近的双数d,在五中有一次机会有效数d是5的倍数.当这发生时,d/10.0确切.这是d/10.0"最接近r/10的双重"标题的非常好的候选者.

候选人有多好?

如果除以10将我们从binade的顶部(刚好低于4)带到了binade的底部(刚好超过0.25),那么可表示的双精度在r/10附近的密度相对较低,而不是在r周围,并且没有机会找到比r/10更接近的两倍d/10.0.

如果除以10将我们从binade的底部(略高于2)带到binade的顶部(刚好低于0.25),那么在r/10附近的双倍比r在r周围更密集.r/10有可能比d/10.0存在更好的双重近似,特别是如果dr已经是一个不好的近似值.

截止点是2.5.实π在这个截止点之上,因此知道其double近似的有效数可被5整除,足以推断出π/ 10将近似于π的近似值的十分之一,并且两者的相对误差都是近似值是相同的.