views:

397

answers:

2

I working on different web-services, and I always use WSDL First.

JAXB generates for a Type like:

<xsd:simpleType name="CurrencyFormatTyp">
    <xsd:restriction base="xsd:decimal">
        <xsd:totalDigits value="13"/>
        <xsd:fractionDigits value="2"/>
        <xsd:minInclusive value="0.01"/>
    </xsd:restriction>
</xsd:simpleType>

a Java binding type BigDecimal (as it's mentioned in JAXB specification).

When I then do some simple arithmetic operation with values of the type double (which are stored in a database and mapped via hibernate to the type double) I run into trouble.

<ns5:charge>0.200000000000000011102230246251565404236316680908203125</ns5:charge>        
<ns5:addcharge>0.0360000000000000042188474935755948536098003387451171875</ns5:addcharge>
<ns5:tax>0.047199999999999998900879205621095024980604648590087890625</ns5:tax>
<ns5:totalextax>0.2360000000000000153210777398271602578461170196533203125</ns5:totalextax>

What would be the right way?

  1. Convert all my values into double (JAXB binding from BigDecimal to double)
  2. Hibernate mapping double to Bigdecimal

and do all my arithmetic operations in one object type.

+3  A: 

You don't ever want to use floating point formats (such as double and float in Java) for currency operations, because they have limited precision and have been designed to hold values that are somehow derived from measuring (in which case they aren't absolutely precise to begin with, in which case the limited precision is less of a problem).

What Every Computer Scientist Should Know About Floating-Point Arithmetic is the article on the topic. It's a bit heavy on math, but it really helps to understand that (Alternatively, the article linked to by Michael Borgwardt is much easier to understand and still demonstrates/explains the problem).

To avoid this kind of problems, make sure that you use BigDecimal exclusively in your code and that all external storage/transfer points use fixed-point/arbitrary-precision values as well (i.e. your database shouldn't store floating point numbers either).

Joachim Sauer
so you recommend to do all calculations with types of BigDecimal, and leave the storage type at doubel (SQL Anywhere DB).
Alex
@Alex: No, that **not** what I said. What I'm saying is this: "Do all calculations with `BigDecimal` and make sure use a datatype that can keep the same precision in storage (most databases have a fixed-point datatype, usually called "decimal" or something similar).
Joachim Sauer
@Joachim Sauer: I converted the database types in decimal, and do all calculation with BigDecimal type. My Problem is fixed now. Thank you.
Alex
+3  A: 
  • Read the Floating-Point Guide
  • Never use double or float for money amounts
  • Use BigDecimal instead, that's exactly what it's for
Michael Borgwardt
That's a good resource, much easier to grok than the article I linked ;-)
Joachim Sauer
@Joachim: that's exactly why I wrote it :)
Michael Borgwardt
@Michael Borgwardt: Great resource, thank you!
Alex