3

I need to convert any floating point number into 5-significant digits format. It means five first non-zero digits (with the last digit not rounded up).

Example

Float("%.#{5}g" % 0.007425742694) => 0.0074257
Float("%.#{5}g" % 3852.574257425742694) => 3852.6

The problem with this approach for me is that 3852.574257425742694 is rounded to 3852.6, and I need 3852.5.

Ilya Cherevkov
  • 1,743
  • 2
  • 17
  • 47
  • Possible duplicate of [Rounding float in Ruby](https://stackoverflow.com/questions/2054217/rounding-float-in-ruby) – Thomas Jager Aug 22 '17 at 16:02
  • It's not really the same. There is https://stackoverflow.com/questions/8382619/how-do-i-round-a-float-to-a-specified-number-of-significant-digits-in-ruby but it also provides solutions with rounding the last digit – Ilya Cherevkov Aug 22 '17 at 16:04
  • 1
    nvm, I've found solution `Float("%.#{6}g" % 3852.574257425742694).to_s[0..-2].to_f` – Ilya Cherevkov Aug 22 '17 at 16:12
  • @IlyaCherevkov the `Float()` portion is unnecessary since `String#%` will return a `String` e.g `("%.#{6}g" % 3852.574257425742694)[0..-2].to_f` right now you are formatting a `Float` as a `String` then converting the `String` to a `Float` then back to a `String` then back to a `Float` – engineersmnky Aug 22 '17 at 16:31
  • 1
    thanks! also it's not valid for numbers with whole part bigger than 5 digits, but it's not the case for me – Ilya Cherevkov Aug 22 '17 at 16:34
  • Hi, 5 significant digits are required by external API, which is used for some stock prices. Prices on the platform are not bigger than xxxx.xxxxxxxx, so first two are not the case for me, for the last `-0.0012345`. I guess if it was the case, the expected output would be `123456` & `-123456` – Ilya Cherevkov Aug 23 '17 at 06:58

1 Answers1

3

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
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100