Java中的浮点型(Double&Float)计算问题

在刚刚做完的一个项目中,遇到了double型计算不精确的问题。到网上查找后,问题得到解决。经验共享,在这里总结一下。

Java中的浮点数类型float和double不能够进行精确运算。这个问题有时候非常严重。比如,经过double型直接计算,1.4×1.5有时会得出2.0999999999999996的结果,但实际上,应该得到2.10。而且,类似的情况并不仅限于乘法计算。

在有的编程语言中提供了专门的货币类型来处理这种情况,但是Java没有。所以,在商业计算中我们要用:java.math.BigDecimal

BigDecimal一共有4个构造方法,其中不属于int的有两个,它们是:

1、BigDecimal(double val)

Translates a double into a BigDecimal.

2、BigDecimal(String val)

Translates the String repre sentation of a BigDecimal into a BigDecimal.

上面的API简要描述相当的明确,而且通常情况下,上面的那一个使用起来要方便一些。但是,第一个构造方法的详细说明中有:

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.

所以,如果需要精确计算,非要用String来构造BigDecimal不可。

一、解决方案:

现在已经可以解决这个问题了,原则是使用BigDecimal并且一定要用String来构造。

但是想像一下吧,如果要做一个加法运算,需要先将两个浮点数转为String,然后构造成BigDecimal,在其中一个上调用add方法,传入另一个作为参数,然后把运算的结果(BigDecimal)再转换为浮点数。没错,这样太繁琐了。

下面的工具类Arith可以简化这部分操作。它提供以下静态方法,包括加减乘除和四舍五入:

public static double add(double v1,double v2)

    public static double sub(double v1,double v2)

    public static double mul(double v1,double v2)

    public static double div(double v1,double v2)

    public static double div(double v1,double v2,int scale)

    public static double round(double v,int scale)

◆源文件Arith.java:

import java.math.BigDecimal;
    public class
Arith{
       
//默认除法运算精度
       
private static final int DEF_DIV_SCALE = 10;
       
//这个类不能实例化
       
private Arith(){

}
   
/**

* 提供精确的加法运算。

* @param v1 被加数

* @param v2 加数

* @return 两个参数的和

*/

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);

}

/**

*
提供(相对)精确的除法运算。当发生除不尽的情况时,由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){

if(scale<0){

throw new IllegalArgumentException(

"The scale must be a positive integer or zero");

}

BigDecimal b = new BigDecimal(Double.toString(v));

BigDecimal one = new BigDecimal("1");

return
b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();

}

}

时间: 2024-12-12 23:42:23

Java中的浮点型(Double&Float)计算问题的相关文章

Java中String转换Double类型 Java小数点后留两位

Java中String转换Double类型 double num1 = 0.0; String qq = "19.987"; num1 = Double.valueOf(qq.toString()); 太祖元年 Java小数点后留两位 double num1 =11; double num1 =21; String num1 =""; DecimalFormat df = new DecimalFormat(".00");num3 = df.fo

Java中如何解决double和float精度不准的问题

我们知道浮点数是无法在计算机中准确表示的,例如0.1在计算机中只是表示成了一个近似值,因此,对付点数的运算时结果具有不可预知性. 在进行数字运算时,如果有double或float类型的浮点数参与计算,偶尔会出现计算不准确的情况.如以下示例代码: package ex; public class BigDeciTest { public static void main(String[] args){ System.out.println(0.05+0.01); System.out.printl

Java 中 long 和 double 的原子性?

JVM中对long的操作是不是原子操作? 为什么对long的操作不是原子的? 在硬件,操作系统,JVM都是64位的情况下呢? java中基本类型中,long和double的长度都是8个字节,32位(4字节)处理器对其读写操作无法一次完成,那么,JVM,long和double是原子性的吗? JVM中对long的操作是不是原子操作? 首先,通过一段程序对long的原子性进行判断.测试程序如下: public class LongAtomTest implements Runnable {     p

java中如果需要精确的计算答案,请避免使用double类型与float类型

double类型与float类型主要用于科学计算与工程计算而设计的,用于二进制浮点计算.对于普通计算通常是结果不准确的,所以对于普通的浮点数的加减法等,解决的方法需要用int,long,BigDecimal来计算. 1.计算出现的问题? 计算结果如下: 显然结果不是我们所需要的.我们需要的结果是:0.88 所以,我们的程序应该这样写: 1 public static void main(String[] args) { 2 BigDecimal a=BigDecimal.valueOf(1.29

Java中比较不同的MD5计算方式

在项目中经常需要使用计算文件的md5,用作一些用途,md5计算算法,通常在网络上查询时,一般给的算法是读取整个文件的字节流,然后计算文件的md5,这种方式当文件较大,且有很大并发量时,则可能导致内存打爆掉.所以如下代码提供了几种方式.并通过计算一个323M的文件的md5和大小给出了,GC的一些信息 代码 /* * Copyright (C) 2016. All Rights Reserved. */ package me.nabil.mixed; import org.apache.common

java中四种阶乘的计算

package com.zf.s2;//创建一个包 import java.math.BigInteger;//导入类 import java.util.ArrayList; import java.util.List; public class TextFactorial {//操作计算阶乘的类     public static int simpleCircle(int num){//简单的循环计算的阶乘         int sum=1;         if(num<0){//判断传入

JAVA中日期转换和日期计算的方法

日期的格式有很多形式,在使用过程中经常需要转换,下面是各种类型转换的使用例子以及日期计算方法的例子. 一.不同格式日期相互转换方法 1 public class TestDateConvertUtil { 2 3 public static void main(String[] args) throws ParseException { 4 // 获取当前时间 5 Date date = new Date(); // 获取当前时间 6 long timestamp = System.curren

java中String类型转换为float类型

import java.io.*; public class Demo1{ public static void main(String args[]) { String df="12.2"; float a=Float.parseFloat(df); if(a>18) System.out.println("你年龄大于18,要对自己的行为负责"); else if(a>10 && a<18) System.out.println(

java中运算符的解析和计算

package com.LBH; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import com.singularsys.jep.Jep; import com.singularsys.jep.bigdecimal.BigDecComponents; public class JieXi { public stati