3

I have the following c program, and some of the times I run it, the output differs, based on the compiler and the platform. I understand that double to int conversion could cause problems.

Here is the code:

//Compiler version gcc 6.3.0
#include <stdio.h>
#include <math.h>

int main(void){
    double d = 2;
    printf("%.20lf\n", pow(10, d));
    printf("%d\n", (int)pow(10, d));
    printf("%d\n", (int)pow(10, 2));
}

100 is the expected value, but the statement

    printf("%d\n", (int)pow(10, d));

has 99 as output when I use both gcc 6.3.0 and Windows 10 x64, but not in other cases.

Here are some results:

//gcc 6.3.0 (Sublime Text 3) in Windows 10 x64
100.00000000000000000000
99 ->this is the problem
100

//gcc 6.3.0 in Android (using Dcoder app)
100.00000000000000000000
100
100

//MSVC(VS 2017 x86) in Windows 10 x64
100.00000000000000000000
100
100

I also tested some online gcc(6.3.0) compilers but all the outputs were 100.

Thanks for the help.

  • Using `(int)round(pow(10, d))` should fix the second one. The third one `pow(10, 2)` was possibly hard coded as `100` by the optimising compiler. – Weather Vane Feb 03 '19 at 19:32
  • @user3386109: This is not a duplicate of [that question](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) because the floating-point format is perfectly capable of representing 100 exactly, so the error is not due to characteristics of floating-point arithmetic. It is due to a deficient implementation of `pow`. – Eric Postpischil Feb 03 '19 at 19:38
  • The posted code does have some syntax problems. The first one is that `double d = 2;` is trying to initialize a `double` from an `int`. The statement should be: `double d = 2.0;` – user3629249 Feb 05 '19 at 02:09

1 Answers1

5

Some implementations of pow return values different from 100 for pow(10, 2). For example, 99.9999999999999857891452847979962825775146484375 may be returned. When this double value is converted to int, the result is 99.

This is a problem in the quality of the pow implementation. Good pow implementations return exact results when exact results are possible.

Additionally, I suspect printf("%.20lf\n", pow(10, d)) is not formatting correctly in the Windows implementation—it may be rounding its conversion internally to something like 15 significant decimal digits before formatting it to 20 digits for output. You can test this by printing printf("%.20g\n", pow(10, d)-100). That will subtract 100 from pow(10, d) in double arithmetic. If it shows a non-zero output, you know that pow(10, d) was not exactly 100, so the printf("%.20lf\n", pow(10, d)) showed an incorrect result.

Note that pow is a difficult function to implement well. Only a limited number of cases have exact results, so most results are necessarily inexact. However, even in those cases, getting a result that is correctly rounded—rounded to the nearest representable value—is difficult. To my knowledge, nobody has implemented pow with that quality. Most pow implementations allow some additional error, and therefore you should not rely on pow to be correctly rounded. However, it is possible to implement pow so that cases where exact results are possible do indeed return exact results, and some implementations achieve that.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    Does gcc 6.3.0 use Microsoft software to compute `pow()`? I would assume it uses `glibc` instead. BTW, I am not aware of any `pow` implementation that *always* delivers an exact result when an exact result is possible. – njuffa Feb 03 '19 at 19:47
  • @njuffa: I believe the macOS `pow` delivers an exact result when an exact result is representable. I know that GCC is “just” the compiler and requires a standard library to be supplied along with it, but I am unfamiliar with common packaging and typical installation configurations for Windows. – Eric Postpischil Feb 03 '19 at 19:49
  • I am looking forward to a published proof of the claim made with regard to macOS `pow`. – njuffa Feb 03 '19 at 19:50
  • 1
    @njuffa: I think you can list (and therefore test) the exact cases by hand: `pow(x, 0)`, `pow(x, 1)`, `pow(x, -1)` for `x` a power of two, `pow(x, n)` for integers `n`>1 where `x` does not have so many bits that `x`^`n` gets too wide, a limited number of square roots, fourth roots, eighth roots, and so on. – Eric Postpischil Feb 03 '19 at 19:51
  • Hmm, well, you can list the categories of exact cases by hand, but testing them is a bit more difficult. `pow(x, 0)` and `pow(x, 1)` are at the edge of feasibility for testing even given plentiful hardware, but they are special cases that are not difficult to handle correctly. I think the remaining cases are few enough in number that they could be tested (by computer, not hand) in a reasonable time. – Eric Postpischil Feb 03 '19 at 20:06
  • The answer would lose nothing if the second paragraph were omitted. – njuffa Feb 03 '19 at 20:26
  • (1) Given that this is a question about gcc, I see nothing that directly implicates Microsoft software as the root cause of the observation. (2) What constitutes a "good" implementation of `pow()` is a matter of opinion (3) making "good" dependent upon an unproven (if plausible) property seems questionable. In a nutshell, I see the second paragraph as based on conjecture and opinion; the answer would be better without it. – njuffa Feb 03 '19 at 21:06
  • Fully agreed on "better" :-) – njuffa Feb 03 '19 at 23:57
  • I've searched more about this problem based on your answer, and found something interesting. Windows platform has its own C library not glibc(which is for Linux) but something provided from msvcrt.dll. However, GCC is not compatible with this Windows based library so there is the alternative called MinGW. –  Feb 04 '19 at 06:52
  • One of my friends who uses MinGW library had the exact same problem, so I'm totally suspicious with this library. For MSVC, it made the right expected value 100 because it was depending on Windows library not MinGW. I'm not 100 percent sure because I'm not a contributor to MinGW library implementation, but anyway, I could get some clues from your answer. Thanks a lot. –  Feb 04 '19 at 06:52
  • 1
    @njuffa: The author confirmed for me the macOS `pow` returns exact results when they are representable. This follows from the fact that it has sub-ULP accuracy; each result is less than 1 ULP from the ideal result. When a result is representable, the value is the only representable value less than 1 ULP from the ideal result. – Eric Postpischil Apr 04 '19 at 20:03