How to Round a Float to a Specified Number of Significant Digits in Ruby

How to round Decimals to the First Significant Figure in Ruby

One should use BigDecimal for this kind of computation.

require 'bigdecimal'

bd = BigDecimal((400.0 / 56000).to_s)
#⇒ 0.7142857142857143e-2
bd.exponent
#⇒ -2

Example:

[10_000.0 / 1_000, 300.0 / 1_000, 600.0 / 800,
500.0 / 1_600, 400.0 / 56_000].
map { |bd| BigDecimal(bd.to_s) }.
map do |bd|
additional = bd.exponent >= 0 ? 0 : bd.exponent + 1
bd.round(2 - additional) # THIS
end.
map(&:to_f)
#⇒ [10.0, 0.3, 0.75, 0.31, 0.007]

Calculate number of significant digits

I understand the numbers are non-negative. One can do the following:

Code

def floor_it(f, sig_digits=5)
pow = sprintf("%e", f)[-3..-1].to_i
(f *= 10**(-pow)).floor(sig_digits-1) * 10**(pow)
end

Examples

floor_it 0.007425742694        #=> 0.0074257
floor_it 3852.574257425742694 #=> 3852.5

Explanation

For

f = 385.74957425742694 
sig_digits = 5

the steps are as follows. First express the number in scientific notation (as a string). See Kernel#sprintf.

a = sprintf("%e", f)
#=> "3.857496e+02"

We wish to extract the last two digits.

b = a[-3..-1]
#=> "02"

Convert that to an integer.

pow = b.to_i
#=> 2

Shift f's decimal point so there is one non-zero digit to the left of the decimal point.

f *= 10**(-pow) 
#=> 3.8574957425742697

Use the method Float#floor to obtain the desired digits.

d = f.floor(sig_digits-1)
#=> 3.8574957425742697.floor(4)
#=> 3.8574

Lastly, shift the decimal point back to its initial position.

d * 10**(pow)
#=> 385.74

Ruby round Float up or down to specific decimal significant figure

In Ruby 2.4+, the Float#float & Float#ceil methods take a ndigits argument:

1.33333333.ceil(2) -> 1.34
1.33333333.floor(3) -> 1.333

However, check out this behavior with those STD lib methods:

# In Ruby 2.4.2:
0.07.ceil(2) -> 0.08
1.1.ceil(2) -> 1.11

Not OK in my book.

For older Ruby versions or if you want to get better results than the STB lib gives, you will need to write your own methods. There are a few different blog posts out there, and I'll explain why they're not consistently correct later, but here are some methods that should work correctly every time:

require 'bigdecimal'

class Float
def ceil2(exp = 0)
BigDecimal(self.to_s).ceil(exp).to_f
end

def floor2(exp = 0)
BigDecimal(self.to_s).floor(exp).to_f
end
end

Now for more on why the following are incorrect:

def ceil_to(x)
(self * 10**x).ceil.to_f / 10**x
end

def floor_to(x)
(self * 10**x).floor.to_f / 10**x
end

# These methods also produce bad results for the examples shown above
0.07.ceil(2) -> 0.08
1.1.ceil(2) -> 1.11

I won't go into the details about what is happening (you can find that
here or here), but floating point arithmetic can be messy and rounding errors do occur.

Ruby: Rounding float in Ruby

When displaying, you can use (for example)

>> '%.2f' % 2.3465
=> "2.35"

If you want to store it rounded, you can use

>> (2.3465*100).round / 100.0
=> 2.35

How do you round a float to 2 decimal places in JRuby?

Float#round can take a parameter in Ruby 1.9, not in Ruby 1.8. JRuby defaults to 1.8, but it is capable of running in 1.9 mode.



Related Topics



Leave a reply



Submit