Arithmetic operations on floating point numbers giving unexpected results

Viewed 181

I know that with binary representation it is not possible to exactly represent a floating-point number (and I also understand why 0.1 + 0.2 == 0.3 is false). Now here is where I got stuck while I tried to experiment with different cases of floating arithmetics:

Looking at the way decimal numbers are converted to binary format, I concluded that two numbers that have the same fractional part in decimal representation (eg: 0.3 and 1.3) will have the same fractional part once converted to binary form.

To test this out I tried the following codes in python:

print(f"{0.3:.20f}")
print(f"{1.3:.20f}")

0.29999999999999998890
1.30000000000000004441

I couldn't understand why the fractional parts were coming out to be different, so to understand the situation better I tried this:

  print(f"{0.3:.20f}")
  print(f"{1 + 0.3:.20f}")
  print(f"{1.3:.20f}")

  0.29999999999999998890
  1.30000000000000004441
  1.30000000000000004441

Question: Since 1 is not an approximate number (as it is possible to represent 1 in exact binary form as 2^0) so why does adding 1 change the fractional part of the number?

Furthermore, when we subtract 1 from 1.3 why is the resulting value not equal to 0.3

print(f"{1.3:.20f}")
print(f"{1.3 - 1:.20f}")
print(f"{0.3:.20f}")

1.30000000000000004441
0.30000000000000004441
0.29999999999999998890

My entire problem can be summarized in the following juxtaposition:

print(1 + .3 == 1.3)
print(0.3 == 1.3 -1)

True
False
1 Answers

exactly represent a floating-point

These floating-point numbers take up a fixed amount of space, 64-bits. That allows about 264 different values to be exactly encoded. 0.3, 1.3 are not in that set.

Each finite encoded value is an integer * power-of-2. So 0.3, 1.3 are not exactly saved as they cannot be scaled to an "integer * power-of-2". Instead nearby values are used:

5404319552844595*pow(2,-54): 0.299999999999999988897... (closest)
                             0.3                        (not encodable)
5404319552844596*pow(2,-54): 0.300000000000000044408... (next closest)

5854679515581644*pow(2,-52): 1.299999999999999822364... (next closest) 
                             1.3                        (not encodable)
5854679515581645*pow(2,-52): 1.300000000000000044408... (closest)

0.3 == 1.3 -1 is better viewed as 0.299999999999999988897... == 1.300000000000000044408... - 1.0.

Subtracting 1.0 from 1.300000000000000044408... is exact: 0.300000000000000044408... and is clearly not equal to 0.299999999999999988897... .


when I tried 1.3 == 1 + 0.3 that came out to be true, this is what confused me

This is like 1.300000000000000044408... = 1.0 + 0.299999999999999988897.... The sum 1.299999999999999988897... is not representable as a floating point as there is a limit to the number of bits used to encode the integer in integer * power-of-2. The closest representable value is used as the sum.

5854679515581644   *pow(2,-52): 1.299999999999999822364 (next closest) 
5854679515581644.75*pow(2,-52): 1.299999999999999988897 (not encodable)
5854679515581645   *pow(2,-52): 1.300000000000000044408 (closer)
Related