双重载 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%20precisiondouble 是一个 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格式。
  • a1a2 分别乘以 pi
  • 那些产品除以180。
  • 取这些商的正弦和余弦。
  • 2 * vy12 * vy2除以g。 (乘以 2 不会引入任何舍入误差,因为它的结果可以用基于二进制的浮点数精确表示。)
  • 这些商乘以 vx1vx2
  • 这些产品被转换为十进制显示。

此外,sincos 难以实现,并且常见的实现更喜欢速度并以允许一些额外错误为代价提供它。他们的结果可能会相差几个 ULP(最低精度单位),在糟糕的实施中可能会相差更多。