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

("291.15".to_f * 100.0).to_i

=> 29114

Thanks,

Tom

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:

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:

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.

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?

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.

You probably guessed it might be me

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?

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?

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:

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?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.3333333333333333Now 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.

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.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.0E39What 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,