How to combine float with a certain number of significant digits in Ruby?

It would be nice to have the equivalent of R signif in Ruby.

For example:

>> (11.11).signif(1) 10 >> (22.22).signif(2) 22 >> (3.333).signif(2) 3.3 >> (4.4).signif(3) 4.4 # It usually 4.40 but that OK. R does not print the trailing 0's # because it returns the float data type. For Ruby we want the same. >> (5.55).signif(2) 5.6 
+8
floating-point ruby significant-digits
source share
7 answers

There is probably a better way, but this works fine:

 class Float def signif(signs) Float("%.#{signs}g" % self) end end (1.123).signif(2) # => 1.1 (11.23).signif(2) # => 11.0 (11.23).signif(1) # => 10.0 
+13
source share

Some of the previous answers and comments allude to this solution, but this is what worked for me:

 # takes in a float value and returns another float value rounded to # given significant figures. def round_to_sig_figs(val, sig_figs) BigDecimal.new(val, sig_figs).to_f end 
+3
source share

I do not see anything like it in Float. Float is basically a wrapper for the native double type and given the usual binary / decimal problems, I am not surprised that Float does not allow you to manipulate significant digits.

However, BigDecimal in the standard library really understands significant numbers, but again, I do not see anything that allows you to directly change the significant numbers in BigDecimal: you can ask for it, but you cannot change it. But you can spoof this using the no-op version of the mult or add methods:

 require 'bigdecimal' a = BigDecimal.new('11.2384') a.mult(1, 2) # the result is 0.11E2 (ie 11) a.add(0, 4) # the result is 0.1124E2 (ie 11.24) 

The second argument to these methods:

If less than the number of significant digits of the result is indicated, the result is rounded to this number of digits in accordance with BigDecimal.mode .

Using BigDecimal will be slower, but it may be your only choice if you need fine-grained control or if you need to avoid the usual floating point problems.

+2
source share

You might be looking for Ruby Decimal .

Then you can write:

 require 'decimal/shortcut' num = 1.23541764 D.context.precision = 2 num_with_2_significant_digits = +D(num.to_s) # => Decimal('1.2') num_with_2_significant_digits.to_f # => 1.2000000000000002 

Or, if you prefer to use the same syntax, add this as a function to the Float class as follows:

 class Float def signif num_digits require 'decimal/shortcut' D.context.precision = num_digits (+D(self.to_s)).to_f end end 

The use will then be the same, i.e.

  (1.23333).signif 3 # => 1.23 

To use it, set the gem

 gem install ruby-decimal 
+2
source share

Here's an implementation that does not use strings or other libraries.

 class Float def signif(digits) return 0 if self.zero? self.round(-(Math.log10(self).ceil - digits)) end end 
+1
source share

@ Blou91's answer is almost there, but it returns a string instead of a float. This is below for me:

 (sprintf "%.2f", 1.23456).to_f 

So, as a function,

 def round(val, sig_figs) (sprintf "%.#{sig_figs}f", val).to_f end 
0
source share

Use sprintf if you want to print trailing zeros

 2.0.0-p353 :001 > sprintf "%.3f", 500 => "500.000" 2.0.0-p353 :002 > sprintf "%.4f", 500 => "500.0000" 2.0.0-p353 :003 > 
-one
source share

All Articles