I want to know the difference between a long doubl

2020-05-09 19:00发布

问题:

For any algorithmic problem, an error of 10-6 is allowed.

I declared a long double as a variable in my first solution and got a WA.

But, when I declared a variable as double, I got an AC.

I want to know why this decision was made because long double is known to be more accurate than double. I have not changed anything except variables, output methods.

Here is my code:

#include <iostream>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <deque>
#include <algorithm>

using namespace std;

#define pi 3.141592653589

int main() {
    double x;
    double y;
    double r = 0.0;
    int n;
    double len;
    double ans = 0.0;
    deque<double> dq;

    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> x >> y;
        len = sqrt(x* x + y * y);
        if (len > r) {
            r = len;
            dq.clear();
            if ((x > 0 && y <= 0) || (x >= 0 && y < 0))
                dq.push_back(360.0 + (90 + atan2(x, y) * 180 * (-1) / pi));
            else
                dq.push_back(90+atan2(x, y) * 180 * (-1) / pi);
        } else if (r == len) {
            if ((x > 0 && y <= 0) || (x >= 0 && y < 0))
                dq.push_back(360 + (90 + atan2(x, y) * 180 * (-1) / pi));
            else
                dq.push_back(90+atan2(x, y) * 180 * (-1) / pi);
        }
    }
    sort(dq.begin(), dq.end());
    if (dq.size() >= 2) {
        for (int i = 0; i < dq.size() - 1; i++) {
            ans = max(ans, dq[i + 1] - dq[i]);
        }
        ans = max(ans, 360 - dq.back() + dq.front());
        printf("%0.6f", ans);
    }
    else
        cout << "360.000000";
}

The only changes I made were:

  • changing the double to long double; and
  • changing the printf format specifier from f to lf.

回答1:

... long double is known to be more accurate than double.

No, it's really not. It may be but it's by no means guaranteed.

The difference between the two types is detailed in the standard (in this case, C++17 [basic.fundamental]/8, though earlier iterations also have similar wording). The standard has this to say about the floating point types (my emphasis):

There are three floating-point types: float, double, and long double.

The type double provides at least as much precision as float, and the type long double provides at least as much precision as double.

The set of values of the type float is a subset of the set of values of the type double; the set of values of the type double is a subset of the set of values of the type long double.

The value representation of floating-point types is implementation-defined.

Since "subset" includes the possibility that the two sets are identical (it's axiomatic that A ⊂ A), there's no actual requirement than long double has a larger range and/or precision than double, though it sometimes does.

If you want to figure out what the differences are in your implementation, you should be looking into the numeric_limits class in the <limits> header, as per the following demo program:

#include <iostream>
#include <limits>
using namespace std;

int main() {
    numeric_limits<double> lim_d;
    numeric_limits<long double> lim_ld;

    cout << "\nmax " << lim_d.max() << " " << lim_ld.max()
         << "\nmin " << lim_d.min() << " " << lim_ld.min()
         << "\nlowest " << lim_d.lowest() << " " << lim_ld.lowest()
         << "\ndigits10 " << lim_d.digits10 << " " << lim_ld.digits10
         << '\n';
}

The output on my system is, formatted for readability:

              double        long double
           ============    =============
max        1.79769e+308    1.18973e+4932
min        2.22507e-308    3.3621 e-4932
lowest    -1.79769e+308   -1.18973e+4932
digits10             15               18

You can see that my range is substantially larger for a long double and there's also (roughly) an extra three decimal digits of precision.


In terms of what effect this can have on your code, it's difficult to say since you haven't actually provided an adequate description of what the problem is (specifically what wa and ac mean). However, since a long double may have more precision and/or range than a double, it's certainly conceivable that this may affect how your code behaves.

I should also mention that the correct format specifier for a long double is actually %Lf (capitalised L) rather than %lf. That may well be causing a problem for you since, with the test data given on the page you linked to in a comment, I get the correct result for double/%f and long double/%Lf.

But it gives different results (and a gcc warning, for that matter) for long double/%lf.



回答2:

The implementation of long double changes on the compiler and the hardware.

Visual Studio just uses long double as a synonym for double. You will need to use the intel compiler on windows to make use of extended precision hardware on intel architectures, or GCC (versions > 4.3). Source: https://en.wikipedia.org/wiki/Long_double

If you want to update your question to include a code sample or details on your compiler & architecture, I can add possibly more detail to the answer. But generally (on Intel) long double uses 80-bit hardware floating point operations.

EDIT: I ran Paxdiablo's code on a Sun/Oracle SparcV9 solaris box:

              double        long double
           ============    =============
max        1.79769e+308      1.18973e+4932
min        2.22507e-308      3.3621e-4932
lowest    -1.79769e+308     -1.18973e+4932
digits10             15                 33


回答3:

By using this code you can easily understand that the long double should be twice as precise as the double.

#include<iostream>
using namespace std;
main()
{
cout<<"size of double is "<<sizeof(double)<<" bytes"<<endl;
cout<<"size of long double is "<<sizeof(long double)<<" bytes";
return 0;
}