Unable to understand the subtraction result - ruby

I executed the following line in Ruby (Aptana IDE)
puts 3.3 - 2.7 == 0.6 #which should be true
and I got the result as
false
Then I executed
puts 3.3 - 2.7
and got the result as
0.5999999999999996
Can anybody please explain about whats going on? Why I got 0.5999999999999996 instead of 0.6?

Floating-point numbers cannot precisely represent all real numbers, and floating-point operations cannot precisely represent true arithmetic operations, this leads to many surprising situations.
I advise to read: https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems
The right way to handle this in Ruby is to use the BigDecimal class
> require 'bigdecimal'
true
> a = BigDecimal.new('3.3')
3.3
> b = BigDecimal.new('2.7')
2.7
> c = BigDecimal.new('0.6')
0.6
> a - b == c
true

It's floating point math. Computers can't represent some values exactly. You can use Ruby's String format to get a string version:
'%0.1f' % (3.3 - 2.7) # => "0.6"
Or adjust your math to multiply your values by the precision you want, then remove that offset:
(3.3 * 10 - 2.7 * 10) / 10 # => 0.6

The question you should ask yourself (or yourinterpreter) is
how is 0.6 different from 0.5999999999999996 in my machine?
and the answer is that it is not different. Floating point number 0.5999999999999996 is 0.6 in your machine because the precision your interpreter is using for floating point numbers can not distinguish the two.
If this representation causes problems you can use explicit rounding Float#round
(3.3 - 2.7).round(1)
#=> 0.6
or special formatting (check Kernel#sprintf for more opts)
"%0.1f" % (3.3 - 2.7)
#=> "0.6"

Related

Float Rounding Changes in Ruby 2.4

Ruby 2.4 uses Gaussian rounding to round off floating point numbers.
According to Wikipedia:
A tie-breaking rule that is less biased (even when the original numbers are positive or negative with unequal probability) is round half to even. By this convention, if the fraction of y is 0.5, then q is the even integer nearest to y. Thus, for example, +23.5 becomes +24, as does +24.5; while −23.5 becomes −24, as does −24.5.
However, executing the following code in Ruby 2.4 produces a different output than what is expected.
[1.5, 2.5, 3.5, 4.5, 5.5].each { | num | puts num.round }
# output:
2
3
4
5
6
# expected output(based on Gaussian rounding):
2
2
4
4
6
Can someone explain why is this so or what am I missing?
In order to apply Gaussian rounding, you have to pass the keyword argument :half.
The keyword argument :half can take either :down or :even and the default behavior is still to round up, just as it was before.
# ruby 2.4.0-rc1
irb(main):001:0> (2.5).round
# => 3
irb(main):008:0> (2.5).round(half: :down)
# => 2
irb(main):009:0> (2.5).round(half: :even)
# => 2
The background to this decision is in this blog post.

multiplying floating point numbers produces zero

the code below outputs 0.0. is this because of the overflow? how to avoid it? if not, why?
p ((1..100000).map {rand}).reduce :*
I was hoping to speed up this code:
p r.reduce(0) {|m, v| m + (Math.log10 v)}
and use this instead:
p Math.log10 (r.reduce :*)
but apparently this is not always possible...
The values produced by rand are all between 0.0 and 1.0. This means that on each multiplication, your number gets smaller. So by the time you have multiplied 1000 of them, it is probably indistinguishable from 0.
At some point, ruby will take your number to be so small that it is 0. for instance: 2.0e-1000 # => 0
Every multiplication reduces your number by about 1/21, so after about 50 of them, you are down 1/250, and after 100000 (actually, after about 700) you have underflowed the FP format itself, see here.
Ruby provides the BigDecimal class, which implements accurate floating point arithmetic.
require 'bigdecimal'
n = 100
decimals = n.times.map { BigDecimal.new rand.to_s }
result = decimals.reduce :*
result.nonzero?.nil? # returns nil if zero, self otherwise
# => false
result.precs # [significant_digits, maximum_significant_digits]
# => [1575, 1764]
Math.log10 result
# => -46.8031931083014
It is a lot slower than native floating point numbers, however. With n = 100_000, the decimals.reduce :* call went on for minutes on my computer before I finally interrupted it.

Why is division in Ruby returning an integer instead of decimal value?

For example:
9 / 5 #=> 1
but I expected 1.8. How can I get the correct decimal (non-integer) result? Why is it returning 1 at all?
It’s doing integer division. You can use to_f to force things into floating-point mode:
9.to_f / 5 #=> 1.8
9 / 5.to_f #=> 1.8
This also works if your values are variables instead of literals. Converting one value to a float is sufficient to coerce the whole expression to floating point arithmetic.
It’s doing integer division. You can make one of the numbers a Float by adding .0:
9.0 / 5 #=> 1.8
9 / 5.0 #=> 1.8
There is also the Numeric#fdiv method which you can use instead:
9.fdiv(5) #=> 1.8
You can check it with irb:
$ irb
>> 2 / 3
=> 0
>> 2.to_f / 3
=> 0.666666666666667
>> 2 / 3.to_f
=> 0.666666666666667
You can include the ruby mathn module.
require 'mathn'
This way, you are going to be able to make the division normally.
1/2 #=> (1/2)
(1/2) ** 3 #=> (1/8)
1/3*3 #=> 1
Math.sin(1/2) #=> 0.479425538604203
This way, you get exact division (class Rational) until you decide to apply an operation that cannot be expressed as a rational, for example Math.sin.
Change the 5 to 5.0. You're getting integer division.
Fixnum#to_r is not mentioned here, it was introduced since ruby 1.9. It converts Fixnum into rational form. Below are examples of its uses. This also can give exact division as long as all the numbers used are Fixnum.
a = 1.to_r #=> (1/1)
a = 10.to_r #=> (10/1)
a = a / 3 #=> (10/3)
a = a * 3 #=> (10/1)
a.to_f #=> 10.0
Example where a float operated on a rational number coverts the result to float.
a = 5.to_r #=> (5/1)
a = a * 5.0 #=> 25.0

"wrong number of arguments" ArgumentError when using round

I am trying to convert a temperature from Fahrenheit to Celsius:
puts 'Convertir grados Fahrenheit a Celcius'
STDOUT.flush
x = gets.chomp
aprox = (x * 100.0).round(2) / 100.0
resultado = (aprox-32)/1.8
puts resultado
I use the correct formula for converting Fahrenheit to Celcius:
Celsius = Fahrenheit - 32 / 1.8
However, when I run this in the console, it gives me the following error:
`round': wrong number of arguments (1 for 0) (ArgumentError)
I've tried different things but I don't understand why this doesn't work.
In ruby version prior to 1.9.0 round does not take arguments. It rounds to the nearest integer (see the documentation about floats and the use of round)
Use this instead:
aprox = (x * 100).round() / 100.0
The whole point of multiplying and dividing by 100 is to round the last two digit of x.
You don't specify what version of Ruby you are using. That makes a difference, because in Rubies prior to 1.9 Float#round did not take a parameter. In 1.9+ it does.
>> RUBY_VERSION #=> "1.9.2"
>> pi = 3.141 #=> 3.141
>> pi.round #=> 3
>> pi.round(1) #=> 3.1
>> 3.141.round(1) #=> 3.1
activesupport (part of rails) also gives you Float#round(precision)

Consistent rounding of floating points in Ruby

I understand due to the inexact representation of floating points, the following code 'feels' inconsistent.
"%.1f" % 1.14 # => 1.1
"%.1f" % 1.15 # => 1.1
"%.1f" % 1.16 # => 1.2
"%.0f" % 1.4 # => 1
"%.0f" % 1.5 # => 2
"%.0f" % 1.6 # => 2
However, is there an easy way of doing consistent floating points rounding by 5? One way might be to do string manipulation explicitly. Is there an easier way or existent library?
If you want decimal precision, use BigDecimal instead of floats.
Edit: You will have to manually round the number to the desired length before passing it to %, otherwise it gets converted to a normal float before being rounded.
"%.1f" % BigDecimal('1.15').round(1) => "1.2"
"%.0f" % BigDecimal('1.5').round(0) => "2"
Just add a tiny pertubation, to ensure things that are just under 0.5 in floating-point become just over.
For example,
x = 1.15
"%.1f" % (1.000001*x) # include correction for imprecise floating-point.
this will be enough to deal with the formatting problems, while very unlikely to cause a relevant error.
also: an obvious follow-on to my earlier question here, which is fine, but included for completeness.
The function roundthis() in this example shows how to round numbers in a controllable, consistent way. Note the small fudge value. Try running this example without the fudge to see what happens.
def roundthis(x, m)
return (x/m+0.50001).floor*m
end
for x in [1.14, 1.15, 1.16]
print "#{x} #{roundthis(x, 0.1)} \n"
end
for x in [1.4, 1.5, 1.6]
print "#{x} #{roundthis(x, 1.0)} \n"
end
This, put into a file named roundtest.rb and executed prints
bash> ruby roundtest.rb
1.14 1.1
1.15 1.2
1.16 1.2
1.4 1.0
1.5 2.0
1.6 2.0
Note the ease of rounding to the nearest 2, 15, 0.005, or whatever.
Multiply by 100, then round, then divide by 100:
(1.15 * 100).round / 100.0 # => 1.15
It's not exactly elegant, but it avoids using strings.

Resources