Double precision problem in Java and bigdecimal solutions

Double precision problem in Java and bigdecimal solutions

 

Recently wrote a free point of the old J2EE code and found a very interesting question, when using Hibernate to float read out from the database to do some work, such as the accumulation of such summary or descending like it You will find that there will be little problem in the final results.
Such as: 3.41 + 5.2 + 56.2 + 23.3 + ... (the price of these two decimal places), the results appear 103.00000000000001 this result, but people count, then it will come to the normal data. It appears double, float up the accuracy of such data will have such problems.
    Then, turn the data points to provide a special type of currency in any programming language to handle this situation, but not Java.
    Rounded
our first reaction is to do rounding. Math class round Method reservation can not be set to several decimal places, we only
like this (two reserved):
public Double round (Double value) {
    return Math.round (value * 100) /100.0;
}
Unfortunately, the above and the code does not work, give this method will return the incoming 4.015 4.01 instead of 4.02,
as we see above
4.015 * 100 = 401.49999999999994
So if we want to achieve precise rounding, can not do any type of operation with a simple
java .text.DecimalFormat can not solve this problem:
System.out.println (new new java.text.DecimalFormat ( "0.00") format (4.025).);
output is 4.02

BigDecimal
in "Effective Java" This book also mentioned this principle, float and double can only be used for scientific computing or
engineering computing, business computing, we use java.math.BigDecimal. BigDecimal fill a total of four making party
law, we do not care enough to make use of those two BigInteger, then there are two, they are:
BigDecimal (Double Val)
          . Translates A Double INTO A BigDecimal
BigDecimal (String Val)
          Translates a String of repre- sentation at the INTO a BigDecimal
BigDecimal.
briefly described above API is quite clear, and usually, the one above to be convenient to use. I
have to use it might think about it, what is the problem? Wait until a problem when it found that the above parties which made enough
detailed description of the method has this to say:
Note:. Results of the this constructor at The CAN BE Somewhat Unpredictable One Might
the ASSUME that new new BigDecimal (.1) IS exactly equal is to. 1, but it is actually equal
.1000000000000000055511151231257827021181583404541015625 to. This IS SO Because
.1 CAN BE Not Represented exactly AS A Double (or, for that Matter, AS A binary
fraction of the any Finite length). THUS, The Long that value being passed in to IS
The IS Not exactly constructor .1 to equal is, Appearances nonwithstanding.
at The (String) constructor, ON at The Hand OTHER, IS Perfectly Predictable: new new
BigDecimal (. "1") IS exactly equal is to .1, AS One Would the Expect THEREFORE, IT IS.
GeneRally Recommended that at The (String) constructor bE used in preference to
the this One.

That we need if accurate calculation, have to make a String to BigDecimal not enough! In the "Effective Java"
example of a book is enough to create a String to BigDecimal, but the book did not emphasize this point, this may be a
small mistake it.

solution
Now we have to solve this problem, the principle is to use BigDecimal and must use String enough to build.
But imagine it, if we do an addition operation, you need to first two floating-point numbers into String, then create enough
to BigDecimal, in which a call to add on, passing another as a parameter, then the result of the operation (
BigDecimal) and then converted to floating-point numbers. You can put up such a cumbersome process? Below we provide a tool class
Arith to streamline operations. It provides the following static methods, including addition, subtraction and rounding:
public static Double the Add (Double v1, v2 Double)
public static Double Sub (Double v1, v2 Double)
public static Double MUL (Double v1, v2 Double)
public static Double div (Double v1, v2 Double)
public static Double div (Double v1, v2 Double, int Scale)
public static Double round (Double v, int Scale)

Appendix

source file Arith.java:
Package Penalty for com.common.util;
Import Classes in java.math .BigDecimal;

public class Arith Final {
// default division precision
private static final int DEF_DIV_SCALE = 2;
// 这个类不能实例化
private Arith() {
}

public static double add(double v1, double v2) {
  BigDecimal b1 = new BigDecimal(Double.toString(v1));
  BigDecimal b2 = new BigDecimal(Double.toString(v2));
  return b1.add(b2).doubleValue();
}

public static double sub(double v1, double v2) {
  BigDecimal b1 = new BigDecimal(Double.toString(v1));
  BigDecimal b2 = new BigDecimal(Double.toString(v2));
  return b1.subtract(b2).doubleValue();
}

public static double mul(double v1, double v2) {
  BigDecimal b1 = new BigDecimal(Double.toString(v1));
  BigDecimal b2 = new BigDecimal(Double.toString(v2));
  return b1.multiply(b2).doubleValue();
}

public static double div(double v1, double v2) {
  return div(v1, v2, DEF_DIV_SCALE);
}

public static double div(double v1, double v2, int scale) {
  if (scale < 0) {
   throw new IllegalArgumentException(
   "The scale must be a positive integer or zero");
  }
  BigDecimal b1 = new BigDecimal(Double.toString(v1));
  BigDecimal b2 = new BigDecimal(Double.toString(v2));
  return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}

public static double round(double v, int scale) {

Published 900 original articles · won praise 387 · Views 2.79 million +

Guess you like

Origin blog.csdn.net/kingmax54212008/article/details/104016809