Forum: Ruby Using Float For Currency

A7db9ec803b5895ae5f916a74e2db329?d=identicon&s=25 Hunter's Lists (Guest)
on 2005-12-13 01:05
(Received via mailing list)
Howdy,

I have some methods that manipulate floats that represent a currency
amount.

I often end up with more precision than I need, i.e.: $9.756.

What is the best way to scale that to 9.76?

Cheers.
912c61d9da47754de7039f4271334a9f?d=identicon&s=25 unknown (Guest)
on 2005-12-13 01:14
(Received via mailing list)
Quoting Hunter's Lists <lists@lastonepicked.com>:

> Howdy,
>
> I have some methods that manipulate floats that represent a
> currency amount.
>
> I often end up with more precision than I need, i.e.: $9.756.
>
> What is the best way to scale that to 9.76?

If you're doing anything that matters, don't use floats for
currency.  There are a lot of really nasty subtle issues that will
lose money between the cracks.

Usually you want a specialized currency type which uses
fixed-precision arithmetic.

-mental
3a83969376c805ef5b6042191fdb0ff3?d=identicon&s=25 Andreas S. (andreas)
on 2005-12-13 01:15
unknown wrote:
> Quoting Hunter's Lists <lists@lastonepicked.com>:
>
>> Howdy,
>>
>> I have some methods that manipulate floats that represent a
>> currency amount.
>>
>> I often end up with more precision than I need, i.e.: $9.756.
>>
>> What is the best way to scale that to 9.76?
>
> If you're doing anything that matters, don't use floats for
> currency.  There are a lot of really nasty subtle issues that will
> lose money between the cracks.
>
> Usually you want a specialized currency type which uses
> fixed-precision arithmetic.

BigDecimal is good.
A7db9ec803b5895ae5f916a74e2db329?d=identicon&s=25 Hunter's Lists (Guest)
on 2005-12-13 01:23
(Received via mailing list)
Thanks. Fortunately this is just some quick guestimation throw-away
stuff.

No real need for precision. I will take a look at BigDecimal but in the
meantime, any thoughts on the original question?

Thx
25e11a00a89683f7e01e425a1a6e305c?d=identicon&s=25 Wilson Bilkovich (Guest)
on 2005-12-13 01:23
(Received via mailing list)
On 12/12/05, Hunter's Lists <lists@lastonepicked.com> wrote:
> Howdy,
>
> I have some methods that manipulate floats that represent a currency amount.
>
> I often end up with more precision than I need, i.e.: $9.756.
>
> What is the best way to scale that to 9.76?
>
Try:
val = 9.756
sprintf("%.2f", val)
That says "print 'val' as a floating point value, with two decimal
places of precision.
3c155ef399326d533efc2eb91ac992e5?d=identicon&s=25 Neil Stevens (Guest)
on 2005-12-13 02:38
(Received via mailing list)
Hunter's Lists wrote:
> Howdy,
>
> I have some methods that manipulate floats that represent a currency amount.
>
> I often end up with more precision than I need, i.e.: $9.756.
>
> What is the best way to scale that to 9.76?

require 'bigdecimal'

amount = BigDecimal.new('9.756')
rounded = (amount * 100).round / 100
printf('%.02f', rounded)

Outputs '9.76'
--
Neil Stevens - neil@hakubi.us

'A republic, if you can keep it.' -- Benjamin Franklin
0b561a629b87f0bbf71b45ee5a48febb?d=identicon&s=25 Dave Burt (Guest)
on 2005-12-13 05:54
(Received via mailing list)
"Wilson Bilkovich" <wilsonb@gmail.com> wrote:
> val = 9.756
> sprintf("%.2f", val)
> That says "print 'val' as a floating point value, with two decimal
> places of precision.

Or just
"%.2f" % val

Cheers,
Dave
38a8230ed3d5c685558b4f0aad3fc74b?d=identicon&s=25 Joe Van Dyk (Guest)
on 2005-12-13 06:12
(Received via mailing list)
On 12/12/05, mental@rydia.net <mental@rydia.net> wrote:
>
> If you're doing anything that matters, don't use floats for
> currency.  There are a lot of really nasty subtle issues that will
> lose money between the cracks.
>
> Usually you want a specialized currency type which uses
> fixed-precision arithmetic.

What cracks can I lose money through?
280b41a88665fd8c699e83a9a25ef949?d=identicon&s=25 Stephen Waits (Guest)
on 2005-12-13 08:06
(Received via mailing list)
Joe Van Dyk wrote:
>
> What cracks can I lose money through?

Floating point numbers represent an extremely wide range of values -
much wider than their integer counterparts.  This is handled through an
exponent and mantissa.  For this ability, they trade off precision.

Think about the case of adding a large floating point number to a small
floating point number:

     irb(main):001:0> a = 1.0e30
     => 1.0e+030
     irb(main):002:0> b = 1.0e-30
     => 1.0e-030
     irb(main):003:0> a + b
     => 1.0e+030

While this is an extreme example, it does demonstrate the loss of
precision.  Essentially, in floating point arithmetic we're trying to
squeeze much more out of, say 32 or 48 or 64 bits.

Integer arithmetic, on the other hand, is exact.  And therefore so is
fixed point arithmetic; however, fixed point doesn't enjoy the wide
representation range as floats.

The bottom line is you should never use floating point when it comes to
money.  Eventually you're going to miss pennies.  Instead represent
things in the smallest denomination, such as cents, and fix it up in
presentation, or use a custom Money column type, or data type.

There's your cracks!  Just say no... unless you're a hot chick.  Even
then it's questionable.

--Steve
B7f73e9f9d35be6a44b15f603f2910d4?d=identicon&s=25 Wybo Dekker (Guest)
on 2005-12-13 16:02
(Received via mailing list)
On Tue, 13 Dec 2005, Neil Stevens wrote:

>
> amount = BigDecimal.new('9.756')
> rounded = (amount * 100).round / 100
> printf('%.02f', rounded)
>
> Outputs '9.76'

There is at least one point more to make about rounding: this method has
a
bias when floats ending with a 5 are involved. In the extreme case, when
all you three-decimal currency amounts end in a 5 (like $9.755, $9.765)
then all are rounded upwards. Instead, half of them should be rounded
up,
the other half down. A good method to do that is to round to the
nearest even digit:

  $9.755 -> $9.76
  $9.765 -> $9.76 (not $9.77)

The attached method round2 does this (although probably not very
efficiently):
  8.5.round2         -> 8
  9.765.round2(0.01) -> 9.76
C3274d0d109830f7f5121a7d8f7ead98?d=identicon&s=25 Malte Milatz (Guest)
on 2005-12-13 21:34
(Received via mailing list)
Joe Van Dyk:
> What cracks can I lose money through?

irb> 0.2 - 0.05 - 0.15
     => 2.77555756156289e-17

You're actually gaining money here.

Malte
912c61d9da47754de7039f4271334a9f?d=identicon&s=25 unknown (Guest)
on 2005-12-13 23:35
(Received via mailing list)
Quoting Malte Milatz <malte__@gmx-topmail.de>:

> Joe Van Dyk:
> > What cracks can I lose money through?
>
> irb> 0.2 - 0.05 - 0.15
>      => 2.77555756156289e-17
>
> You're actually gaining money here.

Indeed.

The critical take-home lesson:

Floating point arithmetic only approximates arithmetic with real
numbers.

Never write code which assumes that math with floating-point code
will observe the normal laws of arithmetic (most common mistake:
assuming == is useful for testing the equality of two
floating-point results).

If you're unsure, it might be better not to use floating-point at
all.

-mental
04d072ab8843cfd3d1714faf3a2a0fb2?d=identicon&s=25 mathew (Guest)
on 2005-12-16 23:15
(Received via mailing list)
Joe Van Dyk wrote:
>On 12/12/05, mental@rydia.net <mental@rydia.net> wrote:
>>If you're doing anything that matters, don't use floats for
>>currency.  There are a lot of really nasty subtle issues that will
>>lose money between the cracks.
[...]
>
> What cracks can I lose money through?

It's not just that; your program logic can also behave unexpectedly.
e.g.

irb> 1.20 - 1.00 == 0.20
=> false

Use BigDecimal for currency. I've just finished some new Rdoc
documentation for it which will hopefully be added to ruby-doc.org and
the 1.9 release, and can forward you a copy if you like.


mathew
280b41a88665fd8c699e83a9a25ef949?d=identicon&s=25 Stephen Waits (Guest)
on 2005-12-16 23:42
(Received via mailing list)
mathew wrote:
> Use BigDecimal for currency. I've just finished some new Rdoc
> documentation for it which will hopefully be added to ruby-doc.org and
> the 1.9 release, and can forward you a copy if you like.

Hi mathew,

Excuse my ignorance, but I'm wondering what BigDecimal does different
than the built-in integer types?

Thanks,
Steve
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2005-12-16 23:45
(Received via mailing list)
On Dec 16, 2005, at 4:39 PM, Stephen Waits wrote:

> mathew wrote:
>> Use BigDecimal for currency. I've just finished some new Rdoc
>> documentation for it which will hopefully be added to ruby-doc.org
>> and the 1.9 release, and can forward you a copy if you like.
>
> Hi mathew,
>
> Excuse my ignorance, but I'm wondering what BigDecimal does
> different than the built-in integer types?

It doesn't use floating point arithmetic.  That means it is accurate,
but slower.

James Edward Gray II
Fe9b2d0628c0943af374b2fe5b320a82?d=identicon&s=25 Eero Saynatkari (rue)
on 2005-12-16 23:51
Hunter's Lists wrote:
> Howdy,
>
> I have some methods that manipulate floats that represent a currency
> amount.
>
> I often end up with more precision than I need, i.e.: $9.756.
>
> What is the best way to scale that to 9.76?

I think this has already been covered, but:

  "Money does not float!"

:)

> Cheers.


E
280b41a88665fd8c699e83a9a25ef949?d=identicon&s=25 Stephen Waits (Guest)
on 2005-12-17 00:01
(Received via mailing list)
James Edward Gray II wrote:
> On Dec 16, 2005, at 4:39 PM, Stephen Waits wrote:
>
>> Excuse my ignorance, but I'm wondering what BigDecimal does different
>> than the built-in integer types?
>
> It doesn't use floating point arithmetic.  That means it is accurate,
> but slower.

Hi James,

I think you misread my question.  I realize the difference between
Integer and FP math, I even explained it earlier in this thread.

I'm asking why one would use BigDecimal, specifically, as opposed to the
built-in Integer types?

Thanks,
Steve
4299e35bacef054df40583da2d51edea?d=identicon&s=25 James Gray (bbazzarrakk)
on 2005-12-17 00:04
(Received via mailing list)
On Dec 16, 2005, at 4:58 PM, Stephen Waits wrote:

> I think you misread my question.

Sorry about that.

> I'm asking why one would use BigDecimal, specifically, as opposed
> to the built-in Integer types?

Well, BigDecimal lets you work with decimals, but will be slower.
Integers will be faster, but you'll need to handle the conversions,
as needed.

Hope I got it right that time.  ;)

James Edward Gray II
Cff9eed5d8099e4c2d34eae663aae87e?d=identicon&s=25 Jacob Fugal (Guest)
on 2005-12-17 00:04
(Received via mailing list)
On 12/16/05, James Edward Gray II <james@grayproductions.net> wrote:
> On Dec 16, 2005, at 4:39 PM, Stephen Waits wrote:
> > Excuse my ignorance, but I'm wondering what BigDecimal does
> > different than the built-in integer types?
>
> It doesn't use floating point arithmetic.  That means it is accurate,
> but slower.

And built-in integer types represent integers, not decimals. :)

That does however, bring up the point of using an integer
representation instead of floating point, eg. manipulating integer
pennies instead of decimal dollars. If you are certain that integer
arithmetic -- with it's corresponding truncation; eg. (5 / 3) * 3
returns 3, not 5 -- is sufficient for your application, it can be
faster than BigDecimal. AFAIK, BigDecimal is doing this same thing
(integer representation), behind the scenes, but is slower due to the
requirements of being flexible in how many decimal places it
represents.

Another class to look into along these lines -- slower but more
encapsulated than a "roll your own" integer representation, and more
domain specific than BigDecimal -- is the great Money library from
Tobias
L├╝tke:
  http://dist.leetsoft.com/api/money/

Jacob Fugal
3c155ef399326d533efc2eb91ac992e5?d=identicon&s=25 Neil Stevens (Guest)
on 2005-12-17 00:13
(Received via mailing list)
Stephen Waits wrote:
> I'm asking why one would use BigDecimal, specifically, as opposed to the
> built-in Integer types?

One would use BigDecimal instead of Fixnum or Bignum when one is going
to do some operation that might produce a fraction.  If you're certain
your operations will always result in whole numbers, though, I don't see
 a point in dragging in BigDecimal.

--
Neil Stevens - neil@hakubi.us

'A republic, if you can keep it.' -- Benjamin Franklin
280b41a88665fd8c699e83a9a25ef949?d=identicon&s=25 Stephen Waits (Guest)
on 2005-12-17 00:28
(Received via mailing list)
James Edward Gray II wrote:
>
> Well, BigDecimal lets you work with decimals, but will be slower.
> Integers will be faster, but you'll need to handle the conversions, as
> needed.

Ahh, I see what BigDecimal is now.  Think I'd rather just use cents and
Integers.

--Steve
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.