0

This is astounding that this happens and I don't have an explanation. A simple console application as created by visual studio:

#include <stdlib.h>
#include <crtdbg.h>

#include "stdafx.h"
#include "ConsoleApplication.h"

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    double a;
    a = 3.4;

    return 0; // <=======  Debug break point set here
}

The value of a is 3.3999999999999999 instead of 3.4. To most programmers this isn't an issue, but why can't the number 3.4 be stored exactly as a double? Thinking about the binary it doesn't make sense.

quetzalcoatl
  • 32,194
  • 8
  • 68
  • 107
Russell Trahan
  • 783
  • 4
  • 34
  • 3
    https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html – OldProgrammer Nov 14 '14 at 00:26
  • 1
    Well there's no appropriate dupe available on SO, but you should get off with all the information [applied here](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). – πάντα ῥεῖ Nov 14 '14 at 00:26
  • 2
    If you want exact, look for a fixed-point number class. – Neil Kirk Nov 14 '14 at 00:26
  • I understand numerical precision (I think). As an engineer I deal with it regularly. I just don't understand why 34 * 10^-1 can't be represented by the float format exactly. I'm not doing any floating point operations here or stretching the precision to the limit. – Russell Trahan Nov 14 '14 at 00:31
  • 1
    duplicate of oh so many questions... (just trying to find one that specifically refers to C++ instead of just IEEE 754) – Alnitak Nov 14 '14 at 00:39

2 Answers2

6

So to represent 0.4 in a binary representation, you look down fractions of powers of two.

Initially, you say,

    1/2 = 0.5.   Too big.         -> 0/2
    1/4 = 0.25.  Great.           -> 0/2 + 1/4       => 0.25
    1/8 = 0.125. We can add this. -> 0/2 + 1/4 + 1/8 => 0.375.

This continues (1/16, 1/32 are too large to add, but 1/64 is good), and we get the repeating pattern (in binary representation of summed fractions)

0.011001100110011001100....

Which increasingly approximates 0.4, but never quite reaches it.

See this python link for more simple explanation or examples.

Link to wikipedia page on IEEE Floating Point

Highlight (float/double are base/radix 2):

Finite numbers, which may be either base 2 (binary) or base 10 (decimal). Each finite number is described by three integers:

s = a sign (zero or one),

c = a significand (or 'coefficient'),

q = an exponent.

The numerical value of a finite number is

(−1)^s × c × b^q

where b is the base (2 or 10), also called radix. For example, if the base is 10, the sign is 1 (indicating negative), the significand is 12345, and the exponent is −3, then the value of the number is −12.345.

Link to IEEE standard

Community
  • 1
  • 1
chrisb2244
  • 2,940
  • 22
  • 44
  • the problem is not the 0.4 per se, since floating point knows nothing about decimal and there will be other numbers ending in 0.4 that _are_ exactly representible. The problem is that there's no possible value of `mantissa * pow(2.0, exponent)` that equals 3.4 – Alnitak Nov 14 '14 at 00:40
  • Ah. I had no idea that a double was base 2. I thought it was `mantissa * pow(10.0, exponent)`. That explains it then. – Russell Trahan Nov 14 '14 at 00:42
  • I will mark this as the answer but it seems that a lot of documentation about IEEE floating point formats hides the fact that is it base 2. I know the mantissa and exponent are just integers, but the 2.0 is never written. – Russell Trahan Nov 14 '14 at 00:43
  • 4
    Almost every typical **binary** representation in base 2.. Or 4, 8, 16 (common), etc. It's quite hard to operate in bases other than 2^N when your most-low-level-datatype is a bit with values 0-or-1 – quetzalcoatl Nov 14 '14 at 00:43
  • Binary doesn't necessitate base 2... Just saying. Math... – Russell Trahan Nov 14 '14 at 00:45
  • 2
    I want to be nice, but if you google "binary", you'll probably find that's not true :) – chrisb2244 Nov 14 '14 at 00:45
  • @RussellTrahan it's more kinda _log base 2_, not _base 2_. – Alnitak Nov 14 '14 at 00:45
  • Ok. Got it. I never made the distinction between binary64 (usually called double) and decimal 64 as being base 2 vs 10. I've used them both a lot but never realized the base was different. Makes sense. – Russell Trahan Nov 14 '14 at 00:57
0

The problem is that there's no exact representation of 3.4 in the IEEE 754 format that uses expressions in the form mantissa * pow(2.0, exponent).

The 0.4 itself isn't strictly the problem, since it's the entire number when expressed in powers of two that needs to be representable, not just the part after the "decimal" point.

Alnitak
  • 334,560
  • 70
  • 407
  • 495