Come to the conclusion first :
Don't use it directly double Variables as constructors BigDecimal Parameters of .
There is a section on the line Java Code logic :
1, One from the interface JSON strand , There's a number in it :57.3.
2, analysis JSON And save this number in a float Variable .
3, Put this float Assign a value to a variable BigDecimal object , It's using BigDecimal Of double The construction of parameters :
new BigDecimal(double val)
4, Put this BigDecimal Save to MySQL database , The field type is decimal(15,2).
This code logic has been running online for a long time , The value stored in the database is 57.3 No problem , But today debug Found that , Step three BigDecimal Object does not hold a value 57.3, It is 57.299999237060546875, Obviously , There is a problem of accuracy .
As for the database, the correct 57.3 This is entirely because the field type is set to 2 Decimal place , exceed 2 Decimal places are rounded off , That's why we got the right result , amount to MySQL This accuracy problem is covered up for us .
I always think this is a pit , So I studied the relevant knowledge .
First of all BigDecimal Of double Parameter construction , In the official JDK This construct is described in the documentation :
public BigDecimal(double val)
Translates a double into a BigDecimal which is the exact decimal representation of the double's binary floating-point value. The scale of the returned BigDecimal is the smallest value such that (10scale × val) is an integer.
Notes:
The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.
When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.
Parameters:
val - double value to be converted to BigDecimal.
Throws:
NumberFormatException - if val is infinite or NaN.
It's like this :
1,BigDecimal(double val) structure , use double When parameters are used to construct a BigDecimal object .
2, But this structure is not very reliable (unpredictable), You might think BigDecimal(0.1) That is to say, proper is equal to 0.1, But what you think you think is what you think ? Not really ,BigDecimal(0.1) This product is actually equal to 0.1000000000000000055511151231257827021181583404541015625, Because exactly 0.1 In itself is not a double( Actually 0.1 Cannot represent any fixed length binary fraction ).
3,BigDecimal(String val) The structure is reliable ,BigDecimal(“0.1”) That is to say, proper is equal to 0.1, It is recommended that you use this structure .
4, If you have to use one double Variable to construct a BigDecimal, That's all right. , We kindly provide static methods valueOf(double), This method follows new Decimal(Double.toString(double)) The effect is the same .
To put it bluntly, don't take it directly double Variables as parameters , Best use String Type as parameter or use static method valueOf(double), I wrote an example to try :
public static void main(String[] args) {
float a=57.3f;
BigDecimal decimalA=new BigDecimal(a);
System.out.println(decimalA);
double b=57.3;
BigDecimal decimalB=new BigDecimal(b);
System.out.println(decimalB);
double c=57.3;
BigDecimal decimalC=new BigDecimal(Double.toString(c));
System.out.println(decimalC);
double d=57.3;
BigDecimal decimalD=BigDecimal.valueOf(d);
System.out.println(decimalD);
}Output results :
57.299999237060546875 57.2999999999999971578290569595992565155029296875 57.3 57.3
In the future, try to follow the routine recommended by the official , Otherwise, I don't know when I will dig a hole for myself .
Add :double turn bigDecimal Precision problem
float The accuracy of the : 2^23 7 position
double The accuracy of the : 2^52 16 position
Decimal system turn Binary system Poor accuracy exists
double g= 12.35; BigDecimal bigG=new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); // Expect to get 12.4 System.out.println(“test G:”+bigG.doubleValue()); test G:12.3
reason :
Definition double g= 12.35; In computer, binary representation may be like this : Defined a g=12.34444444444444449,
new BigDecimal(g) g still 12.34444444444444449 new BigDecimal(g).setScale(1, BigDecimal.ROUND_HALF_UP); obtain 12.3
The correct way to define it is to use a string constructor :
new BigDecimal(“12.35”).setScale(1, BigDecimal.ROUND_HALF_UP)
First of all, we have to discuss this problem from the computer itself . We know , The computer does not recognize any data except binary data . Whatever programming language we use , In what kind of compilation environment , All first Only after the source program is translated into binary machine code can it be recognized by the computer . Take the situation mentioned above as an example , In our source code 2.4 It's in decimal , Computers can't directly recognize , It's going to be binary first . But ask Here comes the question ,2.4 The binary representation of is not accurate 2.4, Instead, the closest binary representation is 2.3999999999999999. The reason is that floating point numbers consist of two parts : Index and mantissa , If you know how to convert a floating-point number from binary to decimal , It should be easy to understand . If in the process of this conversion , Floating point numbers are involved in the calculation , Then the conversion process will become unpredictable know , And it becomes irreversible . We have reason to believe that , It's in the process , There was a loss of precision . As for why some floating-point calculations get accurate results , It should also happen that the binary and The decimal system can be converted accurately . And when you output a single floating-point data , It can output correctly , Such as
double d = 2.4; System.out.println(d);
The output is 2.4, instead of 2.3999999999999999. in other words , When you don't do floating-point calculations , In decimal system, floating point numbers can display correctly . This confirms what I think above , That is, if the floating-point number is involved in the calculation , The conversion between binary and decimal floating-point numbers becomes unpredictable , And it becomes irreversible .
in fact , Floating point numbers are not suitable for precise calculations , And it's suitable for scientific calculation . Here is a little knowledge : since float and double Type is used to denote a number with a decimal point , Then why don't we say They are “ decimal ” perhaps “ The set of real Numbers ”, What about floating point numbers ? Because these numbers are stored in scientific notation . When a number such as 50.534, The form converted to scientific counting is 5.053e1, it The decimal point of has moved to a new position ( That's floating ). so , Floating point numbers are used for scientific calculation , It's really inappropriate to use it for accurate calculation .
stay 《Effective Java》 This principle is also mentioned in this book ,float and double It can only be used for scientific calculation or engineering calculation , In Business Computing, we use java.math.BigDecimal. Use BigDecimal And be sure to use String Come and make it .
BigDecimal Which constructor to use ?
BigDecimal(double val) BigDecimal(String val)
above API The brief description is quite clear , And usually , The one above should be more convenient to use . We may use it without even thinking about it , What's the problem ? When something goes wrong , It is found that the parameter is double There is such a paragraph in the detailed description of the construction method of :
Note: the results of this constructor can be somewhat unpredictable. One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to .1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the long value that is being passed in to the constructor is not exactly equal to .1, appearances nonwithstanding.
The (String) constructor, on the other hand, is perfectly predictable: new BigDecimal(".1") is exactly equal to .1, as one would expect. Therefore, it is generally recommended that the (String) constructor be used in preference to this one.
So if we need accurate calculation , Must use String Come and make it BigDecimal must not !








