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.

on 2005-12-13 02:05

on 2005-12-13 02:14

Quoting Hunter's Lists <removed_email_address@domain.invalid>: > 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

on 2005-12-13 02:15

unknown wrote: > Quoting Hunter's Lists <removed_email_address@domain.invalid>: > >> 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.

on 2005-12-13 02:23

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

on 2005-12-13 02:23

On 12/12/05, Hunter's Lists <removed_email_address@domain.invalid> 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.

on 2005-12-13 03:38

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 S. - removed_email_address@domain.invalid 'A republic, if you can keep it.' -- Benjamin Franklin

on 2005-12-13 06:54

"Wilson B." <removed_email_address@domain.invalid> 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

on 2005-12-13 07:12

On 12/12/05, removed_email_address@domain.invalid <removed_email_address@domain.invalid> 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?

on 2005-12-13 09:06

Joe Van D. 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

on 2005-12-13 17:02

On Tue, 13 Dec 2005, Neil S. 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

on 2005-12-13 22:34

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

on 2005-12-14 00:35

Quoting Malte M. <removed_email_address@domain.invalid>: > Joe Van D.: > > 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

on 2005-12-17 00:15

Joe Van D. wrote: >On 12/12/05, removed_email_address@domain.invalid <removed_email_address@domain.invalid> 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

on 2005-12-17 00:42

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

on 2005-12-17 00:45

On Dec 16, 2005, at 4:39 PM, Stephen W. 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 G. II

on 2005-12-17 00: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

on 2005-12-17 01:01

James Edward G. II wrote: > On Dec 16, 2005, at 4:39 PM, Stephen W. 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

on 2005-12-17 01:04

On Dec 16, 2005, at 4:58 PM, Stephen W. 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 G. II

on 2005-12-17 01:04

On 12/16/05, James Edward G. II <removed_email_address@domain.invalid> wrote: > On Dec 16, 2005, at 4:39 PM, Stephen W. 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 F.

on 2005-12-17 01:13

Stephen W. 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 S. - removed_email_address@domain.invalid 'A republic, if you can keep it.' -- Benjamin Franklin

on 2005-12-17 01:28

James Edward G. 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