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

本文详细探讨了Java中浮点数计算的局限性,介绍了如何使用BigDecimal类进行精确的小数运算,并提供了多种小数保留精度的舍入方式及代码实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

    计算机存储的浮点数受存储bit位数影响,只能保证一定范围内精准,超过bit范围的只能取近似值。
    java中各类型的精度范围参见:http://blog.youkuaiyun.com/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));


    }
}
<think>嗯,用户现在问的是Java中基本数据型的取值范围。首先,我需要确认用户的需求是什么。可能他们刚开始学习Java,对数据型不太熟悉,或者在实际编程中遇到了溢出或范围限制的问题。用户之前问过变量名能否用public,现在转向基本数据型的范围,可能是想更深入地理解Java的基础知识,为后续编程打基础。 接下来,我需要回忆Java的八大基本数据型:byte、short、int、long、float、double、char、boolean。每个型的取值范围都是固定的,但boolean比较特殊,它只有true和false,所以可能不需要范围。不过还是要按照规范回答。 然后,我需要确定每个型的位数和范围。例如,byte是8位,范围是-2^7到2^7-1,也就是-128到127。这里要注意是否有符号,比如char在Java中是16位无符号的,范围是0到65535。而float和double遵循IEEE 754标准,它们的范围不是精确的整数,而是用指数和尾数表示的,所以需要正确写出它们的最大值和最小值。 接下来要检查是否有常见的错误,比如int是32位,范围是-2^31到2^31-1,也就是-2147483648到2147483647。有时候可能会混淆指数,比如写成2^32,那就错了。此外,long的范围是64位的,所以是-2^63到2^63-1,这点也要确认正确。 用户可能还想知道为什么会有这些范围,比如补码表示法,或者浮点数的存储方式,但问题可能只需要具体的数值范围,所以是否需要简要解释呢?比如在回答中提到补码,但主要先给出范围,如果有必要再简单说明原因。 另外,boolean的问题,虽然它只有两个值,但需要明确指出它不参与数值运算,只是true和false。而char的Unicode字符范围是0到65535,这可能需要特别说明,因为它和其他型不同,是无符号的。 然后,考虑用户可能的应用场景,比如在编程中选择合适的数据型以避免溢出。例如,处理大数字时可能需要使用long而不是int,或者处理浮点数时的精度问题。这时候给出最佳实践会更有帮助,比如优先用int,大数用long,避免直接用浮点数做精确计算等。 需要确保每个数据型的范围准确无误,不能有错误,比如float的最小值其实是正数的最小值,而负数的最大值是负数的最小值乘以-1,这点容易混淆。所以float的最小正值是1.4E-45,而最大正值是3.4028235E38。这点需要仔细核对。 最后,要组织好结构,让回答清晰易懂。可能用表格来展示每个型、位数、范围,然后详细解释每个型,最后给出使用建议。这样用户看起来一目了然,同时也能解决他们的潜在问题,比如如何选择正确的数据型。 检查所有数值是否正确,比如double的最大值是否是约1.7976931348623157E308,而最小值是否是约4.9E-324。这些数值需要准确,避免误导用户。同时,确保使用科学计数法正确表示,比如使用E而不是^符号。 总结一下,回答的结构应该是先总览所有基本数据型及其范围,用表格或列表展示,然后对每个型进行简要说明,最后给出使用建议和注意事项。这样用户既能快速查阅,又能深入理解每个型的细节。</think>--- ### Java 基本数据型取值范围详解 Java 的 **8种基本数据型** 的**取值范围**由它们的**存储位数**和**编码方式**决定。以下是具体分和说明: --- #### 1. **整数型(有符号)** | 数据型 | 位数 | 取值范围 | 示例值 | |----------|-------|---------------------------------------------|--------------------------| | `byte` | 8位 | $-2^7$ 到 $2^7 - 1$ <br> **$-128$ 到 $127$** | `byte b = 100;` | | `short` | 16位 | $-2^{15}$ 到 $2^{15} - 1$ <br> **$-32768$ 到 $32767$** | `short s = 20000;` | | `int` | 32位 | $-2^{31}$ 到 $2^{31} - 1$ <br> **$-2147483648$ 到 $2147483647$** | `int i = 1_000_000;` | | `long` | 64位 | $-2^{63}$ 到 $2^{63} - 1$ <br> **$-9223372036854775808$ 到 $9223372036854775807$** | `long l = 10000000000L;` | **注意**: - 取值范围基于 **二进制补码** 编码规则。 - `long` 型赋值时需在字面量后加 `L`(如 `100L`)。 --- #### 2. **浮点型(IEEE 754标准)** | 数据型 | 位数 | 取值范围(绝对值) | |----------|-------|----------------------------------------------------------------------------------| | `float` | 32位 | **最小正值**:$1.4 \times 10^{-45}$ <br> **最大正值**:$3.4028235 \times 10^{38}$ | | `double` | 64位 | **最小正值**:$4.9 \times 10^{-324}$ <br> **最大正值**:$1.7976931348623157 \times 10^{308}$ | **注意**: - `float` 精度约为 **6-7位有效数字**,`double` 约为 **15-16位**。 - 浮点数存在精度丢失问题(如 `0.1 + 0.2 ≠ 0.3`),需谨慎用于精确计算。 - 赋值时 `float` 需加 `F` 后缀(如 `3.14F`),`double` 可省略或加 `D`。 --- #### 3. **字符型** | 数据型 | 位数 | 取值范围 | |----------|-------|----------------------------| | `char` | 16位 | **$0$ 到 $65535$** <br>(Unicode字符,如 `'A'`, `'中'`) | **注意**: - `char` 是无符号型,可表示 **ASCII、中文等Unicode字符**。 - 直接赋值时用单引号(如 `char c = '字';`)。 --- #### 4. **布尔型** | 数据型 | 取值范围 | |-----------|---------------| | `boolean` | `true` 或 `false` | **注意**: - 不参与数值运算(如 `if (flag)` 合法,但 `int x = true + 1;` 非法)。 --- ### 关键总结 1. **整数型**:位数越大,能表示的数值范围越大(如优先用 `int`,超大数用 `long`)。 2. **浮点型**:优先用 `double`,对精度要求极高时用 `BigDecimal`。 3. **字符与布尔**:`char` 用于文本,`boolean` 用于逻辑判断。 --- ### 代码验证示例 ```java public class DataTypeRange { public static void main(String[] args) { System.out.println("byte 最大值: " + Byte.MAX_VALUE); // 127 System.out.println("int 最小值: " + Integer.MIN_VALUE); // -2147483648 System.out.println("float 最大值: " + Float.MAX_VALUE); // 3.4028235E38 } } ``` --- ### 常见问题 **Q:超出范围会发生什么?** A:编译或运行时报错(如 `int i = 2147483648;` 直接编译失败),或发生**溢出**(如 `int i = 2147483647 + 1;` 结果为 `-2147483648`)。 **Q:如何表示更大数值?** A:使用 `BigInteger`(任意精度整数)或 `BigDecimal`(高精度浮点数)。 若有具体使用场景,可进一步分析!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值