6

I've tried searching for information on long double, and so far I understand it is implemented differently by compilers.

When using GCC on Ubuntu (XUbuntu) Linux 12.10 I get this:

double PId = acos(-1);
long double PIl = acos(-1);
std::cout.precision(100);

std::cout << "PId " << sizeof(double) << " : " << PId << std::endl;
std::cout << "PIl " << sizeof(long double)  << " : " << PIl << std::endl;

Output:

PId 8  : 3.141592653589793115997963468544185161590576171875
PIl 16 : 3.141592653589793115997963468544185161590576171875

Anyone understand why they output (almost) the same thing?

stefan
  • 10,215
  • 4
  • 49
  • 90
FreelanceConsultant
  • 13,167
  • 27
  • 115
  • 225
  • 5
    `acos` won't return a long double as long as you don't pass a long double as argument.. – stefan Dec 16 '12 at 17:39
  • 2
    Side note: g++-4.6.3 requires the use of `std::acos` instead of `acos` to show a difference (at least on my ubuntu x86 machine) – stefan Dec 16 '12 at 17:46

3 Answers3

8

According to the reference of acos, it will return a long double only if you pass a long double to it. You'll also have to use std::acos like baboon suggested. This works for me:

#include <cmath>
#include <iostream>

int main() {

  double PId = acos((double)-1);
  long double PIl = std::acos(-1.0l);
  std::cout.precision(100);

  std::cout << "PId " << sizeof(double) << " :  " << PId << std::endl;
  std::cout << "PIl " << sizeof(long double)  << " : " << PIl << std::endl;
}

Output:

PId 8  : 3.141592653589793115997963468544185161590576171875
PIl 12 : 3.14159265358979323851280895940618620443274267017841339111328125

         3.14159265358979323846264338327950288419716939937510582097494459

The last line is not part of the output and contains the correct digits for pi to this precision.

Community
  • 1
  • 1
schnaader
  • 49,103
  • 10
  • 104
  • 136
6

To get the correct number of significant digits use std::numeric_limits. In C++11 we have digits10 for decimal significant digits (as opposed to digits which gives significant bits).

#include <cmath>
#include <iostream>
#include <limits>

int
main()
{
  std::cout.precision(std::numeric_limits<float>::digits10);
  double PIf = acos(-1.0F);
  std::cout << "PIf " << sizeof(float) << " :  " << PIf << std::endl;

  std::cout.precision(std::numeric_limits<double>::digits10);
  double PId = acos(-1.0);
  std::cout << "PId " << sizeof(double) << " :  " << PId << std::endl;

  std::cout.precision(std::numeric_limits<long double>::digits10);
  long double PIl = std::acos(-1.0L);
  std::cout << "PIl " << sizeof(long double)  << " : " << PIl << std::endl;
}

On x86_64 linux I get:

PIf 4 :  3.14159
PId 8 :  3.14159265358979
PIl 16 : 3.14159265358979324
emsr
  • 15,539
  • 6
  • 49
  • 62
  • 1
    It needs to use `std::numeric_limits::digits10 + 1`. E.g. `std::numeric_limits::digits10 + 1 == 3` – Maxim Egorushkin Mar 03 '15 at 16:43
  • @MaximEgorushkin C++11 adds `std::numeric_limits::digits10` that is guaranteed to round trip. In g++ this is routed to `__glibcxx_max_digits(__FLT_MANT_DIG__)` where the arg is a macro gotten from configuration. The result is digits10 + 2 for floating point types. For integral types `max_digits10` is set to 0 (why?). For the above code I should edit to `max_digits10`. Also, digits10 has always been there - i.e. before C++11. – emsr Mar 03 '15 at 21:02
  • I'm going to look further about the integral versions of digits10 and max_digits10. I wonder if either g++ of the standard is making a mistake. – emsr Mar 03 '15 at 21:05
  • I could argue that, for *display* purposes, `digits10` is correct. The extra digits provided by using `max_digits10` are inaccurate but are necessary to ensure that enough bits are loaded after a read so that rewriting the decimal to `digits10` still retains accuracy. Still not sure what's up with integral types. – emsr Mar 03 '15 at 21:09
  • The standard is pretty clear on [`digits10`](http://en.cppreference.com/w/cpp/types/numeric_limits/digits10): _is the number of base-10 digits that can be represented by the type T without change, that is, any number with this many decimal digits can be converted to a value of type T and back to decimal form, without change due to rounding or overflow._ E.g. any 3 digits cannot be represented by `uint8_t`, whereas any 2 can. – Maxim Egorushkin Mar 04 '15 at 11:09
  • I think they punted on `max_digits10` for integers. They should have made it `max_digits10 = digits10 + 1` so that a large uint8_t of, say, 145 could be written and read back. I think a lot of this had to do with mixing the semantics of integer and floating point in the same utility. – emsr Mar 05 '15 at 14:36
4

Try:

long double PIl = std::acos(-1.0L);

That makes you pass a long double and not just an int which gets converted.

Note that mostly all of these numbers are rubbish anyway. With an 8 byte double you get 15 Numbers of precision, if you compare your numbers with the real PI

3.1415926535897932384626433

You see that only the first 15 Numbers fit.

As noted in the comments, you probably won't get double the precision as the implementation might only use a 80Bit representation and then it depends on how many bits it reserves for the mantissa.

Stephan Dollberg
  • 32,985
  • 16
  • 81
  • 107