3

my code:

int main()
{
long long a = pow(2,63) - 1;
long long b = pow(2,63);
double c  = pow(2,63) - 1;
double d = pow(2,63);
printf("%lld %lld \n%f %f \n%lld %lld\n", a, b, c, d, (long long)c, (long long)d);

return 0;
}

and the excute result is (codeblock with gcc in win7 x64):

9223372036854775807 9223372036854775807
9223372036854775800.000000 9223372036854775800.000000
-9223372036854775808 -9223372036854775808

Question:

Why a == b ?

I know that c == d because of the precision of double.

But why (long long)c and (long long)d is not 9223372036854775800 ?

And why (long long)c != a and (long long)d != b?

zakinster
  • 10,508
  • 1
  • 41
  • 52
tenos
  • 909
  • 1
  • 9
  • 16
  • 1
    And you do [What Every Computer Scientist Should Know About Floating-Point Arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html)? – Some programmer dude Oct 08 '13 at 14:10
  • 1
    `pow` returns a `double`, not an integer. – kennytm Oct 08 '13 at 14:12
  • shouldn't it be `%lf` instead of `%f` in your format string for printing doubles? – Nicu Stiurca Oct 08 '13 at 14:20
  • @SchighSchagh: No, `%lf` is for `long double`; `%f` is for `double` (and also `float`, which is promoted to `double` when passed as a variadic argument). – Mike Seymour Oct 08 '13 at 14:26
  • @MikeSeymour Hmm, looks like you are right about float promotion to double, but it turns out that both `%f` and `%lf` work for both `float` and `double`, and you actually use `%Lf` for `long double`. Incidentally, for `scanf` you do need to use `%f` for `float` only and `%lf` for `double` only. – Nicu Stiurca Oct 08 '13 at 14:47

6 Answers6

4

pow(2,63) - 1 is all done in double-precision floating point arithmetic. In particular, the -1 is converted into -1.0 and that is too small to matter

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • that's why `c` == `d` (which the author already knew) - it doesn't explain the rest though. See my answer further for more details. – Sander De Dycker Oct 08 '13 at 15:11
  • @tenos: After drinking a cup of water from the ocean (2^63-1), do you expect the weight of the ocean changes? – masoud Oct 08 '13 at 17:34
2

why a == b? I know that c == d because of the precision of double.

For exactly the same reason. There are no overloads of pow for integer types, so the arithmetic is done using double. Since double typically has 52 bits of significance, adding or subtracting 1 to a value as large as 263 will have no effect.

why (long long)c and (long long)d is not 9223372036854775800?

Because long long is a 64-bit signed type, and the maximum representable value is 263-1. c and d might both have the value 263 (or even a slightly larger value), which is out of range. On a typical 2s-complement platform, this is likely to overflow to give a value around -263, as you observe. But note that this is undefined behaviour; you cannot rely on anything if a floating point conversion overflows.

why (long long)c != a and (long long)d != b?

I don't know; for me, a and b have the same large negative values. It looks like some quirk of your implementation caused a and b to end up with the value 263-1 rather than the expected 263. As always when dealing with floating-point numbers, you should expect small rounding errors like that.

You could get the exact result by using integer arithmetic:

long long a = (1ULL << 63) - 1;
unsigned long long b = 1ULL << 63;

Note the use of unsigned arithmetic since, as mentioned above, the signed (1LL << 63) would overflow.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • “On a typical 2s-complement platform, this will overflow to give -2^63, as you observe.” This overflow is undefined behavior, and does not result in -2^63 on the OP's platform (for `a` and `b`). The compiler does not have to give consistent results between `a` and `(long long)c`. – Pascal Cuoq Oct 09 '13 at 01:01
  • @PascalCuoq: Indeed; I've added some extra qualifications in case anyone else mistakes my description of likely causes of the observed behaviour for any kind of definition. – Mike Seymour Oct 09 '13 at 09:40
2

why a == b

Because your compiler (gcc) calculated the values to initialize a and b with, and found (proved ?) both were matching or exceeding the maximum possible value for a long long, so it initialized both with that maximum value LLONG_MAX (or 0x7FFFFFFFFFFFFFFF, or 9223372036854775807 on your platform).

Note that (as pointed out by Pascal Cuoq) this is undefined behaviour, caused by an overflow while converting a double to a long long when initializing a and b. While gcc deals with this as described above, other compilers can deal with this differently

I know that c ==d because of the precision of double

The reason c and d hold the same value is indeed because of the precision of a double :

  • pow(2, 63) can be accurately represented with fraction 1 and exponent 63
  • pow(2, 63) - 1 cannot be accurately represented

The reason it's not showing 9223372036854775808 (the precise value stored in c and d), is because of the printf precision, which on your platform apparently only shows 17 digits. You might be able to force it to show more using eg. %20.0f, but on Windows that will likely not make a difference due to this bug.

why (long long)c and (long long)d is not 9223372036854775800 ?

Because c and d hold the value 9223372036854775808, or 0x8000000000000000, which when printed as a signed value becomes -9223372036854775808.

Note that this is again undefined behaviour (due to signed overflow).

why (long long)c != a and (long long)d != b?

Because they were calculated in different ways. a and b were calculated by the compiler, while (long long) c and (long long) d were calculated at runtime.

While normally, these different ways of calculating should yield the same results, we're dealing with undefined behaviour here (as explained earlier), so anything goes. And in your case, the compiler's results are different from the runtime results.

Sander De Dycker
  • 16,053
  • 1
  • 35
  • 40
  • Both the initialization of `a` and `b` invoke undefined behavior (overflow in a conversion from double to integer type). They do not have to be 0x7FFFFFFFFFFFFFFF, and they do not have to be the same value. You make it sound as if it is normal for these variables to receive these values but it is no more to be expected than for `u` to be set to `1` in `int t, u; *(&t + 1) = 1;`. – Pascal Cuoq Oct 09 '13 at 00:57
  • @Sander printf("20.0f",c) is the same result as printf("%f",c) – tenos Oct 09 '13 at 03:15
  • @PascalCuoq : it's gcc's behavior - the compiler used by the asker - which is why I said "your compiler ...". – Sander De Dycker Oct 09 '13 at 07:38
  • @tenos : I seem to recall having this issue with the Windows C runtime ... I seem to recall this is a known bug (ie. it's only able to show 17 digits of precision), but someone with better knowledge of the Windows environment will be able to confirm/deny that. – Sander De Dycker Oct 09 '13 at 07:42
  • @tenos : found the bug in question : http://connect.microsoft.com/VisualStudio/feedback/details/329278/printf-sprintf-do-not-print-enough-significant-digits-for-exact-binary-fractions – Sander De Dycker Oct 09 '13 at 07:47
  • @SanderDeDycker I need to insist that it is gcc's behavior to set `a` to `0x7FFFFFFFFFFFFFFF` in the same sense that gcc's behavior is to set `u` to `1` for the snippet `int t, u; *(&t + 1) = 1;`. You wouldn't answer a question about the latter snippet with “your compiler found that variable `u` was declared just after variable `t`, so it set `u` to `1`”. – Pascal Cuoq Oct 09 '13 at 08:51
  • @PascalCuoq : I'm not sure what you want me to do ... do you want me to replace "your compiler" with "gcc" ? That's what I meant, since that's the compiler tenos is using. – Sander De Dycker Oct 09 '13 at 09:05
  • @SanderDeDycker I would suggest “Your compiler is free to generate any code at all for your program, and to give different values to `b` and to `(long long)d`, because each of the expression `(long long)d` and of the initialization of `b` to `pow(2, 63)` invoke undefined behavior. A recent GCC warns at least for `b`'s initialization: overflow in implicit constant conversion [-Woverflow].” – Pascal Cuoq Oct 09 '13 at 09:17
  • I would suggest changing the “Because they were calculated in different ways” part of your answer too. The notion of conversion applied in `long long x = ;` is exactly the same as the notion of conversion in `(long long) `. They are not allowed to give different result unless undefined or unspecified behavior is involved. The fact that they give different results here because one was computed at compile-time and the other at run-time is a detail even if true. – Pascal Cuoq Oct 09 '13 at 09:23
  • @PascalCuoq : hope my edits are acceptable. Thanks for taking the time to improve this answer ! – Sander De Dycker Oct 09 '13 at 10:15
1

Because pow returns a double and double lost precisions. That's why a==b.

CS Pei
  • 10,869
  • 1
  • 27
  • 46
  • why (long long)c != a and (long long)d != b ? – tenos Oct 08 '13 at 14:28
  • Because you can see that their signs are different. If you don't count the first digit (which is the sign) they should be the same. – CS Pei Oct 08 '13 at 14:33
0

pow(2, 63) is equivalent to pow((double) 2, (double) 63).

Indeed, C++11 26.8 [c.math] paragraph 3 says that <cmath> provides the declaration of double pow(double, double) and paragraph 11 says that (emphasis mine)

  1. If any argument corresponding to a double parameter has type long double, then all arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any argument corresponding to a double parameter has type double or an integer type, then all arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arguments corresponding to double parameters are effectively cast to float.

Now, the literals 2 and 63 are ints, therefore, pow(2, 63) is equivalent to pow((double) 2, (double) 63). The returning type is then double which doesn't have 63 bits of precision required to "see" the difference between 2^63 and 2^63 - 1.

I recommend the reading of this post and the excelent answer by Howard Hinnant.

Community
  • 1
  • 1
Cassio Neri
  • 19,583
  • 7
  • 46
  • 68
0

long long -> %lld

long double ->%Lf

double -> %f

float -> %f

int -> %d

Read Chapter 15 in << POINTERS on C >> for more details.

ImaChinese
  • 14
  • 1
  • 7
  • 1) The program in the question is using `printf` properly. 2) Your suggestions are wrong for `printf`, where `%f` expects a `double` (that may result from default argument promotions). – Pascal Cuoq Oct 09 '13 at 11:05