Weird issue with converting floats to integer

Any idea why this calculates the integer the way it does?

("291.15".to_f * 100.0).to_i

=> 29114

Thanks,
Tom

TomRossi7 wrote in post #970207:

Any idea why this calculates the integer the way it does?

> ("291.15".to_f * 100.0).to_i
=> 29114

Because of the vagaries of floating-point arithmetic:

("291.15".to_f * 100.0) < 29115

=> true

printf "%.30f", ("291.15".to_f * 100.0)

29114.999999999996362021192908287048

It bears repeating: never, ever use Floats for arithmetic. If you can't
use fixed-point math, then use BigDecimal.

Thanks,
Tom

Best,

Or just change the way you calculate to get at the level of accuracy
that you want/need:

("291.15".to_f * 1000.0).to_i/10

=> 29115

Jeff

Please quote when replying.

Jeff Burlysystems wrote in post #970344:

Or just change the way you calculate to get at the level of accuracy
that you want/need:

> ("291.15".to_f * 1000.0).to_i/10
=> 29115

That's incredibly silly IMHO. Besides, it will not always be the same
for all values, will it? I think BigDecimal is the right answer here.

Jeff

Best,

I have currency information that I plan to store as an integer --
ironically to avoid issues like this. The user input will always be
in a decimal form. I'm thinking I will just do this ('291.15'.to_f *
100).round instead. That should round out any of the weird float
issues.

Please quote when replying.

TomRossi7 wrote in post #970346:

I have currency information that I plan to store as an integer --
ironically to avoid issues like this. The user input will always be
in a decimal form. I'm thinking I will just do this ('291.15'.to_f *
100).round instead. That should round out any of the weird float
issues.

I think there are better ways of doing that -- such as using a DECIMAL
field in the database. Then no conversion is necessary and the data is
stored as fixed-point.

Or, if you'd rather store the data as an integral number of pennies
(though that may have i18n issues -- there are a few currencies, such as
the Kuwaiti dinar, that are subdivided into 1000, not 100), then a quick
way would be to normalize the string so that it contains 2 decimal
places (so "123" would become "123.00"), then drop the decimal point.

Best,

As Marnen's already pointed out, the issue is that the result is a
*tiny* bit smaller than 29115, so to_i truncates off the entire
fractional part and gives the wrong answer. In general, if you want to
convert a decimal to an integer the way most people would on paper,
use .round.to_i, which will correctly round the float to the nearest
integer.

On the currency issue, it's typically a good idea to avoid floating-
point throughout the process if you're trying to get a reliable fixed-
point result. Decimal columns are definitely a good idea for this sort
of thing.

--Matt Jones

TomRossi7 wrote in post #970346:

I have currency information that I plan to store as an integer --
ironically to avoid issues like this. The user input will always be
in a decimal form. I'm thinking I will just do this ('291.15'.to_f *
100).round instead. That should round out any of the weird float
issues.

As was said before by others in this thread, using the BigDecimal type
really is the most reliable, and simple, solution. Don't reinvent
the wheel with calculating in pennies and rounding etc. ...

In the migration, do this:

class AddAmountToPayment < ActiveRecord::Migration
  def self.up
    add_column :payments, :amount, :decimal, :precision => 12, :scale =>
2
  end
...

This which will work automatically correct when reading
user input in your forms and when doing internal calculations:

$ rails c
Loading development environment (Rails 3.0.3)
001:0> Payment.columns.select{|c| c.sql_type =~ /decimal/}
=> [#<ActiveRecord::ConnectionAdapters::MysqlColumn:0xb6c1e844
@sql_type="decimal(12,2)", @scale=2, @name="amount", @limit=12,
@precision=12, @primary=false, @type=:decimal, @default=nil,
@null=true>]

002:0> payment = Payment.new(:amount => "291.15")
=> #<Payment id: nil, user_id: nil, testing: nil, created_at: nil,
updated_at: nil, amount: #<BigDecimal:b6c09318,'0.29115E3',8(12)>>

003:0> payment.amount
=> #<BigDecimal:b6c07e14,'0.29115E3',8(12)>

004:0> payment.amount.to_s
=> "291.15"

...

019:0> (payment.amount - BigDecimal("291") - BigDecimal("0.15")).to_s
=> "0.0"
020:0> ((291.15 - 291) - 0.15).to_s
=> "-2.27318164292001e-14" # Rounding errors with float !!!

HTH,

Peter

I have to disagree here. I would rephrase it as _almost_ never use
Floats The one time when they would be appropriate is if you have a
large amount of arithmetic to perform. The sort of thing that might
occur in complex statistical analysis for example or engineering
calculations of some sort. On most (possibly all) computers floating
point consumes vastly less processor time to perform than BigDecimal.
This is a rare occurence in Rails apps I expect so I agree that for
most people Marnen's advice would be appropriate.

Colin

Colin Law wrote in post #970376:

29114.999999999996362021192908287048

It bears repeating: never, ever use Floats for arithmetic. If you can't
use fixed-point math, then use BigDecimal.

I have to disagree here.

I knew someone would. :slight_smile:

I would rephrase it as _almost_ never use
Floats The one time when they would be appropriate is if you have a
large amount of arithmetic to perform. The sort of thing that might
occur in complex statistical analysis for example or engineering
calculations of some sort. On most (possibly all) computers floating
point consumes vastly less processor time to perform than BigDecimal.

Then use fixed-point math. IEEE floats are 100% inappropriate for any
sort of mathematical calculation. And who cares how fast they are if
they're inaccurate? Inaccurate engineering calculations are worse than
none at all -- do *you* want to drive over that bridge? :slight_smile:

If you want to use IEEE floats for arithmetic, then the onus is on you
to do the requisite numerical analysis to figure out how much error you
are introducing, and to inform your users of that fact.

This is a rare occurence in Rails apps

Yes indeed!

I expect so I agree that for
most people Marnen's advice would be appropriate.

Colin

Best,

Colin Law wrote in post #970376:

29114.999999999996362021192908287048

It bears repeating: never, ever use Floats for arithmetic. If you can't
use fixed-point math, then use BigDecimal.

I have to disagree here.

I knew someone would. :slight_smile:

You probably guessed it might be me :slight_smile:

I would rephrase it as _almost_ never use
Floats The one time when they would be appropriate is if you have a
large amount of arithmetic to perform. The sort of thing that might
occur in complex statistical analysis for example or engineering
calculations of some sort. On most (possibly all) computers floating
point consumes vastly less processor time to perform than BigDecimal.

Then use fixed-point math. IEEE floats are 100% inappropriate for any
sort of mathematical calculation. And who cares how fast they are if
they're inaccurate? Inaccurate engineering calculations are worse than
none at all -- do *you* want to drive over that bridge? :slight_smile:

We are getting off topic for Rails apps but I think if you looked into
most engineering and scientific software you would find they use
floating point arithmetic. Fixed point is not appropriate as
intermediate calculations need guaranteed _relative_ error, not
absolute error. That is what floats provide.

If you want to use IEEE floats for arithmetic, then the onus is on you
to do the requisite numerical analysis to figure out how much error you
are introducing, and to inform your users of that fact.

Yes of course. As you said, the bridge designer needs to know the
possible errors in his calculations.

Colin

Marnen Laibow-Koser wrote in post #970377:

Then use fixed-point math. IEEE floats are 100% inappropriate for any
sort of mathematical calculation. And who cares how fast they are if
they're inaccurate? Inaccurate engineering calculations are worse than
none at all -- do *you* want to drive over that bridge? :slight_smile:

Let's break this down to a comparison of accuracy vs. precision.

Take for example the value 1/3.

First using floating point math:

x = 1.0
=> 1.0
y = 3.0
=> 3.0
z = x / y
=> 0.3333333333333333

Now with Ruby's BigDecimal math:
x = BigDecimal.new('1')
=> #<BigDecimal:102c078,'0.1E1',4(8)>
y = BigDecimal.new('3')
=> #<BigDecimal:10388f0,'0.3E1',4(8)>
z = x / y
=> #<BigDecimal:103680c,'0.33333333E0',8(16)>

In the case of float we have 16 decimal digits of precision, but in the
case of BigDecimal we have only eight digits of precision.

Yes, in the case of BigDecimal we have a value representing "precisely"
the value 0.33333333, but that result is obviously less accurate than
the result of the floating point calculation.

Over large aggregations this can make a difference. It is certainly the
case that IEEE floats introduce error due to the inherent storage
limitations, but doing precise mathematics as fixed point has it's own
complications as well.

Ruby's BigDecimal class does not provide the needed accuracy for complex
mathematics. The difference is that BigDecimal always stores precise
values within the limits of it storage space. Where floats store
imprecise representations, but representations with a fixed precision.

I don't think either of these classes are appropriate for the sort of
math mentioned above. But, they both have their uses.

Robert Walker wrote in post #970390:

Marnen Laibow-Koser wrote in post #970377:

Then use fixed-point math. IEEE floats are 100% inappropriate for any
sort of mathematical calculation. And who cares how fast they are if
they're inaccurate? Inaccurate engineering calculations are worse than
none at all -- do *you* want to drive over that bridge? :slight_smile:

Let's break this down to a comparison of accuracy vs. precision.

Take for example the value 1/3.

First using floating point math:

x = 1.0
=> 1.0
y = 3.0
=> 3.0
z = x / y
=> 0.3333333333333333

Now with Ruby's BigDecimal math:
x = BigDecimal.new('1')
=> #<BigDecimal:102c078,'0.1E1',4(8)>
y = BigDecimal.new('3')
=> #<BigDecimal:10388f0,'0.3E1',4(8)>
z = x / y
=> #<BigDecimal:103680c,'0.33333333E0',8(16)>

In the case of float we have 16 decimal digits of precision, but in the
case of BigDecimal we have only eight digits of precision.

Are you sure? Or are only eight digits displayed?

Yes, in the case of BigDecimal we have a value representing "precisely"
the value 0.33333333, but that result is obviously less accurate than
the result of the floating point calculation.

Not obvious at all, since I don't know how many 3s the BigDecimal is
*actually* storing.

Over large aggregations this can make a difference.

Yes, it certainly can.

It is certainly the
case that IEEE floats introduce error due to the inherent storage
limitations, but doing precise mathematics as fixed point has it's own
complications as well.

Sure. BigDecimal is the best of both worlds.

Ruby's BigDecimal class does not provide the needed accuracy for complex
mathematics.

Of course it does. Why do you think it does not?

Anyway, if you need more than BigDecimal, then start climbing into the
realms of symbolic math with Rational.

The difference is that BigDecimal always stores precise
values within the limits of it storage space. Where floats store
imprecise representations, but representations with a fixed precision.

I don't think either of these classes are appropriate for the sort of
math mentioned above.

Why is BigDecimal inappropriate? What would you use instead?

But, they both have their uses.

I'm not sure Float has a proper use case at all. :slight_smile:

Best,

$ rails console
Loading development environment (Rails 3.0.3)
ruby-1.9.2-p0 > x=BigDecimal('1')/BigDecimal('3')
=> #<BigDecimal:ad823a4,'0.33333333E0',8(16)>
ruby-1.9.2-p0 > x*BigDecimal('3')
=> #<BigDecimal:ad2b270,'0.99999999E0',8(20)>
ruby-1.9.2-p0 > x*BigDecimal('3') == 1
=> false
ruby-1.9.2-p0 > x*BigDecimal('3') - 1
=> #<BigDecimal:aa4fdf8,'-0.1E-7',4(20)>

Colin

Consider table containing the masses of objects. Columns description
and mass (kg), with the following records
apple, 0.2
earth, 5.9742E24
sun, 1.98892E30
super massive black hole, 1.0E39

What column type would be appropriate for storing the mass?

Colin

Colin Law wrote in post #970449:

[...]
I'm not sure Float has a proper use case at all. :slight_smile:

Consider table containing the masses of objects. Columns description
and mass (kg), with the following records
apple, 0.2
earth, 5.9742E24
sun, 1.98892E30
super massive black hole, 1.0E39

What column type would be appropriate for storing the mass?

Irrelevant. If I understand you correctly, you're asking about DB
column types, not Ruby column types.

Anyway, the answer is: it depends. If I didn't need arbitrary
precision, I'd store them as floats in the DB, and then change to
BigDecimal before doing any math on them.

If I did need arbitrary precision, and the DB didn't provide a
BigDecimal-like type, I'd store them as strings.

Colin

Best,

Colin Law wrote in post #970447:

x = BigDecimal.new('1')
=> #<BigDecimal:102c078,'0.1E1',4(8)>
y = BigDecimal.new('3')
=> #<BigDecimal:10388f0,'0.3E1',4(8)>
z = x / y
=> #<BigDecimal:103680c,'0.33333333E0',8(16)>

In the case of float we have 16 decimal digits of precision, but in the
case of BigDecimal we have only eight digits of precision.

Are you sure? Or are only eight digits displayed?

$ rails console
Loading development environment (Rails 3.0.3)
ruby-1.9.2-p0 > x=BigDecimal('1')/BigDecimal('3')
=> #<BigDecimal:ad823a4,'0.33333333E0',8(16)>
ruby-1.9.2-p0 > x*BigDecimal('3')
=> #<BigDecimal:ad2b270,'0.99999999E0',8(20)>
ruby-1.9.2-p0 > x*BigDecimal('3') == 1
=> false
ruby-1.9.2-p0 > x*BigDecimal('3') - 1
=> #<BigDecimal:aa4fdf8,'-0.1E-7',4(20)>

Interesting. However, the BigDecimal constructor allows specification
of the minimum number of significant figures, so in this case we could
have done BigDecimal.new('1', 16).

Colin

Best,

ruby-1.9.2-p0 > x = BigDecimal.new('1', 16)/BigDecimal.new('3',16)
=> #<BigDecimal:a146bbc,'0.33333333E0',8(16)>
ruby-1.9.2-p0 > x*BigDecimal('3',16) - 1
=> #<BigDecimal:a114acc,'-0.1E-7',4(20)>

It doesn't make any difference how many digits you have, 1/3 as a
decimal number multiplied by 3 will never be equal to 1.

I am not sure why the above still gives only 8 digits accuracy
however, I would have thought it aught to be 16.

Colin

Colin Law wrote in post #970449:

If I did need arbitrary precision, and the DB didn't provide a
BigDecimal-like type, I'd store them as strings.

Which still doesn't allow you to store all numbers with arbitrary
precision (in fact putting my mathematical hat on, most numbers can't
be stored like this). You'll have to deal with numerical error
eventually

Fred

Colin Law wrote in post #970496:

=> #<BigDecimal:ad2b270,'0.99999999E0',8(20)>
ruby-1.9.2-p0 > x*BigDecimal('3') == 1
=> false
ruby-1.9.2-p0 > x*BigDecimal('3') - 1
=> #<BigDecimal:aa4fdf8,'-0.1E-7',4(20)>

Interesting. However, the BigDecimal constructor allows specification
of the minimum number of significant figures, so in this case we could
have done BigDecimal.new('1', 16).

ruby-1.9.2-p0 > x = BigDecimal.new('1', 16)/BigDecimal.new('3',16)
=> #<BigDecimal:a146bbc,'0.33333333E0',8(16)>
ruby-1.9.2-p0 > x*BigDecimal('3',16) - 1
=> #<BigDecimal:a114acc,'-0.1E-7',4(20)>

It doesn't make any difference how many digits you have, 1/3 as a
decimal number multiplied by 3 will never be equal to 1.

I'm aware of that, of course. In this particular case, we could use a
Rational to fix that. But that won't necessarily work with arbitrary
floating-point numbers...

...unless we recast them as Rationals and do all math that way. Hmm.

I am not sure why the above still gives only 8 digits accuracy
however, I would have thought it aught to be 16.

Yeah, that's peculiar. I'll have to run some tests.

(I can almost guarantee that Rubinius would get that one right, since I
wrote most of the BigDecimal library at one time. Don't know if they're
still using it.)

Colin

Best,