bryantsai
bryantsai

Reputation: 3455

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?

Upvotes: 2

Views: 1642

Answers (4)

DarenW
DarenW

Reputation: 16906

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.

Upvotes: 1

Tobias Cohen
Tobias Cohen

Reputation: 20000

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"

Upvotes: 5

Peter
Peter

Reputation: 132177

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.

Upvotes: 2

Alex Reisner
Alex Reisner

Reputation: 29442

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.

Upvotes: 0

Related Questions