求解数学表达式时 C 中的浮点问题
Floating point issue in C when solving a math expression
我在用 C 语言求解数学函数时遇到了问题。
double task1_double() {
double a = 1000;
double b = 0.0001;
double result = (pow((a + b), 2) - (pow(a, 2) + 2 * a * b))/(pow(b, 2));
return result; }
float task1_float() {
float a = 1000;
float b = 0.0001f;
float result = (powf((a + b), 2) - (powf(a, 2) + 2 * a * b))/(powf(b, 2));
return result; }
当我使用双精度数据类型时 returns 1.001172,尽管使用浮点数据类型结果是 6250000.000000。
有人可以解释为什么会这样吗?提前致谢
通过输出中间结果可以发现是float丢精度造成的
#include <stdio.h>
#include <math.h>
double task1_double() {
double a = 1000;
double b = 0.0001;
double s1, s2, s3;
s1 = pow((a + b), 2);
s2 = pow(a, 2) + 2 * a * b;
s3 = pow(b, 2);
double result = (pow((a + b), 2) - (pow(a, 2) + 2 * a * b)) / (pow(b, 2));
printf("_double : %lf - %lf = %.15lf\n", s1, s2, s1 - s2);
printf("_double : (%lf - %lf) / %.10lf = %lf\n", s1, s2, s3, result);
return result;
}
float task1_float() {
float a = 1000;
float b = 0.0001f;
float s1, s2, s3;
s1 = powf((a + b), 2);
s2 = powf(a, 2) + 2 * a * b;
s3 = powf(b, 2);
float result = (powf((a + b), 2) - (powf(a, 2) + 2 * a * b)) / (powf(b, 2));
printf("_float : %lf - %lf = %.15lf\n", s1, s2, s1 - s2);
printf("_float : (%lf - %lf) / %.10lf = %lf\n", s1, s2, s3, result);
return result;
}
int main()
{
printf("%.10lf\n%.10lf\n", task1_double(), task1_float());
return 0;
}
输出:
_float : 1000000.250000 - 1000000.187500 = 0.062500000000000
_float : (1000000.250000 - 1000000.187500) / 0.0000000100 = 6250000.500000
_double : 1000000.200000 - 1000000.200000 = 0.000000010011718
_double : (1000000.200000 - 1000000.200000) / 0.0000000100 = 1.001172
1.0011717677
6250000.5000000000
可以看出:
float运算后出现轻微误差,但除以b的小值会导致误差放大很多倍
你的分子正好是
1000000.20000001 - 1000000.2 = 0.00000001
但是当你减去两个几乎相等的大数时,结果中的相对舍入误差可能会爆炸。这就是你所看到的。这是因为 float
数字有大约 7 个十进制数字的精度,而 double
数字有大约 16 个十进制数字。
让我们一步一步来:
exact float double
x = pow((a + b), 2) 1000000.20000001 1000000.25 1000000.200000009965
y = pow(a, 2) + 2 * a * b 1000000.2 1000000.1875 1000000.199999999953
x - y 0.00000001 0.0625 0.000000010011717677
对于两个数字相对于它们的量级之间如此小的差异,您通常会得到 float
结果等于 0.0。但在这种情况下,恰好 1000000.20000001 和 1000000.2 位于舍入边界的任一侧,导致前者向上舍入而后者向下舍入。所以他们的区别是六个数量级。
我在用 C 语言求解数学函数时遇到了问题。
double task1_double() {
double a = 1000;
double b = 0.0001;
double result = (pow((a + b), 2) - (pow(a, 2) + 2 * a * b))/(pow(b, 2));
return result; }
float task1_float() {
float a = 1000;
float b = 0.0001f;
float result = (powf((a + b), 2) - (powf(a, 2) + 2 * a * b))/(powf(b, 2));
return result; }
当我使用双精度数据类型时 returns 1.001172,尽管使用浮点数据类型结果是 6250000.000000。
有人可以解释为什么会这样吗?提前致谢
通过输出中间结果可以发现是float丢精度造成的
#include <stdio.h>
#include <math.h>
double task1_double() {
double a = 1000;
double b = 0.0001;
double s1, s2, s3;
s1 = pow((a + b), 2);
s2 = pow(a, 2) + 2 * a * b;
s3 = pow(b, 2);
double result = (pow((a + b), 2) - (pow(a, 2) + 2 * a * b)) / (pow(b, 2));
printf("_double : %lf - %lf = %.15lf\n", s1, s2, s1 - s2);
printf("_double : (%lf - %lf) / %.10lf = %lf\n", s1, s2, s3, result);
return result;
}
float task1_float() {
float a = 1000;
float b = 0.0001f;
float s1, s2, s3;
s1 = powf((a + b), 2);
s2 = powf(a, 2) + 2 * a * b;
s3 = powf(b, 2);
float result = (powf((a + b), 2) - (powf(a, 2) + 2 * a * b)) / (powf(b, 2));
printf("_float : %lf - %lf = %.15lf\n", s1, s2, s1 - s2);
printf("_float : (%lf - %lf) / %.10lf = %lf\n", s1, s2, s3, result);
return result;
}
int main()
{
printf("%.10lf\n%.10lf\n", task1_double(), task1_float());
return 0;
}
输出:
_float : 1000000.250000 - 1000000.187500 = 0.062500000000000
_float : (1000000.250000 - 1000000.187500) / 0.0000000100 = 6250000.500000
_double : 1000000.200000 - 1000000.200000 = 0.000000010011718
_double : (1000000.200000 - 1000000.200000) / 0.0000000100 = 1.001172
1.0011717677
6250000.5000000000
可以看出: float运算后出现轻微误差,但除以b的小值会导致误差放大很多倍
你的分子正好是
1000000.20000001 - 1000000.2 = 0.00000001
但是当你减去两个几乎相等的大数时,结果中的相对舍入误差可能会爆炸。这就是你所看到的。这是因为 float
数字有大约 7 个十进制数字的精度,而 double
数字有大约 16 个十进制数字。
让我们一步一步来:
exact float double
x = pow((a + b), 2) 1000000.20000001 1000000.25 1000000.200000009965
y = pow(a, 2) + 2 * a * b 1000000.2 1000000.1875 1000000.199999999953
x - y 0.00000001 0.0625 0.000000010011717677
对于两个数字相对于它们的量级之间如此小的差异,您通常会得到 float
结果等于 0.0。但在这种情况下,恰好 1000000.20000001 和 1000000.2 位于舍入边界的任一侧,导致前者向上舍入而后者向下舍入。所以他们的区别是六个数量级。