Adding utility to core classes

by Caleb Tennis

I have a small repository of utility code that almost all of my Ruby projects require because I find them so beneficial to what I do. One such function is a quick precision hack for the Float class. Here's the code:


class Float
  def prec(x)
    sprintf("%.0" + x.to_i.to_s + "f", self).to_f
  end
end


This is nice, because it allows you to quickly round a float to a certain decimal value without having to resort to much trickery.


irb> i = 100.0 / 9.4
=> 10.6382978723404
irb> i.prec(2)
=> 10.64


It did get me thinking though: is there a smarter way of implementing this method?

We can get rid of the sprintf and just use the % method.


class Float
  def prec(x)
    (("%.0" + x.to_i.to_s + "f") % self).to_f
  end
end


It still seems to me like that code could be simplified even further. Do any of you have any ideas how to make it any more compact?

9 Comments

James Edward Gray II
2005-12-20 12:18:23

class Float
def prec(x)
("%.0#{x.to_i}f" % self).to_f
end
end


Looks a little less noisy to me.


James Edward Gray II

Ryan Leavengood
2005-12-20 12:31:42
I'll second James, as that is exactly what I was going to post.
Jonas
2005-12-20 14:10:49
What about:

class Float
def prec(x)
(self * 10**x).to_i / (10**x).to_f
end
end
Brian Buckley
2005-12-20 19:59:05
An advantage (I think) of Jonas' solution is it allows rounding on the other side of the decimal when x is negative.
1234.567.prec(-2) #=>1200.0
Also why not put prec into Numeric instead of Float?


Question: On Caleb's, James' and Ryan's solutions, why are we calling to_i on x? Doesn't x have to be an integer anyway for prec to work properly? Thanks in advance for explaining.

fansipans
2005-12-21 12:40:51
34 characters: (self * 10**x).to_i / (10**x).to_f
29 characters: ("%.0#{x.to_i}f" % self).to_f


in the spirit of a good game of golf, my stab at it:


19 characters: to_s[/.*\..{#{x}}/]


can't you leave off the final .to_f since coercion should take care of any type discrepancy?

fansipans
2005-12-21 12:58:24
to_s[/.*\..{#{x}}/]


19 characters! Ahh... line noise in ruby ;D

Ryan Leavengood
2005-12-21 13:12:15
Hi Brian,


Our use of to_i is "Duck Typing" at work. If whatever is passed in can respond to to_i, we will have an integer. So whether the parameter is a String, a Fixnum, a Float or anything else that responds to to_i, it can be used. If it cannot be converted, an error will be thrown.

Ryan Bates
2005-12-21 14:04:57
I have been using these three methods:

class Float
  def round_to(x)
    (self * 10**x).round.to_f / 10**x
  end
  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
end


I wish the round, floor, and ceil methods allowed an optional precision argument.

Aristotle Pagaltzis
2005-12-21 16:19:02

This should work:


class Float
    def prec( x )
        sprintf( "%.*f", x, self ).to_f
    end
end


Which is about as simple as I'd want it to be; any less would be obfuscatory.


(Unless maybe Ruby's printf is dumbed down compared to the one I know from C and Perl; I wouldn't know.)