0

Using this link as a guide, https://www.geeksforgeeks.org/difference-float-double-c-cpp/#:~:text=double%20is%20a%2064%20bit,15%20decimal%20digits%20of%20precision. double is a 64 bit IEEE 754 double precision Floating Point Number (1 bit for the sign, 11 bits for the exponent, and 52 bits for the value), i.e. double has 15 decimal digits of precision , the below code does not maintain 15 decimal digits of precision. Rather, 14.

It is for simple projectile motion calculator, where, the range of a projectile launched at 30 degrees should match that of the same projectile launched at 60 degrees.

#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;
}

Output: 55.19375906810931, 55.19375906810933

  • 1
    `std::cout << std::setprecision(20) << range1 << ", " << range2 << std::endl;` gives `55.19375906810930843, 55.193759068109329746`. `std::setprecision` sets the number of all digits, not only those after the dot. – 273K May 16 '21 at 05:17
  • This is basically an artifact of the way that floating point works. Notice that you're only off by `0.00000000000002` (only the last digit). Floating point has a certain number of digits (actually bits) of precision, but the precision is not just in the final value but all the intermediate values as well. This means that you'll sometimes have rounding errors, where the early rounding affects the final result. – Justin May 16 '21 at 05:19
  • How can I make the code produce the result to 15 digits of decimal precision? – asjhdbashjdbasjhdbhjb May 16 '21 at 05:20
  • @asjhdbashjdbasjhdbhjb The easiest thing to try would be using `long double`s. On Intel machines, `long double` is an 80-bit floating point number rather than a 64-bit floating point number, so it has more internal precision. If you use `long double`, you might have enough extra precision. – Justin May 16 '21 at 05:22
  • `i.e. double has 15 decimal digits of precision` does not mean tenths after dot. – 273K May 16 '21 at 05:22
  • Although there may also be a limitation on the precision that `std::sin`/`std::cos` give you; I'm not aware of whether they give you the full amount of precision. I do know that the hardware sin/cos instructions generally do not, as it's far more efficient to not go to the full precision (I believe they implement it through a lookup table + several iterations of a technique such as Newton's method; adding precision increases the number of iterations necessary) – Justin May 16 '21 at 05:25
  • @Justin on my machine, it is using msvc with intel i7 cpu, but long double gave the same result? – asjhdbashjdbasjhdbhjb May 16 '21 at 05:38
  • MSVC compiler consider `long double` as 64-bit, just like `double`. For 80-bit `long double`, you need GCC or Clang. – prapin May 16 '21 at 08:56
  • @prapin suppose that 80-bits isn't enough for what I need, are there any other options? – asjhdbashjdbasjhdbhjb May 16 '21 at 09:09
  • 1
    Be aware that some rounding already happens when computing `a1 * pi/180.0` and `a2 * pi/180.0`. – chtz May 16 '21 at 10:16
  • asjhdbashjdbasjhdbhjb, To reduce errors in trig function when the argument is in degrees, consider [Sin and Cos give unexpected results for well-known angles](https://stackoverflow.com/a/31525208/2410359). – chux - Reinstate Monica May 17 '21 at 03:09

1 Answers1

4

double overloaded sin() and cos() do not maintain 15-digit decimal precision

It is not possible for any fixed-size numerical format to “maintain” a specific precision, regardless of whether it is floating-point, integer, fixed-point, or something else.

Whenever the result of an operation performed with real-number mathematics is not representable in the numerical format, only an approximation of the real-number result can be returned. The real-number result must be rounded to some representable value. This introduces a rounding error.

When there are multiple operations, the rounding errors may accumulate and compound in various ways. The interactions and consequences may be very complicated, and there is an entire field of study, numerical analysis, for it.

As a simple example, consider integer arithmetic, in which the resolution is 1. Yet, if we compute 17/3•5 with 17/3*5, we get 25, where the real-number result would be 28⅓, and the integer result nearest the ideal result would be 28. So the computed result is off by three units from the best representable result (and 3⅓ from the real-number result) even though we only did two operations. Integer arithmetic cannot “maintain” 1 unit of precision.

In your sample, rounding errors occur in these operations:

  • 9.80665 is converted to the double format.
  • The numeral for π is converted to the double format.
  • a1 and a2 are each multiplied by pi.
  • Those products are divided by 180.
  • The sines and cosines of those quotients are taken.
  • 2 * vy1 and 2 * vy2 are divided by g. (The multiplication by 2 does not introduce any rounding error as its result is exactly representable in binary-based floating-point.)
  • Those quotients are multiplied by vx1 and vx2.
  • Those products are converted to decimal for display.

Additionally, sin and cos are difficult to implement, and common implementations prefer speed and provide it at the cost of allowing a little additional error. Their results could be off by a few ULP (units of least precision), possibly more in bad implementations.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • What would be a solution for specifying decimal precision (I know you *can't* necessarily specify it due to rounding errors and compounding them), but there is a python library called **mpmath** that lets you set the bits of precision (i.e. default is 53 (double), but its able to take 1000+). Is there such a thing in C or C++? – asjhdbashjdbasjhdbhjb May 16 '21 at 22:43
  • It would probably make sense to mention `sinpi` and `cospi` here, even if they are only available as platform-specific extensions at the moment. Sometimes that is all that is needed to fix accuracy problems related to trig functions, e.g. for [this question](https://scicomp.stackexchange.com/questions/31345/whittaker-shannon-interpolation-accuracy-dies-with-speedup-can-it-be-fixed/31351#31351) – njuffa May 17 '21 at 00:33