java中小数的处理:高精度运算用bigDecimal类,精度保留方法,即舍入方式的指定

一、 计算机的小数计算一定范围内精确,超过范围只能取近似值:

    计算机存储的浮点数受存储bit位数影响,只能保证一定范围内精准,超过bit范围的只能取近似值。
    java中各类型的精度范围参见:http://blog.csdn.net/longshenlmj/article/details/47616481
编程时注意:
doulbe类型的数,不能用等号判定是否相等(或者是一定范围内可以)。因为两次同样的计算(除法)结果可能出现小数部分不同。甚至极端的时候,初始化两个小数时,都可能不相等(用数值和字符串分别初始化bigdecimal的小数就会不等)

java小数处理方法的经验总结:

(1)小数计算对精度无要求时,使用float节省时间。

(2)如果有精度要求,用BigDecimal类处理(初始化必须使用字符串,因为用数值初始化会得到近似值,不准确),然后设置保留位数和 舍入法(half_up四舍五入,half_even银行家,half_down向下取整)

(3)精度要求低时可转化为整数处理(集体统一扩大数量级):
    乘以10的级数转化为整数处理,小数点右移几位,但整数值不要超过对应类型的取值范围。比如保留4位小数,可统一乘以10000,然后只保留整数计算结果,保留近位的话就多乘一位。
    这种方式在RTB项目MDSP的算法核心模块中使用,几十万的投放量,用int或long就可以处理,更大范围的整数处理BigInteger。

这样的好处是:

a,计算快,除了除法,其他运算上整数计算(加减乘)节省时间;
b,除法中,小数部分可直接省略,或向上取整(小数大于0时则加1)也可以让小数点多移动一位来保留进位。

二、java专门的小数运算类:BigDecimal类型(比double和float小数运算更精准的小数计算)

    float和double只能用来做科学计算或者是工程计算,在广域数值范围上提供较为精确的快速近似计算;而在商业计算要求结果精确(比如,有的编程语言中提供了专门的货币类型来处理),所以Java使用java.math.BigDecimal专门处理小数精度
构造器描述:
    BigDecimal(int)       创建一个具有参数所指定整数值的对象。
    BigDecimal(double)    创建一个具有参数所指定双精度值的对象。
    BigDecimal(long)      创建一个具有参数所指定长整数值的对象。
    BigDecimal(String)    创建一个具有参数所指定以字符串表示的数值的对象。

使用原则:初始化小数必须用String来够造,BigDecimal(String),因为用double的小数是近似值,不是精确值。

BigDecimal成员方法
    add(BigDecimal)        对象自身与参数相加,然后返回这个对象。
    subtract(BigDecimal)   对象自身与参数相减,然后返回这个对象。
    multiply(BigDecimal)   对象自身与参数相乘,然后返回这个对象。
    divide(BigDecimal)     对象自身与参数相除,然后返回这个对象。
    toString()             BigDecimal对象的数值转换成对应的字符串。
    doubleValue()          BigDecimal对返回double值。
    floatValue()           BigDecimal对返回float。
    longValue()            BigDecimal对返回long值。
    intValue()             BigDecimal对返回int值。

三、java小数保留精度的舍入方式

1,java 常用的四舍五入法实现:

Math类中的round方法不能设置保留几位小数,但可以乘100达到保留2位的目的:
        Math.round(value*100)/100.0;

或者,直接用java.text.DecimalFormat指定保留几位小数,用哪几种舍入法:
        DecimalFormat decFormat = new DecimalFormat("#.00");
        decFormat.setRoundingMode(RoundingMode.HALF_UP);

2,java的8种舍入方式:

1、 ROUND_UP:向上取整(丢掉小数,整数加1) 远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。

2、ROUND_DOWN:向下取整(丢掉小数)。趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。

3、ROUND_CEILING:向正无穷方向走,始终不会减少计算值。如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;如果为负,则舍入行为与 ROUND_DOWN 相同。Math.round()方法就是使用的此模式。

4、ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。

5、 HALF_UP:四舍五入,最近数字舍入(5进)。

6、 HALF_DOWN:四舍六入,最近数字舍入(5舍)。

7、 HAIL_EVEN:银行家舍入法。四舍六入五偶舍。即舍弃位4舍6入,当为5时看前一位,奇进偶舍。向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
     也就是说,如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同; 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。
     注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。

8、ROUND_UNNECESSARY 断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

四、BigDecimal的java代码实例

package test;

import java.math.BigDecimal;

public class bigDecimalTest {

    public static void main(String[] args) {
        BigDecimal a = new BigDecimal(1);
        BigDecimal b = new BigDecimal(3);

        BigDecimal sum = new BigDecimal("0");
        for(int i=0;i<2;i++)
        {
            sum = sum.add(a);
        }
        //除法运算
        System.out.println("a/b="+a.divide(b,5,BigDecimal.ROUND_HALF_UP).doubleValue());
        System.out.println("a/b="+a.divide(b,6,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")));
        System.out.println("a/b="+a.divide(b,6,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")).doubleValue());
//      System.out.println("a/b="+a.divide(b,11).doubleValue()); //不指定舍入方式,报错“Invalid rounding mode”

        int mytt= 100;//整数可以随意初始化
        BigDecimal oirginC = new BigDecimal(Integer.toString(mytt));
        BigDecimal oirgin2 = new BigDecimal(mytt);
        System.out.println("oirginC:"+oirginC);
        System.out.println("oirgin2:"+oirgin2);

        BigDecimal scale = new BigDecimal("0.002");//小数必须用string, 否则会出现误差,如double
        double testfloat=0.001;
        BigDecimal scale2 = new BigDecimal(testfloat);
        System.out.println(scale);
        System.out.println(scale2);//输出为近似数0.001000000000000000020816681711721685132943093776702880859375

        System.out.println(scale.equals(scale2));//两个比较对象必须在相同尺度才能用equal,2.0和2.00不相等,所以要用compare
        System.out.println(scale.compareTo(scale2));//输出中,小于、等于、大于分别对应-1、0、1。可以用与不同尺度scale的对象,只要值相等就行了,如1.0等于1.00
        if(0 == b.compareTo(new BigDecimal("3.00"))){//不同尺度的数
            System.out.println("equal");
        }

        //bigDecimal乘法运算返回int类型
        System.out.println("multipy:"+oirginC.multiply(scale).intValue());
        //bigDecimal加法运算返回int类型
        System.out.println("add:"+b.add(a).doubleValue());
        //bigDecimal减法运算返回int类型
        System.out.println("substract:"+b.subtract(new BigDecimal(3.00)).doubleValue());

        //Math.ceil()向上取整,就是小数不为0则整数加1舍去小数
        int test1 =3;
        double test2=0.01;
        System.out.println(Double.valueOf(Math.ceil(test1 * test2)).intValue());
        System.out.println(Math.ceil(1.02));

    }
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 08-18

java中小数的处理:高精度运算用bigDecimal类,精度保留方法,即舍入方式的指定的相关文章

JAVA中的单例模式(采用单例模式定义的类)(转)

1     单例(Singleton)模式:单例模式确保某一个类只有一个 实例,而且该类只能是自己 实例化自己并向其他类公开 这个实例的对象创建 模式 采用单例模式的类:根据单例模式知道其要满足以下三点 1. 确保某一个类只有一个实例 2. 而且自己实例化 3. 并向其他类提供这个实例类 . 2    确保以上三点的所采用的编程策略     * 把构造方法声明为Private.确保只能由自己创建,避免外部创建实例或者被子类继承从而创造额外实例:    * 定义一个私有静态的该类的实例作为该类的数

Java中的null对象也可以访问static成员变量和方法

声明:本博客为原创博客,未经允许,不得转载!小伙伴们如果是在别的地方看到的话,建议还是来csdn上看吧(链接为 http://blog.csdn.net/bettarwang/article/details/26515271),看代码和提问.讨论都更方便. 一般来说,一个类的对象要在实例化之后才可以访问类中的成员变量和方法.如果它还是null,通常意义上我们就认为它不能访问类中的成员.实际上确实不提倡这样,而且null对象确实不能访问实例成员(变量和方法),否则会引发NULLPointerExc

java中商业数据计算时用到的类BigDecimal和DecimalFormat

1.引言 借用<Effactive Java>这本书中的话,float和double类型的主要设计目标是为了科学计算和工程计算.他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的.然而,它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合.但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦. 2.BigDecimal简介 BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成.如果为零或正数

《java并发编程实战》读书笔记4--基础构建模块,java中的同步容器类&amp;并发容器类&amp;同步工具类,消费者模式

上一章说道委托是创建线程安全类的一个最有效策略,只需让现有的线程安全的类管理所有的状态即可.那么这章便说的是怎么利用java平台类库的并发基础构建模块呢? 5.1 同步容器类 包括Vector和Hashtable,此外还包括在JDK1.2中添加的一些功能相似的类,这些同步的封装器类由Collections.synchronizedXxx等工厂方法创建的.这些类实现线程安全的方式是:将他们的状态封装起来,并对每个共有方法都进行同步,使得每次只能有一个线程能访问容器的状态. 关于java中的Vect

Java中的大数处理类BigInteger和BigDecimar浅析

这两个类位于java.math包内,要使用它们必须在类前面引用该包:import java.math.BigInteger;和import java.math.BigDecimal; BigInteger和BigDecimal分别表示不可变的任意精度的整数和不可变的有符号的任意精度的十进制数(浮点数).主要用于高精度计算中.这两个类使得java中的大数,高精度运算变得很简单. 下面从几个方面对BigInteger和BigDecima做一个简单的比较: 一.常量 BigInteger:ONE,ZE

Java中不同的并发实现的性能比较

Fork/Join框架在不同配置下的表现如何? Java 8的并行流也是毁誉参半.并行流(Parallel Stream)的语法糖令人兴奋不已.现在Java中实现并发编程存在多种方式,我们希望了解这么做所带来的性能提升及风险是什么.从经过260多次测试之后拿到的数据来看,还是增加了不少新的见解的,这里我们想和大家分享一下. ExecutorService vs. Fork/Join框架 vs. 并行流 在10年前,Java的并发还只能通过第三方库来实现.然后Java 5到来了,并引入了java.

java中常见的异常类

1. java.lang.nullpointerexception   这个异常大家肯定都经常遇到,异常的解释是"程序遇上了空指针",简单地说就是调用了未经初始化的对象或者是不存在的对象,这个错误经常出现在创建图片,调用数组这些操作中,比如图片未经初始化,或者图片创建时的路径错误等等.对数组操作中出现空指针,很多情况下是一些刚开始学习编程的朋友常犯的错误,即把数组的初始化和数组元素的初始化混淆起来了.数组的初始化是对数组分配需要的空间,而初始化后的数组,其中的元素并没有实例化,依然是空

深入Java核心 Java中多态的实现机制(1)

多态性是Java面向对象的一个重要机制,本文将向您详细介绍Java语言中多态性的实现原理和方法,通过多态一点带出更多Java面向对象有趣而实用的知识. 多态性是面向对象程序设计代码重用的一个重要机制,我们曾不只一次的提到Java多态性.在Java运行时多态性:继承和接口的实现一文中,我们曾详细介绍了Java实现运行时多态性的动态方法调度:今天我们再次深入Java核心,一起学习Java中多态性的实现. “polymorphism(多态)”一词来自希腊语,意为“多种形式”.多数Java程序员把多态看

JAVA中参数传递时值传递的机制分析

参数传递是什么? 在C的函数或是JAVA的方法中,向一个函数或方法内部传递一个参数,比如: void fun( int num ){ num+=2 ; } int a = 3 ; fun( a ) ; 这个a就被作为参数传入函数fun()中,作为a,然后返回或者不返回值 回到最初,函数的作用是复用,那么我们希望这个参数传递是什么样的呢?就是假如我们去掉函数的外衣,就让函数变成代码放到之前是函数的地方,那么很自然这里最后b的值会被改变,这可以说是最朴实的参数传递了,自然的样子. 但是人们又发明了另

Java基础知识强化10:Java中的中间缓存变量机制

1.对于自增运算++j与j++,由于加一的执行顺序不同,所以Java中有中间缓存变量来储存其单个表达式的值,而j的自增自减的结果依然保留在原来的变量储存区.因为本体是j的值,而单个表达式的值是中间产生的一个临时变量值,是在整条计算表达式结束后就可以抛弃的值,所以用个临时中间缓存变量在放就可以了.这就可以实现自增自减运算在计算时值的加减1顺序差异产生的表达式与本体值差异的两个变量储存. 2. 1 因为在计算过程中,使用了Java中间变量缓存机制.在java中,执行自增运算时,会为每一个自增操作分配