# Error in binary representation (IEEE 754)

**URL:** <https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166>\
**Category:** Factual Questions\
**Created:** [January 18, 2014, 9:08pm UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166 "2014-01-18T21:08:23Z")\
**Posts on this page:** 13\
**Page:** 2

<div class="post-metadata">

**Author:** ![Dr.Strangelove](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/dr.strangelove/32/6613_2.png) [@Dr.Strangelove](https://boards.straightdope.com/u/Dr.Strangelove)\
**Post date:** [January 20, 2014, 2:22am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/21 "2014-01-20T02:22:14Z")

</div>

> [@septimus](#):
>
> A less-known “gotcha” is that equality tests may fail _even when the exact same computation sequence is used to derive the comparands!_ This can arise on Pentium where, IIRC, floating-point values are stored as 8 bytes typically but registers are higher-precision, use 10 bytes, and thus may not compare as equal with their own stored values!

A similar thing that has bitten people many times in my industry: certain computational architectures support a fused-multiply-add instruction that computes A\*B+C in one shot. Depending on exactly how the code got optimized, that sequence may end up getting fused or not, which alters the rounding behavior and thus the final result. Even within the same program there could be differences here.

---

<div class="post-metadata">

**Author:** ![Senegoid](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/senegoid/32/6606_2.png) [@Senegoid](https://boards.straightdope.com/u/Senegoid)\
**Post date:** [January 20, 2014, 2:28am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/22 "2014-01-20T02:28:34Z")

</div>

> [@Jragon](#):
>
> That program will actually work depending on your compiler or language spec. The program
> 
> one := 1.0  
> addedOne := .1 + .1 + .1 + .1 +… // Pretend this is 10 .1s
> 
> Will often work correctly because of some idiosyncracies of how certain compilers evaluate constants at compile-time. You can actually often tell the extent of constant compile-time variable anaysis depending on whether the program
> 
> tenth := .1  
> one := 1.0  
> addedOne := tenth + // added 10 times

Yeah, I wrote it the way I did in the hope of defeating, as much as possible, some attempts at optimization that some compilers might do.

> [@](#):
>
> Using a for loop to add .1 10 times usually more reliably results in a wrong answer, though perhaps amusingly JIT-compiled languages that do optimization during runtime may unroll the loop after 2-3 executions and the function will end up returning a different value in later calls during an execution than it does the first few calls of an execution. (Java does this sort of thing often)

Gaak! Whatever kind of optimization a compiler/interpreter may do, I’d at least hope it would be more consistent (and hence, predictable) than that. We’d like to hang onto our fantasy that computers are deterministic, but this calls even that into question!

---

<div class="post-metadata">

**Author:** ![Jragon](https://avatars.discourse-cdn.com/v4/letter/j/e19b73/32.png) [@Jragon](https://boards.straightdope.com/u/Jragon)\
**Post date:** [January 20, 2014, 3:01am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/23 "2014-01-20T03:01:22Z")

</div>

> [@Senegoid](#):
>
> Gaak! Whatever kind of optimization a compiler/interpreter may do, I’d at least hope it would be more consistent (and hence, predictable) than that. We’d like to hang onto our fantasy that computers are deterministic, but this calls even that into question!

Dude, that’s not even the half of it. This was a performance bug rather than a results bug, but the JIT once unrolled an O(n^2) loop for me that was taking forever. But it only unrolled it on Windows machines – and if it was a laptop, it would only do it if it was running on outlet power and not battery. Needless to say I was very confused when my groupmates on other systems yelled at me for making the game unplayable.

Edit:

And as for scaled integers. You absolutely have to remember the multiply-before-divide rule if you use it.

92 \* (1/92) =/= (92\*1)/92

in integer arithmetic.

---

<div class="post-metadata">

**Author:** ![Isilder](https://avatars.discourse-cdn.com/v4/letter/i/cc9497/32.png) [@Isilder](https://boards.straightdope.com/u/Isilder)\
**Post date:** [January 20, 2014, 3:58am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/24 "2014-01-20T03:58:28Z")

</div>

> [@Jragon](#):
>
> So Excel CAN do what you ask, you just have to dive into a few menus!

can it ? it can only show values in decimal,  
but the ‘\>’ test is being done in binary.

---

<div class="post-metadata">

**Author:** ![Jragon](https://avatars.discourse-cdn.com/v4/letter/j/e19b73/32.png) [@Jragon](https://boards.straightdope.com/u/Jragon)\
**Post date:** [January 20, 2014, 4:03am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/25 "2014-01-20T04:03:42Z")

</div>

> [@Isilder](#):
>
> can it ? it can only show values in decimal,  
> but the ‘\>’ test is being done in binary.

Not necessarily, if you enable that option it’s possible they use a decimal format or some other trick. This could either be a tedious string compare, or a Rational type. Note that when you enable this option, you have to pick a decimal level of precision, consider

type Rat struct {  
Numerator, Denominator int  
}

If I have 2.53, I can express this as Rat{253, 100}, both of which can be expressed in binary with perfect precision. While it’s a bit more costly, both in terms of memory and performance, you can have perfect equivalence and ordering (\<, \>) tests on this type, using the same methods you learned for adding fractions of unlike denominators in grade school. In fact, it should be relatively cheap since all numbers should have the same Denominator component (in this case, 100), the only relevant bit is the ordering of numerators. Though whether the denominators stay 100 depends, I suppose, on how you handle division.

---

<div class="post-metadata">

**Author:** ![Senegoid](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/senegoid/32/6606_2.png) [@Senegoid](https://boards.straightdope.com/u/Senegoid)\
**Post date:** [January 20, 2014, 7:08am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/26 "2014-01-20T07:08:04Z")

</div>

> [@Jragon](#):
>
> Dude, that’s not even the half of it. This was a performance bug rather than a results bug, but the JIT once unrolled an O(n^2) loop for me that was taking forever. But it only unrolled it on Windows machines – and if it was a laptop, it would only do it if it was running on outlet power and not battery. Needless to say I was very confused when my groupmates on other systems yelled at me for making the game unplayable.

What kind of internal criteria do you suppose the JIT was using to decide when to do that and when not to?

---

<div class="post-metadata">

**Author:** ![Jragon](https://avatars.discourse-cdn.com/v4/letter/j/e19b73/32.png) [@Jragon](https://boards.straightdope.com/u/Jragon)\
**Post date:** [January 20, 2014, 7:11am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/27 "2014-01-20T07:11:43Z")

</div>

> [@Senegoid](#):
>
> What kind of internal criteria do you suppose the JIT was using to decide when to do that and when not to?

It’s not a big secret that the JVM is better optimized on Windows than other platforms, I assume the logic simply didn’t exist on the OS X and Linux implementations. As for the plugged in/not plugged in thing, I assume it has something to do with clock speed, Windows laptops have features that underclock your components when running off battery power so that the battery will last longer. I assume the JVM feels it can’t unroll loops or do other JIT optimizations if the clock isn’t fast enough because it will slow down the program unacceptably, so when it’s underclocked by Windows it won’t unroll the loop because the clock is under the acceptable level.

---

<div class="post-metadata">

**Author:** ![Senegoid](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/senegoid/32/6606_2.png) [@Senegoid](https://boards.straightdope.com/u/Senegoid)\
**Post date:** [January 20, 2014, 7:16am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/28 "2014-01-20T07:16:34Z")

</div>

> [@Jragon](#):
>
> And as for scaled integers. You absolutely have to remember the multiply-before-divide rule if you use it.
> 
> 92 \* (1/92) =/= (92\*1)/92
> 
> in integer arithmetic.

Gotta be careful! You could sum up:

```auto

int sum = 0 ;
for ( int i = 1 ; i < ∞ ; i++ ) {
    sum = sum + i ;
}

```

and, if the compiler optimized stupidly, [you could end up with sum = -1/12](http://boards.straightdope.com/sdmb/showthread.php?t=712713) 😛

(But I suppose no matter how the compiler does or doesn’t optimize it, it would take O(∞) time. 🙂 )

---

<div class="post-metadata">

**Author:** ![septimus](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/septimus/32/410_2.png) [@septimus](https://boards.straightdope.com/u/septimus)\
**Post date:** [January 20, 2014, 7:57am UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/29 "2014-01-20T07:57:19Z")

</div>

BTW, the [Kahan summation algorithm](http://en.wikipedia.org/wiki/Kahan_summation_algorithm) may be worth knowing, for programmers who lose precision during very long summations. I do very little floating-point work myself, so have seldom if ever needed it. (I use it sometimes anyway, just as a fetish :rolleyes: ) If there’s interest, I’ll post demonstration C code.

No … I’ll just post demonstration code despite total lack of interest. 🆒

```auto

/*
 * This is a demonstration of floating-point addition.
 * We sum 1 + 1/2 + 1/3 + ... + 1/100000000 four different ways.
 * (a) sum ordinarily, largest addends first
 * (b) sum ordinarily, smallest addends first
 * (c) sum with error carries, per William Kahan
 * (d) predict the correct sum, using Euler's formula
 *
 * To emphasize summation precision problems, we use 'float' sums
 * rather than 'double' sums.
 */
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

typedef float Real;

struct Floatsum {
        Real sum, carry;
};

void usual_add(double addend, struct Floatsum *kp)
{
        kp->sum += addend;
}

void kahan_add(double addend, struct Floatsum *kp)
{
        Real y = addend - kp->carry;
        Real tmp = kp->sum + y;
        kp->carry = (tmp - kp->sum) - y;
        kp->sum = tmp;
}

main(argc, argv)
        char **argv;
{
        struct Floatsum J;
        int k, Many = 100000000;

        /* Sum the first Many reciprocals. */
        /* naive addition */
        J.sum = 0;
        for (k = 1; k <= Many; k++)
                usual_add(1.0 / k, &J);
        printf("%f
", J.sum);

        /* small-to-large addition */
        J.sum = 0;
        for (k = Many; k >= 1; k--)
                usual_add(1.0 / k, &J);
        printf("%f
", J.sum);

        /* Do Kahan adds */
        J.sum = J.carry = 0;
        for (k = 1; k <= Many; k++)
                kahan_add(1.0 / k, &J);
        printf("%f
", J.sum);

        /* Euler's formula */
        printf("%f
", log(Many + 0.0) + 0.57721566 + 0.5 / Many);
}

```

---

<div class="post-metadata">

**Author:** ![Chronos](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/chronos/32/134_2.png) [@Chronos](https://boards.straightdope.com/u/Chronos)\
**Post date:** [January 20, 2014, 3:29pm UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/30 "2014-01-20T15:29:17Z")

</div>

I’m not familiar with the arrow operator used there. Is that the same thing as the dot used in structs?

---

<div class="post-metadata">

**Author:** ![chrisk](https://avatars.discourse-cdn.com/v4/letter/c/6de8d8/32.png) [@chrisk](https://boards.straightdope.com/u/chrisk)\
**Post date:** [January 20, 2014, 3:37pm UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/31 "2014-01-20T15:37:00Z")

</div>

> [@Chronos](#):
>
> I’m not familiar with the arrow operator used there. Is that the same thing as the dot used in structs?

Equivalent. If you’ve got a pointer to a struct, then you use the -\> operator to say 'get me the member of the struct this points to.) Like you use the dot for a member of an ordinary struct variable.

---

<div class="post-metadata">

**Author:** ![septimus](https://sea3.discourse-cdn.com/straightdope/user_avatar/boards.straightdope.com/septimus/32/410_2.png) [@septimus](https://boards.straightdope.com/u/septimus)\
**Post date:** [January 20, 2014, 5:13pm UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/32 "2014-01-20T17:13:01Z")

</div>

The operator ’ -\> ’ is an unnecessary frill in C. The expression  
P-\>B  
is simply shorthand for  
(\*P).B

It provides a “smoother-looking” way to follow pointer chains. For example, here’s a line from the Linux kernel inode code:  
bdi = sb-\>s\_bdev-\>bd\_inode-\>i\_mapping-\>backing\_dev\_info;

---

<div class="post-metadata">

**Author:** ![RaftPeople](https://avatars.discourse-cdn.com/v4/letter/r/6f9a4e/32.png) [@RaftPeople](https://boards.straightdope.com/u/RaftPeople)\
**Post date:** [January 21, 2014, 3:59pm UTC](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166/33 "2014-01-21T15:59:45Z")

</div>

> [@Chronos](#):
>
> Personally, if I were writing the cash-register software, I’d make it so any purchase over $19.995 triggered the “at least $20” test, since it’d be rare indeed for any rounding error to be larger than half a cent, and those are the values that would (for what the customer sees) appear to be correct.

If you were writing cash-register software or any kind of business or accounting software you would probably do what the entire industry does and that is use decimal/numeric data types instead of floating point.

[Previous page](https://boards.straightdope.com/t/error-in-binary-representation-ieee-754/679166.md?page=1)
