Float Rounding Changes in Ruby 2.4 - ruby

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.

Related

what does the number 1e18 in ruby mean

I am trying to replace the number 18 in 1e18 with a variable but everything I have tried gives an error. Perhaps if I knew what it does I can be able to write the formula myself differently.
What does the letter 3 do to a number. How can I apply it to a variable called, say, X?
How different is e from **
what does the number 1e18 in ruby mean?
1e18 (or 1E18) is a number literal using E-notation. Ruby interprets this number as a floating point number with the value 1 × 1018 (i.e. 1,000,000,000,000,000,000).
I am trying to replace the number 18 in 1e18 with a variable
1e18 is equivalent to:
1.0 * 10 ** 18
#=> 1.0e+18
so you can write:
x = 18
1.0 * 10 ** x
#=> 1.0e+18
or simply:
10.0 ** x
#=> 1.0e+18
How different is e from **
The result is the same, but 1e18 – being a literal – is evaluated by the parser whereas ** is a method call.
As Sami's comment mentions:
1e18 is a scientific notation meaning 1 * 10^18
Read more about the number here
How to work with such numbers in Ruby?
Here's a simple example:
require 'bigdecimal'
a = BigDecimal.new "1e18"
#=> #<BigDecimal:2cf0880,'0.1E19',9(18)>
a.to_f
#=> 1.0e+18
a.to_s
#=> "0.1E19" # notice 0.1E19 and not 1.0E19
"%f" % a
#=> "1000000000000000000.000000"
("%f" % a).to_i
#=> 1000000000000000000

Unable to understand the subtraction result

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"

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

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