精华内容
下载资源
问答
  • 假设我们在做电商项目,在进行计算时这个丢失精度在产品价格计算就会出现问题,很有可能造成我们手里有9.99元然后后面会有一堆9,但是呢这些钱无法购买一个10元的商品。 在某些编程语言中有专门处理货币的类型,...

    问题描述及方案

    • 假设我们在做电商项目,在进行计算时这个丢失精度在产品价格计算就会出现问题,很有可能造成我们手里有9.99元然后后面会有一堆9,但是呢这些钱无法购买一个10元的商品。
      在某些编程语言中有专门处理货币的类型,但是Java没有,不过没关系我们可以通过BigDecimal来解决这个问题。
    • 下面我们来看几个例子。

    testOne
    - Test1
    - 这个呢就是Java本身对于浮点计算的时候会丢失精度,一定要注意,一定要注意,它所引起的事情呢基本没有小事,如果在线上订购量大的话,会引起大的故障,可能会导致下不了订单或是对账出现问题

    testTwo
    - Test2
    - 当使用BigDecimal后结果就更乱了,比test1结果还可怕又长又乱,它算出来的数比0.06是多的还是刚才那个意思,假设我们现在,
    银行卡里面有0.06元然后我买的两个商品,分别是0.05元和0.01,当我下单的时候如果没有做处理那么他需要付的是0.06000000000000000298372437868010820238851010799407958984375 这就导致余额不够,订单无法下。这还没解决,不要紧,接着看

    testThree
    - Test3
    - 用String的BigDecimal构造器,这个结果就是我们想要的,本身呢BigDecimal这个类型是用它来解决这个问题但是我们在选择用它的时候,一定一定一定要选择它的String构造器一旦不用就像test2一样会发生严重过的精度问题,这个原则在《Effective Java》这本书中也有说,是说这个原则如果float和double只能用来做 科学计算或者工程计算,但是在商业计算中我们要用BigDecimal。

    源码分析

    1.说明
    - 在java.math包中找到API类BigDecimal,然后找到BigDecimal(double)构造方法
    - 说明
    - 源代码里写的很明白这个结果和这个构造器就会产生这个种问题它是无限接近于这么一个数这个构造器呢不是正好的,而是等于0.1

    2.用法
    - 用法
    - 如果用这个构造器呢,把它转成String,用Double的toString(double),里面放double然后用BigDecimal(String)这个String去构造,
    也就是说如果要用的话,就把它转成String然后在选择用BigDecimal的String构造器去获得结果。另外3个这里就不讲了自己去看;
    - 为了方便我们可以把它写成一个util类,日后我整理完了会把它放在GitHub上。

    原文:http://www.godql.com/blog/2017/05/17/Precision/
    作者:Dr.Lester

    展开全文
  • 最近连续俩次遇到运算后数值精度丢失问题,所以记录一下。 问题1:java计算百分比,应该得到57,可返回的就是56  在java代码中 BigDecimal progress; BigDecimal a = new BigDecimal(POF); BigDecimal ...

    最近连续俩次遇到运算后数值精度丢失问题,所以记录一下。

    问题1:java计算百分比,应该得到57,可返回的就是56

      在java代码中

            BigDecimal progress;
    
            BigDecimal a = new BigDecimal(POF);
            BigDecimal b = new BigDecimal(RF);
            if(b.compareTo(a)==1&&b.subtract(a).longValue()<1000000){
                return 100;
            }
            progress = a.divide(b,2,BigDecimal.ROUND_UP);
            return progress*100 ;         

    这段代码的计算都是用BigDecimal运算的,这没什么问题。可惜啊!return progress*100 ; 功亏一篑!

     

            BigDecimal progress;
    
            BigDecimal a = new BigDecimal(POF);
            BigDecimal b = new BigDecimal(RF);
            if(b.compareTo(a)==1&&b.subtract(a).longValue()<1000000){
                return 100;
            }
            progress = a.divide(b,2,BigDecimal.ROUND_UP);
            BigDecimal multiply = progress.multiply(new BigDecimal(100));
            return multiply.intValue();

     

    问题二:    在jsp页面中对金额计算,0.00元竟然还带个负号

     

     在java中测试

     

    double浮点型,计算结果确实不是0,而是一个近似0的负数。

    解决方案:

    (由于我后台返回的数据都是保留小数点后两位,所以对运算数字先升级运算,再对结果降级处理

    累计其他 :{{$itzUtils.thousands(((dczqt*100-dczdcz*100)/100).toFixed(2))}}元</span>

     

    转载于:https://www.cnblogs.com/caoyajun33-blog/p/8029299.html

    展开全文
  • 并且,使用 BigDecimal 的 Double 入参的构造方法同样存在精度丢失问题,应该使用 String 入参的构造方法或者 BigDecimal.valueOf 方法来初始化。 第二,对浮点数做精确计算,参与计算的各种数值应该始终使用 ...

    1. “危险”的 Double

       先从简单的反直觉的四则运算看起。对几个简单的浮点数进行加减乘除运算

    System.out.println(0.1+0.2);
    System.out.println(1.0-0.8);
    System.out.println(4.015*100);
    System.out.println(123.3/100);
    
    double amount1 = 2.15;
    double amount2 = 1.10;
    if (amount1 - amount2 == 1.05)
        System.out.println("OK");
    
    结果如下:
    
    0.30000000000000004
    0.19999999999999996
    401.49999999999994
    1.2329999999999999

               可以看到,输出结果和我们预期的很不一样。比如,0.1+0.2 输出的不是 0.3 而是 0.30000000000000004。出现这种问题的主要原因是,计算机是以二进制存储数值的,浮点数也不例外。Java 采用了IEEE 754 标准实现浮点数的表达和运算,你可以通过这里查看数值转化为二进制的结果。比如,0.1 的二进制表示为 0.0 0011 0011 … (0011 无限循环),再转换为十进制就是0.1000000000000000055511151231257827021181583404541015625。对于计算机而言,0.1 无法精确表达,这是浮点数计算造成精度损失的根源。

          优化方案:

           BigDecimal 类型,浮点数精确表达和运算的场景,一定要使用这个类型。不过,在使用 BigDecimal 时有几个坑需要避开。我们用 BigDecimal 把之前的四则运算改一下:

    
    System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2)));
    System.out.println(new BigDecimal(1.0).subtract(new BigDecimal(0.8)));
    System.out.println(new BigDecimal(4.015).multiply(new BigDecimal(100)));
    System.out.println(new BigDecimal(123.3).divide(new BigDecimal(100)));
    
    结果如下:
    
    0.3000000000000000166533453693773481063544750213623046875
    0.1999999999999999555910790149937383830547332763671875
    401.49999999999996802557689079549163579940795898437500
    1.232999999999999971578290569595992565155029296875

       可以看到,运算结果还是不精确,只不过是精度高了而已。浮点数运算避坑第一原则:使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化 BigDecimal

           

    
    System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
    System.out.println(new BigDecimal("1.0").subtract(new BigDecimal("0.8")));
    System.out.println(new BigDecimal("4.015").multiply(new BigDecimal("100")));
    System.out.println(new BigDecimal("123.3").divide(new BigDecimal("100")));
    
    结果如下:
    
    0.3
    0.2
    401.500
    1.233

    2.用 equals 做判等,就一定是对的吗?

       现在我们知道了,应该使用 BigDecimal 来进行浮点数的表示、计算。并且包装类的比较要通过 equals 进行,而不能使用 ==。那么,使用 equals 方法对两个 BigDecimal 判等,一定能得到我们想要的结果吗?

    boolean  res  = new BigDecimal("1.0").equals(new BigDecimal("1"));
    System.out.println(res);
    
    结果如下:
    false

      BigDecimal 的 equals 方法的注释中说明了原因,equals 比较的是 BigDecimal 的 value 和 scale,1.0 的 scale 是 1,1 的 scale 是 0,所以结果一定是 false。源码如下:

    
    /**
     * Compares this {@code BigDecimal} with the specified
     * {@code Object} for equality.  Unlike {@link
     * #compareTo(BigDecimal) compareTo}, this method considers two
     * {@code BigDecimal} objects equal only if they are equal in
     * value and scale (thus 2.0 is not equal to 2.00 when compared by
     * this method).
     *
     * @param  x {@code Object} to which this {@code BigDecimal} is
     *         to be compared.
     * @return {@code true} if and only if the specified {@code Object} is a
     *         {@code BigDecimal} whose value and scale are equal to this
     *         {@code BigDecimal}'s.
     * @see    #compareTo(java.math.BigDecimal)
     * @see    #hashCode
     */
    @Override
    public boolean equals(Object x)

       优化方案:

         如果我们希望只比较 BigDecimal 的 value,可以使用 compareTo 方法。

       

     boolean  res  = new BigDecimal("1.0").compareTo(new BigDecimal("1")) == 0;
     System.out.println(res);

    3. 小心数值溢出问题

          数值计算还有一个要小心的点是溢出,不管是 int 还是 long,所有的基本数值类型都有超出表达范围的可能性。比如,对 Long 的最大值进行 +1 操作,显然这是发生了溢出,而且是默默地溢出,并没有任何异常。

          

    
    long l = Long.MAX_VALUE;
    System.out.println(l + 1);
    System.out.println(l + 1 == Long.MIN_VALUE);
    
    结果如下:
    
    -9223372036854775808
    true

         优化方案:

         方案一:使用 Math 类的 addExact、subtractExact 等 xxExact 方法进行数值运算,这些方法可以在数值溢出时主动抛出异常

          

    
    try {
        long l = Long.MAX_VALUE;
        System.out.println(Math.addExact(l, 1));
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    
    运行结果:
    
    java.lang.ArithmeticException: long overflow
      at java.lang.Math.addExact(Math.java:809)
      at org.geekbang.time.commonmistakes.numeralcalculations.demo3.CommonMistakesApplication.right2(CommonMistakesApplication.java:25)
      at org.geekbang.time.commonmistakes.numeralcalculations.demo3.CommonMistakesApplication.main(CommonMistakesApplication.java:13)

        方案二:使用大数类 BigInteger。BigDecimal 是处理浮点数的专家,而 BigInteger 则是对大数进行科学计算的专家。

        

    
    BigInteger i = new BigInteger(String.valueOf(Long.MAX_VALUE));
    System.out.println(i.add(BigInteger.ONE).toString());
    
    
    运行结果
    
    9223372036854775808
    

    重点回顾

    今天,我与你分享了浮点数的表示、计算、舍入和格式化、溢出等涉及的一些坑。

    第一,切记,要精确表示浮点数应该使用 BigDecimal。并且,使用 BigDecimal 的 Double 入参的构造方法同样存在精度丢失问题,应该使用 String 入参的构造方法或者 BigDecimal.valueOf 方法来初始化。

    第二,对浮点数做精确计算,参与计算的各种数值应该始终使用 BigDecimal,所有的计算都要通过 BigDecimal 的方法进行,切勿只是让 BigDecimal 来走过场。任何一个环节出现精度损失,最后的计算结果可能都会出现误差。

    第三,对于浮点数的格式化,如果使用 String.format 的话,需要认识到它使用的是四舍五入,可以考虑使用 DecimalFormat 来明确指定舍入方式。但考虑到精度问题,我更建议使用 BigDecimal 来表示浮点数,并使用其 setScale 方法指定舍入的位数和方式。

    第四,进行数值运算时要小心溢出问题,虽然溢出后不会出现异常,但得到的计算结果是完全错误的。我们考虑使用 Math.xxxExact 方法来进行运算,在溢出时能抛出异常,更建议对于可能会出现溢出的大数运算使用 BigInteger 类。

    总之,对于金融、科学计算等场景,请尽可能使用 BigDecimal 和 BigInteger,避免由精度和溢出问题引发难以发现,但影响重大的 Bug。

    思考与讨论

    1.BigDecimal提供了 8 种舍入模式,你能通过一些例子说说它们的区别吗?

    2.数据库(比如 MySQL)中的浮点数和整型数字,你知道应该怎样定义吗?又如何实现浮点数的准确计算呢?

    展开全文
  • 之前需要接收数据库小数点后保留四位的数值,刚开始使用BigDecimal 来接收,接收0.3568 这种数值没有问题,当数值为0.6220时候,接收到的数为0.622,由于数据保留的位数必须一致,所以采用了下面的方法来解决。...

    原因:

         之前需要接收数据库小数点后保留四位的数值,刚开始使用BigDecimal 来接收,接收0.3568 这种数值没有问题,当数值为0.6220时候,接收到的数为0.622,由于数据保留的位数必须一致,所以采用了下面的方法来解决。

     

    一.转成String,在实体类属性上直接添加

        @JsonFormat(shape = JsonFormat.Shape.STRING)
        private BigDecimal value;

    二.使用String 来接收数值,可以保证精度不丢失

        

    展开全文
  • 下面小编就为大家带来一篇解决java数值范围以及float与double精度丢失的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • java类型转换精度丢失

    千次阅读 2019-06-03 20:19:05
    从图中我们可以看出存储占用字节少的类型向存储占用字节多的类型转化不会丢失精度,而存储占用字节多的类型向存储占用字节少的类型(包括占用字节一样的类型)转换都可能发生精度丢失,那么为什么会造成精度丢失呢?...
  • @Configuration public class JacksonConfig { /** * Jackson全局转化long类型为String,解决jackson序列化时传入前端Long类型缺失精度问题 */ @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2...
  • 前端请求后端接口获取BigDecimal类型字段数值丢失精度,例如:5999.00变成5999、5999.50变成5999.5。 2、解决方法: 在字段上添加 @JsonFormat(shape = JsonFormat.Shape.STRING)注解,在序列化数据时会以String...
  • 啥都不说,先看一个例子 public static void main(String[] ... //加减乘除都出现了对应的精度问题 System.out.println(0.05 + 0.01); //0.060000000000000005 System.out.println(1.0 - 0.42); //0.580000000...
  • 1.java中int,float,long,double取值范围 public class TestOutOfBound { public static void main(String[] args) { System.out.println(Integer.MAX_VALUE-(-Integer.MAX_VALUE)); //内存溢出 System.out.println...
  • 在开发过程中,我们经常会发现浮点类型的数值精度丢失,比如:System.out.println(2.0 - 1.1)我们肯定觉得应该输出0.9,但是程序输出的是0.8999999999999999在java程序中,JDK提供了BigDecimal类,以解决精度丢失...
  • java精度丢失问题

    千次阅读 2015-02-03 10:30:04
     精度   举例:double result = 1.0 - 0.9; 这个结果不用说了吧,都知道了,0.09999999999999998 float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,这是为了在广泛的数字范围...
  • JAVA BigDecimal的相加 丢失精度问题

    万次阅读 2018-03-20 17:30:34
    在处理BigDecimal 对象的 数值相加的问题上遇到麻烦,借鉴了 JAVA BigDecimal的相加 的文章,但是依然没有解决我的问题。其文章分析如下(纯属借鉴!)代码如下:[java] view plain copyBigDecimal totalAmount...
  • 修复Long类型太长,而Java序列化JSON丢失精度问题的方法 Java序列化JSON时long型数值,会出现精度丢失的问题。 原因: java中得long能表示的范围比js中number大,也就意味着部分数值在js中存不下(变成不准确的值). ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 12,095
精华内容 4,838
关键字:

java数值丢失精度

java 订阅