Floating point 为什么π;和π/10在表示为binary64时似乎具有相同的相对错误?
假设您很快想要确定π或π/10中的哪一个在以IEEE 754二进制64格式表示时具有最大的相对误差。另外,您手头只有一个C编译器 您可以编写下面的C程序,或更紧凑的版本:Floating point 为什么π;和π/10在表示为binary64时似乎具有相同的相对错误?,floating-point,Floating Point,假设您很快想要确定π或π/10中的哪一个在以IEEE 754二进制64格式表示时具有最大的相对误差。另外,您手头只有一个C编译器 您可以编写下面的C程序,或更紧凑的版本: #include <stdio.h> #include <math.h> volatile long double pil = 3.14159265358979323846L; volatile double pi = 3.14159265358979323846; volatile long do
#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);
}
#包括
#包括
挥发性长双pil=3.14159265358979323846L;
挥发性双pi=3.14159265358979323846;
挥发性长双tpil=0.314159265358979323846L;
挥发性双tpi=0.314159265358979323846;
int main(){
易变长双abs=pil-pi;
printf(“%La\n%La\n%La\n”,pil,(长双)pi,abs);
printf(“pi:abs err%La->rel%La\n”,abs,abs/pil);
挥发性长双ABS=tpil-tpi;
printf(“pi/10:abs错误%La->rel%La\n”,abs,abs/tpil);
}
有趣的是,这个程序显示π和π/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
0xc.90fdaa22168c235p-2
0xc.90fdaa22168cp-2
0x8.d4p-56
pi:abs错误0x8.d4p-56->rel 0xb.3d85789395e215bp-58
pi/10:abs错误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倍
这似乎仍然是一个不太可能的巧合。当表示为
double
时,取3到3.5之间的一个实常数比其第十个具有相同相对误差的先验概率是否比我的直觉所说的要大?或者有没有其他方式来看待它,比如“只要(double)(π/10)的有效位有足够的尾随零,可以精确地乘以10,就会发生这种情况”,这似乎更频繁(接近1/8)?看这个问题的更好方法是注意到最近π的有效位是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);
…
#包括
#包括
#包括
#包括
#包括
挥发性长双pil=3.14159265358979323846L;
挥发性双pi=3.14159265358979323846;
挥发性长双tpil=0.314159265358979323846L;
挥发性双tpi=0.314159265358979323846;
无效打印\u有效位(双d){
uint64_t有效位;
memcpy(&有效位,&d,8);
有效位&=((uint64\u t)1使用MP软件包,我确认观察结果,但错误值不同:pi=4000 C90FDAA2 2168C000 00000000…pi=4000 C90FDAA2 2168C234 C4C6628B 80DC1…abs err=-1.2246467991473532e-016-0x1.1a6263p-53 rel err=-3.8981718325193755e-017-0x1.678afbp-55 pi/10=3FFD D97BB4 E78A070000000000…pi/10=3FFD A0D97BB4 E78701C3 D09EB53C 67167…abs错误=-1.2246467991473531e-017-0x1.c3d09fp-57相关错误=-3.8981718325193755e-017-0x1.678afbp-55
Significand of (double)pi: 1921fb54442d18 7074237752028440
Significand of (double)(pi/10): 141b2f769cf0e0 5659390201622752