双重载 sin() 和 cos() 不保持 15 位小数精度
double overloaded sin() and cos() do not maintain 15-digit decimal precision
以此 link 为指导,https://www.geeksforgeeks.org/difference-float-double-c-cpp/#:~:text=double%20is%20a%2064%20bit,15%20decimal%20digits%20of%20precision。 double 是一个 64 位 IEEE 754 双精度浮点数(1 位用于符号,11 位用于指数,52 位用于值),即 double有 15 位十进制数字的精度 ,下面的代码不保持 15 位十进制数字的精度。相反,14.
用于简单的弹丸运动计算器,其中,以 30 度发射的弹丸的射程应与以 60 度发射的同一弹丸的射程相匹配。
#include <iostream>
#include <iomanip>
int main()
{
const double g = 9.80665;
const double pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
double a1 = 30.0;
double a2 = 60.0;
double v = 25.0;
double vx1 = v * cos(a1 * pi/180.0);
double vy1 = v * sin(a1 * pi/180.0);
double vx2 = v * cos(a2 * pi/180.0);
double vy2 = v * sin(a2 * pi/180.0);
double t_max1 = 2 * vy1 / g;
double t_max2 = 2 * vy2 / g;
double range1 = t_max1 * vx1;
double range2 = t_max2 * vx2;
std::cout << std::setprecision(16) << range1 << ", " << range2 << std::endl;
return 0;
}
输出:55.19375906810931, 55.19375906810933
double overloaded sin() and cos() do not maintain 15-digit decimal precision
任何固定大小的数字格式都不可能“保持”特定的精度,无论它是浮点数、整数、定点数还是其他。
每当用实数数学执行的运算结果无法用数字格式表示时,只能返回实数结果的近似值。实数结果必须四舍五入到某个可表示的值。这引入了舍入误差。
当有多个运算时,舍入误差可能会以各种方式累积和复合。相互作用和后果可能非常复杂,并且有一个完整的研究领域numerical analysis。
作为一个简单的例子,考虑分辨率为 1 的整数运算。然而,如果我们用 17/3*5
计算 17/3•5,我们得到 25,其中实数结果为28⅓,最接近理想结果的整数结果为 28。因此即使我们只进行了两次运算,计算结果仍与最佳可表示结果相差三个单位(与实数结果相差 3⅓)。整数运算不能“保持”1个精度单位。
在您的示例中,这些操作出现舍入错误:
- 9.80665 转换为
double
格式。
- π的数字转换为
double
格式。
a1
和 a2
分别乘以 pi
。
- 那些产品除以180。
- 取这些商的正弦和余弦。
2 * vy1
和2 * vy2
除以g
。 (乘以 2 不会引入任何舍入误差,因为它的结果可以用基于二进制的浮点数精确表示。)
- 这些商乘以
vx1
和 vx2
。
- 这些产品被转换为十进制显示。
此外,sin
和 cos
难以实现,并且常见的实现更喜欢速度并以允许一些额外错误为代价提供它。他们的结果可能会相差几个 ULP(最低精度单位),在糟糕的实施中可能会相差更多。
以此 link 为指导,https://www.geeksforgeeks.org/difference-float-double-c-cpp/#:~:text=double%20is%20a%2064%20bit,15%20decimal%20digits%20of%20precision。 double 是一个 64 位 IEEE 754 双精度浮点数(1 位用于符号,11 位用于指数,52 位用于值),即 double有 15 位十进制数字的精度 ,下面的代码不保持 15 位十进制数字的精度。相反,14.
用于简单的弹丸运动计算器,其中,以 30 度发射的弹丸的射程应与以 60 度发射的同一弹丸的射程相匹配。
#include <iostream>
#include <iomanip>
int main()
{
const double g = 9.80665;
const double pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679;
double a1 = 30.0;
double a2 = 60.0;
double v = 25.0;
double vx1 = v * cos(a1 * pi/180.0);
double vy1 = v * sin(a1 * pi/180.0);
double vx2 = v * cos(a2 * pi/180.0);
double vy2 = v * sin(a2 * pi/180.0);
double t_max1 = 2 * vy1 / g;
double t_max2 = 2 * vy2 / g;
double range1 = t_max1 * vx1;
double range2 = t_max2 * vx2;
std::cout << std::setprecision(16) << range1 << ", " << range2 << std::endl;
return 0;
}
输出:55.19375906810931, 55.19375906810933
double overloaded sin() and cos() do not maintain 15-digit decimal precision
任何固定大小的数字格式都不可能“保持”特定的精度,无论它是浮点数、整数、定点数还是其他。
每当用实数数学执行的运算结果无法用数字格式表示时,只能返回实数结果的近似值。实数结果必须四舍五入到某个可表示的值。这引入了舍入误差。
当有多个运算时,舍入误差可能会以各种方式累积和复合。相互作用和后果可能非常复杂,并且有一个完整的研究领域numerical analysis。
作为一个简单的例子,考虑分辨率为 1 的整数运算。然而,如果我们用 17/3*5
计算 17/3•5,我们得到 25,其中实数结果为28⅓,最接近理想结果的整数结果为 28。因此即使我们只进行了两次运算,计算结果仍与最佳可表示结果相差三个单位(与实数结果相差 3⅓)。整数运算不能“保持”1个精度单位。
在您的示例中,这些操作出现舍入错误:
- 9.80665 转换为
double
格式。 - π的数字转换为
double
格式。 a1
和a2
分别乘以pi
。- 那些产品除以180。
- 取这些商的正弦和余弦。
2 * vy1
和2 * vy2
除以g
。 (乘以 2 不会引入任何舍入误差,因为它的结果可以用基于二进制的浮点数精确表示。)- 这些商乘以
vx1
和vx2
。 - 这些产品被转换为十进制显示。
此外,sin
和 cos
难以实现,并且常见的实现更喜欢速度并以允许一些额外错误为代价提供它。他们的结果可能会相差几个 ULP(最低精度单位),在糟糕的实施中可能会相差更多。