To_D to Always Return 2 Decimals Places in Ruby

Displaying an output in Ruby to 2 decimal places

You have to add a dot in the format to say you want the 2 to be the precision after the decimal dot:

s = '%.2f' % @price

How do you round a float to 2 decimal places in JRuby?

Float#round can take a parameter in Ruby 1.9, not in Ruby 1.8. JRuby defaults to 1.8, but it is capable of running in 1.9 mode.

How can I make eval return a number with decimal places?

All you need is the denominator of the division to be floating point and Ruby will understand to use floating point arithmetic on the expression instead of the default integer arithmetic:

irb(main):001:0> a = 5
irb(main):002:0> b = 4
irb(main):003:0> a / b
=> 1
irb(main):004:0> a / b.to_f
=> 1.25
irb(main):005:0> (a + b) / b.to_f
=> 2.25


To further clarify - It actually has nothing to do with eval in itself. It's just the way Ruby (and most other languages) handle arithmetic expressions. Most expressions are done with integer arithmetic by default.

When doing division in Ruby all you need is either dividend or divisor to be float to trigger floating point arithmetic.

rails decimal scale = 2 not working

scale specifies number of digits after the decimal point preserved in the database. Saving 1.234 will round to 1.23, 500.00 will be stored as 500.00.

In Ruby on Rails, they will be represented as a BigDecimal. The BigDecimal will not know about the format that was used in the DB. If its value is 500.0, its to_s method will output it as 500.0 because that's accurate enough.

To format the values as currency, use the number_to_currency helper method.

Also see Ruby on Rails: best method of handling currency / money and Does Ruby have any number formatting classes?

Inconsistent conversion of Float into Decimal in Ruby

So why is this happening?

TL;DR different precisions are used.

Long answer:

64.4.to_d calls bigdecimal/util's Float#to_d:

def to_d(precision=nil)
BigDecimal(self, precision || Float::DIG)

Unless specified, it uses an implicit precision of Float::DIG which is 15 for current implementations:

#=> 15

So 64.4.to_d is equivalent to:

BigDecimal(64.4, Float::DIG)
#=> #<BigDecimal:7fd7cc0aa838,'0.644E2',18(36)>

BigDecimal#* on the other hand converts a given float argument via:

if (RB_TYPE_P(r, T_FLOAT)) {
b = GetVpValueWithPrec(r, DBL_DIG+1, 1);

DBL_DIG is the C-equivalent of Float::DIG, so it's basically:

BigDecimal(64.4, Float::DIG + 1)
#=> #<BigDecimal:7fd7cc098408,'0.6440000000 000001E2',27(36)>

That said, you can get the expected result if you provide the precision explicitly, either:

f.to_d(16) == 1.to_d * f
#=> true


f.to_d == 1.to_d.mult(f, 15)
#=> true

and of course by explicitly converting f via to_d:

f.to_d == 1.to_d * f.to_d
#=> true

Isn't this a bug?

It looks like one, you should file a bug report.

Note that neither 0.644E2, nor 0.6440000000000001E2 is an exact representation of the given floating point number. As already noted by Eli Sadoff, 64.4's exact value is 64.400000000000005684341886080801486968994140625, so the most exact BigDecimal representation would be:

#=> #<BigDecimal:7fd7cc04a0c8,'0.6440000000 0000005684 3418860808 0148696899 4140625E2',54(63)>

IMO, 64.4.to_d should return just that.

Related Topics

Leave a reply