0

I have some code:

num1 = 1.001
num2 = 0.001
sum  = num1 + num2
puts sum

I expected 1.002000 but I am getting 1.0019999999999998. Why is this the case?

Milo P
  • 1,377
  • 2
  • 31
  • 40
Nodir Nasirov
  • 1,488
  • 3
  • 26
  • 44

2 Answers2

0

This is a commonly-found, fundamental problem with binary representation of fractional values, and is not Ruby-specific.

Because of the way that floating-point numbers are implemented as binary values, there are sometimes noticeable discrepancies between what you'd expect with "normal" decimal math and what actually results. Ruby's default representation of floating-point numbers is no exception--since it is based on the industry-standard double-precision (IEEE 754) format, it internally represents non-integers as binary values, and so its approximations don't quite line up with decimal values.

If you need to do decimal calculations in Ruby, consider using BigDecimal (documentation):

require 'bigdecimal'
num1 = BigDecimal.new("1.001")
num2 = BigDecimal.new("0.001")
puts num1 + num2 #=> 0.1002E1
Milo P
  • 1,377
  • 2
  • 31
  • 40
  • It's a problem with binary arithmetic, not languages. You can no more represent 0.1 exactly in binary than you can 1/3 in decimal. Any decimal is a bad recommendation for Money. Better to recommend a true class that does not depend on floating point numbers, big or small. – duffymo Mar 16 '16 at 20:51
  • @duffymo Strictly speaking, yes, but for the OP's purposes, the proximate cause is that Ruby by default represents a value like `1.001` as an instance of Class: Float ("inexact real numbers using the native architecture's double-precision floating point representation", according to the docs). I mentioned the bit about this being the case in most languages as a direct response to the OP's apparent assumption that this is a Ruby-specific issue. Point taken with money, though. – Milo P Mar 16 '16 at 21:09
  • It's not Ruby; it's binary numbers. You're representing floating point numbers using the IEEE 754 standard with a finite width. True of all languages, not just Ruby. – duffymo Mar 16 '16 at 23:09
  • @duffymo Again, I understand that, I'm responding to the OP's (apparently inexperienced) characterization of the problem as a Ruby issue. ("Why does this happen in Ruby?" "This happens in most languages, including Ruby, and here's why.") It's not *strictly* true that the IEEE 754 representation is a necessary feature of all languages--for example, some language might use something like dec64 by default--but close enough. I've edited my wording so that it's hopefully abundantly clear. – Milo P Mar 17 '16 at 00:06
  • Which languages use dec64? I've never heard of that spec. Does dec refer to Digital Equipment Corporation? I think it's important to make clear to the OP that this is much more than a Ruby problem. – duffymo Mar 17 '16 at 00:12
  • @duffymo "This is a common problem with binary representation of fractional values, and is not Ruby-specific" is the clearest, most accessible way I can think to convey this. If you can think of a better way to put it, please feel free to edit my answer or add your own. – Milo P Mar 17 '16 at 00:22
  • My comments said it first and better. You've only improved what was a poor answer by my prodding. – duffymo Mar 17 '16 at 00:23
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/106523/discussion-between-milo-price-and-duffymo). – Milo P Mar 17 '16 at 00:36
  • Let's not. I'm not interested. – duffymo Mar 17 '16 at 01:18
  • 1
    Although the problem occurs in many languages, this answer offers not only am explanation but a ruby solution to avoid the problem. – chux - Reinstate Monica Mar 20 '16 at 00:24
-1

In a perfect world yes you expected that 1.002000, but you have an error due to rounding in floating point arithmetic operation, you can check on the web machine epsilon or just floating point. For example, you can calculate the relative error like that, and the error machine for the ruby language is 1e-15

f = 0.0
100.times { f += 0.1 }
p f                            #=> 9.99999999999998       # should be 10.0 in the ideal world.
p 10-f                         #=> 1.9539925233402755e-14 # the floating-point error.
daday001
  • 91
  • 1
  • 9