正確な浮動小数点演算ツールクラスおよびBigDecimal.valueOf(double d)とnew BigDecimal(double d)の違い
30674 ワード
ツールクラスは次のとおりです.
テスト:
BigDecimal.valueOfとnew BigDecimalの区別テストは以下の通りです.
実行後のテスト結果は次のとおりです.
テスト結果の推計により、new BigDecimal(double d)は浮動小数点データdを正確に10進法のBigDecimalに変換し、0.999はdoubleに正確に表示されないため、精度が失われる現象が発生していることが分かった.BigDecimal.valueOf(double d)は、まずdを文字列に変換し、ソースコードは以下の通りです.
正確に計算するにはBigDecimal.valueOf(double d)またはnew BigDecimal(String d)を使用し、new BigDecimal(double d)を使用しないでください.
package com.iscas.common.tools.core.arithmetic;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
*
*
* BigDecimal , ,
*
* @author zhuquanwen
* @vesion 1.0
* @date 2020/8/11 21:00
* @since jdk1.8
*/
public class FloatExactArithUtils {
private FloatExactArithUtils() {}
/**
*
* */
public static double add(double data1, double data2) {
BigDecimal b1 = BigDecimal.valueOf(data1);
BigDecimal b2 = BigDecimal.valueOf(data2);
return b1.add(b2).doubleValue();
}
/**
*
* */
public static double subtract(double data1, double data2) {
BigDecimal b1 = BigDecimal.valueOf(data1);
BigDecimal b2 = BigDecimal.valueOf(data2);
return b1.subtract(b2).doubleValue();
}
/**
*
* */
public static double multiply(double data1, double data2) {
BigDecimal b1 = BigDecimal.valueOf(data1);
BigDecimal b2 = BigDecimal.valueOf(data2);
return b1.multiply(b2).doubleValue();
}
/**
* ,
* */
public static double divide(double data1, double data2, int scale) {
if (scale < 0) {
throw new RuntimeException(" 0");
}
BigDecimal b1 = BigDecimal.valueOf(data1);
BigDecimal b2 = BigDecimal.valueOf(data2);
return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
}
/**
* , , 10
* */
public static double divide(double data1, double data2) {
return divide(data1, data2, 10);
}
}
テスト:
package com.iscas.common.tools.arithmetic;
import com.iscas.common.tools.core.arithmetic.FloatExactArithUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.math.BigDecimal;
/**
*
*
* @author zhuquanwen
* @vesion 1.0
* @date 2020/8/11 21:05
* @since jdk1.8
*/
@RunWith(JUnit4.class)
@Slf4j
public class FloatExactArithUtilsTests {
@Test
public void testAdd() {
double data1 = 0.01;
double data2 = 0.05;
log.debug(String.format(" :%f", (data1 + data2)));
double result = FloatExactArithUtils.add(data1, data2);
log.debug(String.format(" :%f", result));
Assert.assertEquals(0.06, result, 0);
}
@Test
public void testSub() {
double data1 = 0.051;
double data2 = 0.01;
log.debug(String.format(" :%f", (data1 - data2)));
double result = FloatExactArithUtils.subtract(data1, data2);
log.debug(String.format(" :%f", result));
Assert.assertEquals(0.041, result, 0);
}
@Test
public void testMultiply() {
double data1 = 0.051;
double data2 = 0.01;
log.debug(String.format(" :%f", (data1 * data2)));
double result = FloatExactArithUtils.multiply(data1, data2);
log.debug(String.format(" :%f", result));
Assert.assertEquals(0.00051, result, 0);
}
@Test
public void testDivide() {
double data1 = 0.051;
double data2 = 0.01;
log.debug(String.format(" :%f", (data1 / data2)));
double result = FloatExactArithUtils.divide(data1, data2, 1);
log.debug(String.format(" :%f", result));
Assert.assertEquals(5.1, result, 0);
}
@Test
public void testBigDecimal() {
double d = 0.999;
BigDecimal b1 = BigDecimal.valueOf(d);
BigDecimal b2 = new BigDecimal("0.999");
BigDecimal b3 = new BigDecimal(d);
System.out.printf("BigDecimal.valueOf(double d) :%s
", b1.toString() );
System.out.printf("new BigDecimal(String d) :%s
", b2.toString() );
System.out.printf("new BigDecimal(double d) :%s
", b3.toString() );
}
}
BigDecimal.valueOfとnew BigDecimalの区別テストは以下の通りです.
@Test
public void testBigDecimal() {
double d = 0.999;
BigDecimal b1 = BigDecimal.valueOf(d);
BigDecimal b2 = new BigDecimal("0.999");
BigDecimal b3 = new BigDecimal(d);
System.out.printf("BigDecimal.valueOf(double d) :%s
", b1.toString() );
System.out.printf("new BigDecimal(String d) :%s
", b2.toString() );
System.out.printf("new BigDecimal(double d) :%s
", b3.toString() );
}
}
実行後のテスト結果は次のとおりです.
BigDecimal.valueOf(double d) :0.999
new BigDecimal(String d) :0.999
new BigDecimal(double d) :0.99899999999999999911182158029987476766109466552734375
Process finished with exit code 0
テスト結果の推計により、new BigDecimal(double d)は浮動小数点データdを正確に10進法のBigDecimalに変換し、0.999はdoubleに正確に表示されないため、精度が失われる現象が発生していることが分かった.BigDecimal.valueOf(double d)は、まずdを文字列に変換し、ソースコードは以下の通りです.
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new BigDecimal(Double.toString(val));
}
正確に計算するにはBigDecimal.valueOf(double d)またはnew BigDecimal(String d)を使用し、new BigDecimal(double d)を使用しないでください.