5

I am trying to calculate (6.6 % 1.1). I expect this to be 0, but I get 1.0999999999999996. You can easily reproduce this in the javascript console. My guess its an interal rounding bug? How do you solve this issue?

evaenrique
  • 981
  • 2
  • 8
  • 17
  • 8
    To answer the real question: You should not use the modulo operator on non-integer numbers. The problem is that `6.6 / 1.1` is not `6.0` but `5.999999999999999`, which will then be rounded down to `5` instead of `6`, so the difference is `6.6 - 5.0 * 1.1 = 1.1` instead of `6.6 - 6.0 * 1.1 = 0`. Do you really need the modulo operator? Usually, it is better to avoid it for non-natural numbers (or at least integers). – Tobias Jun 06 '16 at 16:22
  • Well, it's fine to use `%` on non-integers; it's got well-defined semantics. The problem is that most people would never guess what those semantics are without looking it up, and even then figuring out *why* they're like that is kind-of challenging. – Pointy Jun 06 '16 at 16:26
  • Consider using https://github.com/MikeMcl/bignumber.js/ – Gilad Artzi Jun 06 '16 at 16:33

3 Answers3

5

The % operator acts on non-integer values in a way that's not exactly obvious. In an expression

n % d

what JavaScript does is find the biggest integer (I'll call it q to mirror the spec) that's less than n / d. It then computes the product d * q, and the result is the difference n - (d * q).

(I've ignored the sign issue for simplicity.)

The idea makes sense of you think about integer remainders: if n and d are integers, then the formulas above make perfect sense. Take 17 % 3. Well 17 / 3 is 5.something, so q is 5 and the remainder is 17 - (5 * 3) or 2.

In your case, that process is made a little confusing by the fact that, with standard IEEE 754 floating point, 6.6 / 1.1 is not 6; due to precision with representing decimal floating point values with binary floating point, it's just a tiny bit less than 6. Thus, when q is calculated, it comes out to 5 instead of 6. The answer is thus 6 - (5 * 1.1), or 6 - 5.5, which is (again, imprecision) just a tiny bit less than 1.1.

Pointy
  • 405,095
  • 59
  • 585
  • 614
1

JavaScript use IEEE double-precision to store floating point numbers, so 6.6 is 6.5999999 (I guess?). You can use

(66 % 11)/10

to avoid errors.

Duy Tran
  • 311
  • 1
  • 8
0

As the others have noted, you're seeing rounding effects from IEEE-754 floating point numbers. This isn't unique to JavaScript:

$ python
>>> 6.6 % 1.1
1.0999999999999992

The way to deal with float problems is to use an arbitrary precision math library:

For example, with big.js:

x = new Big(6.6)
// Big {s: 1, e: 0, c: Array[2]}
y = new Big(1.1)
// Big {s: 1, e: 0, c: Array[2]}
x.mod(y).toString()
// "0"
azza-bazoo
  • 21
  • 4