2

I have a small code which does some number transformations. I want to turn a number from double to long and then using right bit shift to cast it to short. But it gives me different results and I don't know why.

I have 3 numbers in an array and I make the sum of them using a for loop and every time I am gonna cast the result to short.

There is a number with .000000007 more exactly 63897600.000000007. Adding this to the total and then subtracting it gives me different results.

I can't figure out why does this occur and how can I manage this particular case.

Here is my code:

#include <stdio.h>

#define DOUBLETOLONG(number)   (long)(number)
#define NEAREST(number)        ((short)((number + 32768) >> 16))
#define LONGTOSHORT(number)    NEAREST(DOUBLETOLONG(number))

int main() {
    int k = 0;
    double array[3] ={ 41451520.000000, 63897600.000000007, -63897600.000000007 };
    double total_x = array[0];
    short j = LONGTOSHORT(total_x);

    printf("j = %d\n", j);

    for (k = 1; k < 3; k++) {
        total_x = total_x+array[k];
        j = LONGTOSHORT(total_x);
        printf("j = %d\n", j);
    }
    return 0;
}

This are the results:

j = 633
j = 1608
j = 632
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 3
    Dealing with binary floating point can be frustrating. Do study this carefully: http://stackoverflow.com/questions/588004/is-floating-point-math-broken – Bathsheba Sep 20 '16 at 07:44
  • 1
    If you want to cast a `long` to a `short` you shouldn't shift it, just use `(short)number` – tversteeg Sep 20 '16 at 07:45
  • 2
    Why are you obfuscating the code? – 2501 Sep 20 '16 at 07:47
  • It's necessary to shift it (this is a request from the teacher). – Andrei Cristian Sep 20 '16 at 07:48
  • Also take a look at http://en.cppreference.com/w/c/numeric/math/nextafter. It's a C++ file, but the functions also exist in C99. – Bathsheba Sep 20 '16 at 07:50
  • @AndreiCristian right shifting a long 16 times will get you the Most significant bytes. What you get in typecasting to short is the least significant 2 bytes. Which do you want? – Rishikesh Raje Sep 20 '16 at 07:59
  • More exactly i want to have the same number as i had in 1st operation. I don't know hot to manage to have 633 all the time instead of 632 – Andrei Cristian Sep 20 '16 at 08:01
  • If you have warnings enabled on your compiler, you should be receiving warning for violations of the *strict aliasing* rule. You may want to look at an `unsigned long/double union` to properly handle reading the `double` as an `unsigned long` and vice versa. – David C. Rankin Sep 20 '16 at 08:08
  • @AndreiCristian: That's because using decimal notation is not a precise way of representing binary floating point numbers. See the link I pasted up in the first comment. – Bathsheba Sep 20 '16 at 08:12
  • To _round_ a `double()`, `` provides `rint()`, `round()`, `nearbyint()`. Suggest incorporating into your solution. – chux - Reinstate Monica Sep 20 '16 at 13:02

1 Answers1

2

41451520 + 63897600 = 105349120

In a double this integer can still be accurately represented. However, we didn't account for the fractional part 0.000000007. Let's check what the next biggest double is:

#include <stdio.h>
#include <math.h>

int main(int argc, char** argv) {
    printf("%.23f\n", nextafter(105349120.0, INFINITY));
    return 0;
}

Turns out, it's 105349120.000000014901.... Let's put those next to eachother:

105349120.000000014901...
        0.000000007

This means that 105349120.000000007 is closer to 105349120 than the next bigger double, so it correctly gets rounded down to 105349120.

However, when we subtract again, 105349120 - 63897600.000000007 gets rounded down, because the next smaller double than 41451520 is (nextafter(41451520.0, 0)) 41451519.999999992549.... Put them next to eachother:

41451519.999999992549...
41451519.999999993

Yep, closer to the first double below 41451520 than 41451520 itself. So it correctly gets rounded down to 41451519.999999992549....

When you convert 41451519.999999992549... to an integer it floors the number, resulting in one less than what you expect.


Floating point math is full of surprises. You should read What Every Computer Scientist Should Know About Floating-Point Arithmetic, but perhaps it's still too advanced for now. But it's important to be aware that yes, floating point is full of surprises, but no it isn't magic, and you can learn the pitfalls.

orlp
  • 112,504
  • 36
  • 218
  • 315