Sergey Potapov

Talking about Linux, Ruby and other hackers' stuff.

Rational Can't Be Coerced Into BigDecimal in Ruby 1.9.3

Trying to move a rails application from ruby 1.8.7 to 1.9.3 I ran into coercion issue of Rational class.

Ruby 1.9.3:

1
2
3
4
5
6
7
8
require 'bigdecimal'
require 'rational'

# You can multiply Rational against BigDecimal
Rational(1) * BigDecimal('1')  # => <BigDecimal:a566d0,'0.1E1',9(36)>

# But you can't do the same when you change order
BigDecimal('1') * Rational(1)  # => TypeError: Rational can't be coerced into BigDecimal

On other hand in Ruby 1.8.7:

1
2
3
4
5
6
7
8
require 'bigdecimal'
require 'rational'

# BigDecimal * Rational works OK
BigDecimal('1') * Rational(1)  # => 1.0 (Float)

# But Rational * BigDecimal doesn't
Rational(1) * BigDecimal('1')  # => TypeError: Rational can't be coerced into BigDecimal

It’s looks weird. So I can only say for sure that Rational -> BigDecimal coercion is not implemented in Ruby.

I’ve tried to fix it with simple monkey patch:

1
2
3
4
5
6
7
8
9
10
11
# Works only for Ruby1.9.3
class Rational
  def coerce(value)
    case value
    when BigDecimal
      return self, value
    else
      super
    end
  end
end

And it works OK against simple examples:

1
2
Rational(1) * BigDecimal('1')  # => <BigDecimal:a566d0,'0.1E1',9(36)>
BigDecimal('1') * Rational(1)  # => <BigDecimal:a566d0,'0.1E1',9(36)>

But it causes intermittent segmentation faults when I run Rails application.

Any ideas about the coercion? Is it expected behaviour of ruby1.9.3? I found no bugs reported this issue on https://bugs.ruby-lang.org.

I’ll appreciate any feedback. Thanks.

Comments