Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Numeric validator precision/scale validation fails for certain floats on MRI 1.9.3 #51

Open
exitface opened this Issue · 2 comments

2 participants

@exitface

In validators/numeric_validator.rb on line 38

      def value_as_string(value)
        case value
          # Avoid Scientific Notation in Float to_s
          when Float      then value.to_d.to_s('F')
          when BigDecimal then value.to_s('F')
          else value.to_s
        end
      end

Given: f=-75.6942185

We call, f.to_d.to_s('F')

On all rubies except 1.9.3 we get:

"-75.6942185"

On MRI 1.9.3 we get:

"-75.69421850000001"

When then causes the validator to report an invalid precision/scale

@jablkopp

The same problem with number: 842.31

1.9.3p0 :001 > 842.31.to_d.to_s
=> "842.3099999999999" 

This means that we can have random validation errors ("Value is not a number") during save model.

In ruby 1.9.3 method to_d in Foat class changed:

class Float < Numeric
  def to_d(precision=nil)
    BigDecimal(self, precision || Float::DIG+1)
  end
end

In ruby 1.9.2 it was:

class Float < Numeric
  def to_d
    BigDecimal(self.to_s)
  end
end

This is why it works in 1.9.2 and doesn't work in 1.9.3.

1.9.3p0 :008 > BigDecimal(842.31, Float::DIG+1).to_s
 => "842.3099999999999" 
1.9.3p0 :009 > BigDecimal(842.31.to_s).to_s
 => "842.31" 

To fix it in your app you can override to_d method like this:

class Float < Numeric
  def to_d(precision=nil)
   if precision
     BigDecimal(self, precision)
   else
     BigDecimal(self.to_s)
   end
  end
end

but this monkeypatch is not the best idea and it should be fixed somehow in DM.

@jablkopp

This is safer fix (for DM-core ~> 1.1.0):

# Ruby 1.9.3 hack

module DataMapper
  class Property
    class Decimal193 < Decimal
      def typecast_to_primitive(value)
        if value.kind_of?(::Integer)
          value.to_s.to_d
        elsif value.kind_of?(::Float)
          BigDecimal(value.to_s)
        else
          typecast_to_numeric(value, :to_d)
        end
      end
    end
  end
end

and then use Decimal193 instead of Decimal in your models.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.