Win a copy of Think Java: How to Think Like a Computer Scientist this week in the Java in General forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Arithmetic using double

 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi all,
I have a query on the following:


This prints 828.94, but changing one digit to the following


prints 828.9300000000001

I know that this style of arithmetic is discouraged, especially where you need some precision e.g. money values so my questions are:

1) Why does the + operation give 13 decimal places in one case and 2 decimal places in another, caused simply by changing from .33 to .32 - did I cross some threshold? Is there any "pattern" to what combination of numbers produce an erroneous result or is it down to JVM, i.e. why did it happen in one case and not in the other above?

2) Why would you ever use the primitive type "double" when you cannot rely on simple arthmetic operations?

Thanks
Tom
 
fred rosenberger
lowercase baba
Bartender
Posts: 12122
30
Chrome Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
most decimal fractions cannot be accurately represented by powers of 2, but some can (i.e. .75 can be represented by 1/2 + 1/4).

The ones that can't are changed to something pretty close. you apparently found two numbers that work well when added, and two numbers that don't.

as to why you would use them - because often they're 'close enough'. Many engineering specs allow for tolerances. the bolt must be 27 mm long, +/- .02mm. You can use some formatting techniques to hide the extra digits if you don't like them.
 
Henry Wong
author
Marshal
Pie
Posts: 21114
78
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Is there any "pattern" to what combination of numbers produce an erroneous result or is it down to JVM, i.e. why did it happen in one case and not in the other above?


And as a side note, this has nothing to do with the JVM, or even Java. Java supports the IEEE standard for floating point numbers. This is the same standard that is supported by most of the modern day processors today, and hence, supported by most modern day computer languages.

Henry
 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hey guys,
Thanks for the prompt replies! I see, so the "pattern" I was talking about is whether or not the expected result can be represented as some combination of fractions that are a power of 2 e.g. 1/2, 1/8 etc.

We are getting seemingly "random" errors in our application where by monetary amounts that we would expect to be 0.0 are "sometimes" coming back as 0.01. We think it is down to the fact that some places in the code use += and + for arithmetic on doubles while others use BigDecimal.

The reason I mentioned the following:


quote: Is there any "pattern" to what combination of numbers produce an erroneous result or is it down to JVM, i.e. why did it happen in one case and not in the other above?

And as a side note, this has nothing to do with the JVM, or even Java. Java supports the IEEE standard for floating point numbers. This is the same standard that is supported by most of the modern day processors today, and hence, supported by most modern day computer languages.


is that the problem seems to only appear when deployed in Websphere running Linux, I cannot see it in Tomcat running on windows...but that doesnt make sense based on Henry's comment above...

Tom
 
Henry Wong
author
Marshal
Pie
Posts: 21114
78
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
is that the problem seems to only appear when deployed in Websphere running Linux, I cannot see it in Tomcat running on windows...but that doesnt make sense based on Henry's comment above...



Do you remember in first grade math, when they teach you that addition is associative? ie. order doesn't matter?

Well, it's not! Order does matter! (at least for IEEE floating point numbers) So, if you have two application that add numbers in different order, or call libraries with different implementations, etc., you will have different results.

As Fred mentioned, you can use some formatting techniques to hide the extra digits if you don't like them. Or do some rounding to get rid of the errors, but based on how the libraries are written, and what numbers you use, floating point precision errors can appear (or seem to not appear) anywhere.

Henry
[ October 01, 2008: Message edited by: Henry Wong ]
 
fred rosenberger
lowercase baba
Bartender
Posts: 12122
30
Chrome Java Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
as another note, you should not use floating point math for monetary calculations. you should use some kind of integer, and adjust to the atomic unit - in the U.S., you would do all computation in pennies, then re-format as needed.

even then, it can be tricky. You have to account for things like "if apples are 3 for a dollar, how much are they each?", with the answer being $0.33, $0.33 and $0.34.
 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

as another note, you should not use floating point math for monetary calculations. you should use some kind of integer, and adjust to the atomic unit - in the U.S., you would do all computation in pennies, then re-format as needed.


That reminds me of that Cher song "If I could turn back time"

Yep in retrospect double was not a good choice, although in my defense I only joined the company recently so all of it was already there

Would you guys not recommend BigDecimal for representing monetary amounts?
 
Campbell Ritchie
Sheriff
Posts: 48917
58
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
BigDecimal or integer arithmetic are both suitable for money. You can get a performance enhancement with integers, traded off against the risk of an overflow error. So BigDecimal is what is usually recommended.
 
Michael Johnson, CO
Greenhorn
Posts: 4
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Using BigDecimal also allows you to set your rounding strategy and the number of digits to show to the right of the decimal. Also, if you're required to return your answer as a double than you can just use BigDecimal's asDouble() method to return the result in the proper format after you've performed your calculations.
 
Rob Spoor
Sheriff
Pie
Posts: 20527
54
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Originally posted by Michael Johnson, CO:
BigDecimal's asDouble() method

Shouldn't that be doubleValue()?

Since BigDecimal (and BigInteger as well) extend Number, you can get all numeric primitives using <primitive>Value().
 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK makes sense, I think the key is to decide at the start what your strategy is for this sticky issue whereas I am in the middle of code that is already out there an whole scale refactoring will be met with major resistance....

BTW, since from reading the javadoc, the constructor BigDeciam(String s) is preferred method of instantiating a BigDecimal, why did they include the constructor BigDecimal(double d), since it even says in the javadoc that the results are unpredictable?! Is it because some applications allow tolerances?.... but in that case why not just use primitive double type?

Again, thanks for the input guys, really clearing my head
 
Campbell Ritchie
Sheriff
Posts: 48917
58
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Why should you restrict use of an API? If they thought somebody would want to use doubles to instantiate BigDecimals, they provided a constructor; remember it does give a warning about being unpredictable. If you want to add 12345678.9 and 0.00000000000000000987654321 with BigDecimal you will get a different result from double, so there is a possible use for BigDecimal over and above the double arithmetic.

But you want to avoid that constructor for use with money etc.
 
Tom Johnson
Ranch Hand
Posts: 142
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
OK, I guess its useful to have it as its "pitfalls" are documented.

thanks again
 
Campbell Ritchie
Sheriff
Posts: 48917
58
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You're welcome.

Of course if you were writing your own BigDecimal class you would omit that constructor!
 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic