7

Why modulus calculation of 2.4 by 0.8 returns 0.8? Shouldn't it return 0?

It happens both in PHP, via fmod, and JavaScript, via %.

Even a google research with this operation fails: http://www.google.com/search?q=2.4+mod+0.8

murilolobato
  • 349
  • 4
  • 14
  • 1
    it is even worse when i tried in chrome, it shows `0.7999999999999998` – Kaddath Jul 26 '17 at 14:10
  • 1
    you should really just multiply all numbers by 10 to the power of how many decimals you have – Luca Kiebel Jul 26 '17 at 14:15
  • 1
    It is because of how floating point numbers are represented in memory… btw `0.8*3 = 2.4000000000000004` see website http://0.30000000000000004.com for more details on this – Bogdan Savluk Jul 26 '17 at 14:17
  • @RiggsFolly Plus, the linked question is about C and C++, not PHP. – roberto06 Jul 26 '17 at 14:18
  • 2
    To be fair, I don't think language matters all too much (after all, OP is also asking about JavaScript). This seems to be about floating point math in combination with fmod in general, I guess? Ah, what do I know. Also see https://stackoverflow.com/questions/3966484/why-does-modulus-operator-return-fractional-number-in-javascript – domsson Jul 26 '17 at 14:21
  • @idmean Before we wrongly flag this as duplicate a second time, does that really answer why an expected result of `0.0` turns out as `0.8` or `0.7999999999999998`? I think a more in-depth explanation (example of the steps involved) might actually be warranted here? Not sure. – domsson Jul 26 '17 at 14:44
  • @domsson I think the 2nd answer there https://stackoverflow.com/a/3966533/1779477 does answer the question. Reading that I could exactly relieve where the rounding error occurs. – idmean Jul 26 '17 at 14:46
  • `bc` correctly returns 0 `bc -l <<< '2.4%0.8'` – arielnmz Jul 26 '17 at 16:38
  • Also tested in Python3 `>>> 2.4%0.8` returns `0.7999999999999998`, and `>>> import decimal as d >>> d.Decimal(2.4)%dDecimal(0.8)` returns `d.Decimal('0.7999999999999998223643160600')`, interestingly, `>>> d.Decimal('2.4')%dDecimal('0.8')` returns `Decimal('0.0')`. Note the `'`. – arielnmz Jul 26 '17 at 16:43

2 Answers2

0

Simply put, in order to represent a number in decimal we do something like this:

1550 = 1000 + 5·100 + 5·10 = 1·10³ + 5·10² + 5·10¹

In order to represent a number in binary form from decimal you have to change bases:

106 = 2^6 + 2^5 + 2^3 + 2^1 = 0110 1010 

Where 0's would equal to 0·2^7, 0·2^4, etc.

As you can see the pattern is clear: just sum powers of 2 as needed until you get the number you want until you reach 0 (2^0) for the number 1. If you take things a bit further it works the same for decimals, you just keep substracting 1 from the powers:

0.5 = 2^-1 = 1/2^1

Now for other numbers:

0.6875 = 0.1011 = 1/2^1 + 1/2^3 + 1/2^4

However, there are numbers that you can't represent as sums of inverse powers of two, something as simple as 0.1 should require you to add very small numbers to approach that value and would look like 0.0001100110011... which actually rounds up to 0.0999755859375..., and since the language has to change bases everytime you have to do arithmetics, these problems start to show up.

arielnmz
  • 8,354
  • 9
  • 38
  • 66
0

If you look closer at what happens in this concrete example, the influence of the binary format becomes more visible.

The input is stored as a binary floating point approximation. This then can be printed as decimal number uniquely identifying the binary,

In [1]: print "%.25f"%2.4
2.3999999999999999111821580

In [2]: print "%.25f"%0.8
0.8000000000000000444089210

The first step is the computation of the quotient and of its integer part. Obviously, as the stored numbers are smaller 2.4 and larger 0.8, the quotient is smaller than 3,

In [3]: print "%.25f"%(2.4/0.8)
2.9999999999999995559107901

The modulo result, the remainder, is then finally the difference after removing the integer multiple,

In [4]: print "%.25f"%(2.4-2*0.8)
0.7999999999999998223643161
Lutz Lehmann
  • 25,219
  • 2
  • 22
  • 51
  • That explains the `0.79999...` we see with some languages, but how does the `0.0` (!) come about that we see with, for example, PHP's `fmod()`? – domsson Jul 27 '17 at 18:55
  • This would depend on the algorithm used, for instance a different rounding mode for the quotient might give the more correct `3`. However, if the data type is standard double, then the `0.0` should actually be `-0.0000000000000004440892099`, which is negative and might violate remainder conventions. – Lutz Lehmann Jul 27 '17 at 19:13