[ACCEPTED]-Determine if a string is a valid float value-ruby

Accepted answer
Score: 39

Here's one way:

class String
  def valid_float?
    # The double negation turns this into an actual boolean true - if you're 
    # okay with "truthy" values (like 0.0), you can remove it.
    !!Float(self) rescue false
  end
end

"a".valid_float? #false
"2.4".valid_float? #true

If you want to avoid the 3 monkey-patch of String, you could always 2 make this a class method of some module 1 you control, of course:

module MyUtils
  def self.valid_float?(str)
    !!Float(str) rescue false
  end
end
MyUtils.valid_float?("a") #false
Score: 28

An interesting fact about the Ruby world 13 is the existence of the Rubinius project, which 12 implements Ruby and its standard library 11 mostly in pure Ruby. As a result, they have 10 a pure Ruby implementation of Kernel#Float, which 9 looks like:

def Float(obj)
  raise TypeError, "can't convert nil into Float" if obj.nil?

  if obj.is_a?(String)
    if obj !~ /^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/
      raise ArgumentError, "invalid value for Float(): #{obj.inspect}"
    end
  end

  Type.coerce_to(obj, Float, :to_f)
end

This provides you with a regular 8 expression that matches the internal work 7 Ruby does when it runs Float(), but without 6 the exception. So you could now do:

class String
  def nan?
    self !~ /^\s*[+-]?((\d+_?)*\d+(\.(\d+_?)*\d+)?|\.(\d+_?)*\d+)(\s*|([eE][+-]?(\d+_?)*\d+)\s*)$/
  end
end

The nice 5 thing about this solution is that since 4 Rubinius runs, and passes RubySpec, you 3 know this regex handles the edge-cases that 2 Ruby itself handles, and you can call to_f 1 on the String without any fear!

Score: 9
# Edge Cases:
# numeric?"Infinity" => true is_numeric?"Infinity" => false


def numeric?(object)
true if Float(object) rescue false
end

#Possibly faster alternative
def is_numeric?(i)
i.to_i.to_s == i || i.to_f.to_s == i
end

0

Score: 9

Ruby 2.6+

Ruby 2.6 added a new exception keyword argument to Float.

So, now to check 2 whether a string contains a valid float 1 is as simple as:

Float('22.241234', exception: false)
# => 22.241234

Float('abcd', exception: false)
# => nil

Here is a link to the docs.

Score: 4

I saw the unresolved discussion on cast+exceptions 37 vs regex and I thought I would try to benchmark 36 everything and produce an objective answer:

Here 35 is the source for the best case and worst 34 of each method attempted here:

require "benchmark"
n = 500000

def is_float?(fl)
  !!Float(fl) rescue false
end

def is_float_reg(fl)
  fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
end

class String
  def to_float
    Float self rescue (0.0 / 0.0)
  end
end


Benchmark.bm(7) do |x|
  x.report("Using cast best case") {
    n.times do |i|
      temp_fl = "#{i + 0.5}"
      is_float?(temp_fl)
    end
  }
  x.report("Using cast worst case") {
    n.times do |i|
      temp_fl = "asdf#{i + 0.5}"
      is_float?(temp_fl)
    end
  }
  x.report("Using cast2 best case") {
    n.times do |i|
      "#{i + 0.5}".to_float
    end
  }
  x.report("Using cast2 worst case") {
    n.times do |i|
      "asdf#{i + 0.5}".to_float
    end
  }
  x.report("Using regexp short") {
    n.times do |i|
      temp_fl = "#{i + 0.5}"
      is_float_reg(temp_fl)
    end
  }
  x.report("Using regexp long") {
    n.times do |i|
      temp_fl = "12340918234981234#{i + 0.5}"
      is_float_reg(temp_fl)
    end
  }
    x.report("Using regexp short fail") {
    n.times do |i|
      temp_fl = "asdf#{i + 0.5}"
      is_float_reg(temp_fl)
    end
  }
  x.report("Using regexp long fail") {
    n.times do |i|
      temp_fl = "12340918234981234#{i + 0.5}asdf"
      is_float_reg(temp_fl)
    end
  }

end

With the following 33 results with mri193:

              user     system      total        real
Using cast best case  0.608000   0.000000   0.608000 (  0.615000)
Using cast worst case  5.647000   0.094000   5.741000 (  5.745000)
Using cast2 best case  0.593000   0.000000   0.593000 (  0.586000)
Using cast2 worst case  5.788000   0.047000   5.835000 (  5.839000)
Using regexp short  0.951000   0.000000   0.951000 (  0.952000)
Using regexp long  1.217000   0.000000   1.217000 (  1.214000)
Using regexp short fail  1.201000   0.000000   1.201000 (  1.202000)
Using regexp long fail  1.295000   0.000000   1.295000 (  1.284000)

Since we are dealing 32 with only Linear time algorithms I think 31 we use empirical measurements to make generalizations. It 30 is plain to see that regex is more consistent 29 and will only fluctuate a bit based on the 28 length of the string passed. The cast is 27 clearly faster when there are no failure, and 26 much slower when there are failures.

If we 25 compare the success times we can see that 24 the cast best case is about .3 seconds faster 23 than regex best case. If we divide this 22 by the amount of time in the case worst 21 case we can estimate how many runs it would 20 take to break even with exceptions slowing 19 the cast down to match regex speeds. About 18 6 seconds dived by .3 gives us about 20. So 17 if performance matters and you expect less 16 than 1 in 20 of your test to fail then used 15 cast+exceptions.

JRuby 1.7.4 has completely 14 different results:

              user     system      total        real
Using cast best case  2.575000   0.000000   2.575000 (  2.575000)
Using cast worst case 53.260000   0.000000  53.260000 ( 53.260000)
Using cast2 best case  2.375000   0.000000   2.375000 (  2.375000)
Using cast2 worst case 53.822000   0.000000  53.822000 ( 53.822000)
Using regexp short  2.637000   0.000000   2.637000 (  2.637000)
Using regexp long  3.395000   0.000000   3.395000 (  3.396000)
Using regexp short fail  3.072000   0.000000   3.072000 (  3.073000)
Using regexp long fail  3.375000   0.000000   3.375000 (  3.374000)

Cast is only marginally 13 faster in the best case (about 10%). Presuming 12 this difference is appropriate for making 11 generalizations(I do not think it is), then 10 the break even point is somewhere between 9 200 and 250 runs with only 1 causing an 8 exception.

So exceptions should only be used 7 when truly exceptional things happens, this 6 is decision for you and your codebase. When 5 they aren't used code they are in can be 4 simpler and faster.

If performance doesn't 3 matter, you should probably just following 2 whatever conventions you team or code base 1 already has and ignore this whole answers.

Score: 4
def float?(string)
  true if Float(string) rescue false
end

This supports 1.5, 5, 123.456, 1_000 but not 1 000, 1,000, etc. (e.g. same 1 as String#to_f).

>> float?("1.2")
=> true
>> float?("1")
=> true
>> float?("1 000")
=> false
>> float?("abc")
=> false
>> float?("1_000")
=> true

Source: https://github.com/ruby/ruby/blob/trunk/object.c#L2934-L2959

Score: 3

Umm, if you don't want exceptions then perhaps:

def is_float?(fl)
   fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
end

Since 3 OP specifically asked for a solution without 2 exceptions. Regexp based solution is marginally 1 slow:

require "benchmark"
n = 500000

def is_float?(fl)
  !!Float(fl) rescue false
end

def is_float_reg(fl)
  fl =~ /(^(\d+)(\.)?(\d+)?)|(^(\d+)?(\.)(\d+))/
end

Benchmark.bm(7) do |x|
  x.report("Using cast") {
    n.times do |i|
      temp_fl = "#{i + 0.5}"
      is_float?(temp_fl)
    end
  }
  x.report("using regexp") {
    n.times do |i|
      temp_fl = "#{i + 0.5}"
      is_float_reg(temp_fl)
    end
  }
end

Results:

5286 snippets:master!? % 
             user     system      total        real
Using cast  3.000000   0.000000   3.000000 (  3.010926)
using regexp  5.020000   0.000000   5.020000 (  5.021762)
Score: 2

Try this

def is_float(val)
  fval = !!Float(val) rescue false
  # if val is "1.50" for instance
  # we need to lop off the trailing 0(s) with gsub else no match
  return fval && Float(val).to_s == val.to_s.gsub(/0+$/,'') ? true:false
end 

s = "1000"
is_float s
 => false 

s = "1.5"
is_float s
 => true 

s = "Bob"
is_float s
 => false

n = 1000
is_float n
 => false 

n = 1.5
is_float n
 => true

0

Score: 1

I tried to add this as a comment but apparently 8 there is no formatting in comments:

on the 7 other hand, why not just use that as your 6 conversion function, like

class String
  def to_float
    Float self rescue (0.0 / 0.0)
  end
end
"a".to_float.nan? => true

which of course 5 is what you didn't want to do in the first 4 place. I guess the answer is, "you have 3 to write your own function if you really 2 don't want to use exception handling, but, why 1 would you do that?"

More Related questions