Friday, July 16, 2021

Java Mistake 1 - Using float and double for monetary or financial calculation Example

Java is considered a very safe programming language compared to C and C++ as it doesn't have free() and malloc() to directly do memory allocation and deallocation, You don't need to worry about array overrun in Java as they are bounded and there is NO pointer arithmetic in Java. Still, there are some sharp edges in the Java programming language that you need to be aware of while writing enterprise applications. Many of us make a subtle mistake in Java which looks correct in the first place but turns out to be buggy when looked at carefully. In this series of java articles, I will be sharing some common Java mistakes programmers make while programming applications in Java.

As I have said earlier every day we learn new things but we forget something equally important. This again highlights the importance of code review and the following best practices in Java. In this part, we will discuss why double and float should not be used in the monetary or financial calculation where the exact result of the calculation is expected.

And, If you are new to Java then I also recommend you go through these Java Programming online courses to learn Java programming and development in a better and more structured way. This is one of the best and up-to-date courses to learn Java online.


Using double and float for exact calculation

This is one of the common mistakes Java programmers make until they are familiar with the BigDecimal class. When we learn Java programming we have been told that use float and double to represent decimal numbers it's not been told that result of a floating-point number is not exact, which makes them unsuitable for any financial calculation which requires exact result and not an approximation. 

Both float and double are designed for engineering and scientific calculation and many times don’t produce exact results also result of the floating-point calculation may vary from JVM to JVM. Look at below example of BigDecimal and double primitive which is used to represent monetary value, It quite clear that floating-point calculation may not be exact and one should use BigDecimal for financial calculations.



public class BigDecimalExample {
 
    public static void main(String args[]) throws IOException {
      
      //floating point calculation
      double amount1 = 2.15;
      double amount2 = 1.10;
      System.out.println("difference between 2.15 and 1.0 using double is: " + (amount1 - amount2));
    
      //Use BigDecimal for financial calculation
      BigDecimal amount3 = new BigDecimal("2.15");
      BigDecimal amount4 = new BigDecimal("1.10") ;
      System.out.println("difference between 2.15 and 1.0 using BigDecimal is: " + (amount3.subtract(amount4)));      
    }     
}
Output:
difference between 2.15 and 1.0 using double is: 1.0499999999999998
difference between 2.15 and 1.0 using BigDecmial is: 1.05


From above example of floating point calculation is pretty clear that result of floating point calculation may not be exact at all time and it should not be used in places where exact result is expected.

Using Incorrect BigDecimal constructor

Another mistake Java Programmers make is using wrong constructor of BigDecmial. BigDecimal has overloaded constructor and if you use the one which accept double as argument you will get same result as you do while operating with double. So always use BigDecimal with String constructor. here is an example of using BigDecmial constructed with double values:

//Creating BigDecimal from double values
BigDecimal amount3 = new BigDecimal(2.15);
BigDecimal amount4 = new BigDecimal(1.10) ;
System.out.println("difference between 2.15 and 1.0 using BigDecmial is: " + (amount3.subtract(amount4)));

Output:
difference between 2.15 and 1.0 using double is: 1.0499999999999998
difference between 2.15 and 1.0 using BigDecmial is: 1.049999999999999822364316059974953532218933105468750

I agree there is not much difference between these two constructor but you got to remember this.

Using result of floating point calculation in loop condition

One more mistake from Java programmer can be using result of floating point calculation for determining conditions on loop. Though this may work some time it may result  in infinite loop another time. See below example where your Java program will get locked inside infinite while loop:

double amount1 = 2.15;
double amount2 = 1.10;

while((amount1 - amount2) != 1.05){
  System.out.println("We are stuck in infinite loop due to comparing with floating point numbers");
}

Output:
We are stuck in an infinite loop due to comparing with floating-point numbers
We are stuck in an infinite loop due to comparing with floating-point numbers
……………
…………..

This code will result in an infinite loop because the result of subtraction of amount1 and amount 2 will not be 1.5 instead it would be "1.0499999999999998" which make the boolean condition true.

That’s all on this part of learning from mistakes in Java, bottom line is :
  • Don’t use float and double on monetary calculation.
  • Use BigDecimal, long, or int for monetary calculation.
  • Use BigDecimal with String constructor and avoid double one.
  • Don’t use floating-point results for comparing loop conditions.


Other Java tutorials you may like

19 comments :

Anonymous said...

Hey - that explain why all financial operations are computed using BigDecimal in my company.

You opened my eyes!

Anonymous said...

Hi Javin ,
great post! learnt about why to use BigDecimal.
Looking forward for this article series.

Anonymous said...

Funny to read about financial calculation. This java double format is useless for science too. Or want someone used that to calculate statistics of medical drugs safety for example?

I think this type actually is mistake. Seams that someone forgot to set all bits from 8 byte number to 0 and rounds some memory garbage from a tail in every operation. And the most evil you can not predict this error - next operation you can add it in your result or multiply

And BigDecimal is not a cure, because it is limited to use only functions - not operators. As for me it is horror to write some opeartion like a = x*b+c*b using BigDecimal class and it's functions.

Anonymous said...

can anyone multiply (200 digit number) with (200 digit number) without using BIGNUMBER?

Miller said...

Using float and double for financial calculations can be serious mistake. Great to see that you are educating people with this kind of practical advice, long is also not a perfect solution, as monetary calculation for notional and future predictions may go beyond range of long data type.

Javin @ ClassLoader in Java said...

@h143570, floating point calculation is a big topic in itself. Double or float can not be used whenever you need exact numbers. By the way, thanks for sharing those links.

Anonymous said...

float and double data types are mainly provided for scientific and engineering calculations. Java uses binary floating point calculations which is good for approximation but doesn't provide exact result. Bottom line is, don't use float/double when exact calculation is needed. You can not represent values like 0.1 or 0.01 or any negative power of 10 accurately in Java. calculating interest, expenses is one example of this.

Željko Trogrlić said...

This is not specific to Java: I learned that hard way while developing Clipper application. Old mainframes had BCD numbers for the very same reason.

Anonymous said...

You shouldn't use BigDecimal for financial calculations either. You should really use a specific Currency class instead. Imagine this: You calculate a discount of 50% on a price of $5.55. That results in a discount of $2.755 and a discounted price of $2.755. Since we cannot pay $2.755 we need to round the numbers. So we round them. Result: Discount is now $2.76 and price is now $2.76 . Total is $0.02 higher than the total price before discount. The solution is of course to calculate the discount, round it, and then *subtract* it from the original price, in order to get the discounted price. You can do this with a BigDecimal, but a designated Currency class is just a better approach. A Currency class with discount() and other methods on, which perform these calculations correctly.

Unknown said...

This is nonsense and a damn myth. Double is fine for financial calculations, given you understand its limitations, and only want cent precision.

The first important part to understand is that you can't use the == operator to determine equality for doubles (you shouldn't ever do this).

Instead, you use an operator like
boolean equalsWithinPrecision(double a, double b, double eps) {
return Math.abs(a-b) < eps;
}
where a sane value for eps varies with application. 1E-2, 1E-10, whatever. It's also common to have rounding as the last step in any sequence of calculations.

The second part is to understand that doubles begin to lose precision at very large values. Although this doesn't become a problem until about 2^52 (10^15-ish). So unless you're dealing in sums in the trillions of dollars, you're basically fine.

Anonymous said...

It is not a myth. Manually rounding on doubles is the same a applying duct tape: it will usually work, for a while... Its better to use a type/library that can represent your financial amounts exactly, with the exact/correct decimal rounding applied automatically after every operation.

Anonymous said...

By Jelle Foks,
The keyword for financial calculations, is not "arbitrary precision math", it is decimal fixed or floating point. The difference is in the rounding. There is a reason why it's defined as part of IEEE 754-2008, and if you don't use it where you should, people can end up with other people's money, that's why people tend to find it more important to do the calculation right than to do it fast.
See more discussion on this thread :
https://plus.google.com/b/116401207721513511359/116401207721513511359/posts

lingmaaki said...

Decimal

In case of financial applications it is better to use Decimal types because it gives you a high level of accuracy and easy to avoid rounding errors

Double

Double Types are probably the most normally used data type for real values, except handling money.

Float

It is used mostly in graphic libraries because very high demands for processing powers, also used situations that can endure rounding errors.

Moer info....Difference between Float , Double and Decimal

Ling

Bhujang said...

Nice post man

Houdini said...

and there is ***NO*** pointer arithmetic in Java

bohan said...

Both the article and most comments reflect the poor understanding that plague java programmers.

javin paul said...

Hello Bohan, can you please elaborate your point?

Anonymous said...

Hi Javin

Regarding your post stating
"there is pointer arithmetic in Java"

People like me refer your posts and assume that the contents are 100% true and never think of validating it from somewhere.

Today for the first time I skim through the comments and saw that

HoudiniDecember 10, 2016 at 1:06 AM
and there is ***NO*** pointer arithmetic in Java

I wasn't sure so I validated this and got to know that what this person said is true.


Could you please be more sensible while posting anything on your blog, I am not sure how much I read and out of which what is correct and what's wrong.


If there is any mistake or typo, you have got to update the content, may be not for you but for the sake of the readers like me.

javin paul said...

Hello Annoymous, yes there is NO pointer arithmetic in Java, that's the big difference between Java and C/C++ which provides direct memory access as well pointer arithmetic. Regarding your point, yes, that was a typo and should have bee updated long back. apologies from my side.

Post a Comment