3

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
E_net4
  • 27,810
  • 13
  • 101
  • 139
BOT_bkcd
  • 321
  • 3
  • 12
  • 3
    @KlausD. I don't think it's a dupe. The OP seems to understand what binary, floating point representation is and its limitations. They are asking about this specific phenomena – juanpa.arrivillaga Jun 12 '21 at 19:31
  • @KlausD No, I have read that answer & as I have mentioned above I understand the limitations of floating point precision. But my question is a little different as you can see above. – BOT_bkcd Jun 12 '21 at 19:33
  • That question contains so man comments and links in them. There's nothing related to your problem? – Klaus D. Jun 12 '21 at 19:46
  • 2
    Re: "why does adding 1 change the fractional part of the number?" Rounding. – njuffa Jun 12 '21 at 19:50
  • 1
    @njuffa but when I subtract 1 from 1.3 why is it not equal to 0.3? – BOT_bkcd Jun 12 '21 at 19:52
  • 1
    @njuffa Then here why is it that `1 + .3 == 1.3` is `True`? – j1-lee Jun 12 '21 at 19:53
  • 2
    It's because of the different rounding cutoff, due to the use of the corresponding standard like IEEE_754, as described in the linked (duplicate) question. In other words, there is a procedure that converts the fraction to rounded decimals, that is not consistent over fractions with the different integer parts but same decimal parts, as you noticed with 1.3 and 0.3, the .3 in x.3 is rounded differently depending on x. – Vepir Jun 12 '21 at 20:03
  • 2
    @j1-lee Whether rounding causes the result of a finite-precision floating-point operation to be different from the naively expected result depends on the specifics of the operands, for example, their relative magnitude (for specific examples, see Sterbenz Lemma, subtractive cancellation). Sometimes the result matches the naive expectation, sometimes it doesn't. To get a handle on this, I would recommend simulating the binary arithmetic operation in question by hand, and all should become clear. – njuffa Jun 12 '21 at 20:04
  • 1
    @Vepir that explains why (1.3 - 1) != 0.3, but can you please explain why (1 + 0.3) == 1.3 even though the integral parts are different. Shouldn't they be rounded differently? – BOT_bkcd Jun 12 '21 at 20:11
  • 1
    @Vepir Thanks, I wanted this kind of answer. The link might contain the point you mentioned, but as an untrained/nonprofessional person, I couldn't find that explanation there. – j1-lee Jun 12 '21 at 20:11
  • @j1-lee if you understood how it works can you explain why 1 + 0.3 == 1.3 even though they have different integral parts so shouldn't they should be rounded differently? – BOT_bkcd Jun 12 '21 at 20:32
  • 4
    @Gravity It's because of what njuffa explains in their comment above, *"Sometimes the result matches the naive expectation, sometimes it doesn't."*; The arithmetic operations on floats are not the same as naive arithmetic operations you would expect. You can use [ecs.umass.edu/ece/koren/arith/simulator/FPAdd/](http://www.ecs.umass.edu/ece/koren/arith/simulator/FPAdd/) for addition and subtraction, [h-schmidt.net/FloatConverter/IEEE754.html](https://www.h-schmidt.net/FloatConverter/IEEE754.html) for conversion. Indeed, I get [0.3 + 1.0 == 1.3, 1.3 - 1.0 != 0.3](https://pastebin.com/UtrmbiaM). – Vepir Jun 12 '21 at 20:50
  • @Vepir Thanks a lot, that was really helpful – BOT_bkcd Jun 13 '21 at 04:19

1 Answers1

0

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)
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 0.3 != 1.3 -1 made sense to me but when I tried 1.3 == 1 + 0.3 that came out to be true, this is what confused me – BOT_bkcd Jun 13 '21 at 12:57