这是我正在进行的bank测试,了解FP
基本操作(+
,-
,*
,/
)会引入错误:
#include <iostream>
#include <math.h>
int main() {
std::cout.precision(100);
double a = 0.499999999999999944488848768742172978818416595458984375;
double original = 47.9;
double target = original * a;
double back = target / a;
std::cout << original << std::endl;
std::cout << back << std::endl;
std::cout << fabs(original - back) << std::endl; // its always 0.0 for the test I did
}
您能告诉我两个值(original
和a
),一旦*
(或/
),由于FP
数学而引入错误?
并且如果存在,是否可以确定该错误是由*
还是/
引起的?如何? (因为同时需要两个值才能返回值; 80 bit
?)
使用+
很容易(只需将0.499999999999999944488848768742172978818416595458984375
添加到0.5
,就可以得到1.0
,就像0.5 + 0.5
一样。)
但是我无法使用*
或/
做同样的事情。
答案 0 :(得分:0)
输出:
#include <cstdio>
int main(void)
{
double a = 1000000000000.;
double b = 1000000000000.;
std::printf("a = %.99g.\n", a);
std::printf("a = %.99g.\n", b);
std::printf("a*b = %.99g.\n", a*b);
}
是:
a = 1000000000000. a = 1000000000000. a*b = 999999999999999983222784.
假定IEEE-754基本的64位二进制浮点具有正确的舍入至最接近的值,并与偶数相关。
很明显,999999999999999983222784与精确的数学结果1000000000000•1000000000000,1000000000000000000000000不同。
答案 1 :(得分:0)
将任意两个大†数相乘,由于可表示的值在较大的值范围内相距很远,因此可能会出现错误。
虽然此错误从绝对值上来说可能很大,但相对于数字本身的大小而言仍然很小,因此,如果执行反向除法,则第一个操作的错误将以相同的比例缩小,并且完全消失。因此,此操作顺序是稳定的。
如果相乘的结果大于可表示的最大值,则它将溢出到无穷大(可能取决于配置),在这种情况下,反向除法不会导致原始值,而是保持无穷大。
类似地,如果用大数除,则可能会下溢最小的可表示值,从而导致零或次正规值。
†数字不一定必须很大。在考虑巨大价值时,更容易理解这个问题。这个问题也适用于很小的值。例如:
2.100000000000000088817841970012523233890533447265625 ×
2.100000000000000088817841970012523233890533447265625
正确的结果:
4.410000000000000373034936274052605470949292688633679117285...
浮点结果示例:
4.410000000000000142108547152020037174224853515625
错误:
2.30926389122032568296724439173008679117285652827862296732064351090230047702789306640625
× 10^-16
答案 2 :(得分:0)
是否存在两个彼此相乘(或相除)的数字会引入错误?
使用"%a"
可以更容易地看到它。
结果的精度不足时,将进行四舍五入。通常double
具有53位的 binary 精度。将下面的2个27位数字相乘得到一个确切的53位答案,但是2个28位数字不能形成55位有效答案。
除法很容易演示,只需尝试1.0/n*n
。
int main(void) {
double a = 1 + 1.0/pow(2,26);
printf("%.15a, %.17e\n", a, a);
printf("%.15a, %.17e\n", a*a, a*a);
double b = 1 + 1.0/pow(2,27);
printf("%.15a, %.17e\n", b, b);
printf("%.15a, %.17e\n", b*b, b*b);
for (int n = 47; n < 52; n += 2) {
volatile double frac = 1.0/n;
printf("%.15a, %.17e %d\n", frac, frac, n);
printf("%.15a, %.17e\n", frac*n, frac*n);
}
return 0;
}
输出
//v-------v 27 significant bits.
0x1.000000400000000p+0, 1.00000001490116119e+00
//v-------------v 53 significant bits.
0x1.000000800000100p+0, 1.00000002980232261e+00
//v-------v 28 significant bits.
0x1.000000200000000p+0, 1.00000000745058060e+00
//v--------------v not 55 significant bits.
0x1.000000400000000p+0, 1.00000001490116119e+00
// ^^^ all zeros here, not the expected mathematical answer.
0x1.5c9882b93105700p-6, 2.12765957446808505e-02 47
0x1.000000000000000p+0, 1.00000000000000000e+00
0x1.4e5e0a72f053900p-6, 2.04081632653061208e-02 49
0x1.fffffffffffff00p-1, 9.99999999999999889e-01 <==== Not 1.0
0x1.414141414141400p-6, 1.96078431372549017e-02 51
0x1.000000000000000p+0, 1.00000000000000000e+00