精华内容
下载资源
问答
  • 最近连续俩次遇到运算后数值精度丢失问题,所以记录一下。 问题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

    展开全文
  • 假设我们在做电商项目,在进行计算时这个丢失精度在产品价格计算就会出现问题,很有可能造成我们手里有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

    转载于:https://www.cnblogs.com/xiaobufan/p/7076986.html

    展开全文
  • Java 中隐藏着一个看似是 bug 的冷门现象:在一些数值计算中得不到你想象的结果,会多很多位小数点后面的数字。其实,这是浮点型数字的精度损失问题,本文简单做一个现象记录,以供读者参考。在此说明,以下内容...

    在 Java 中隐藏着一个看似是 bug 的冷门现象:在一些数值计算中得不到你想象的结果,会多很多位小数点后面的数字。其实,这是浮点型数字的精度损失问题,本文简单做一个现象记录,以供读者参考。

    在此说明,以下内容中涉及的代码已经被我上传至 Github:LossPrecision ,读者可以提前下载查看。

    现象演示记录

    假如读者按照下面的代码运行,猜猜看是什么结果。1

    2

    3

    4

    5

    6

    7

    8

    9

    10public void lossPrecisionTest () {

    // 结果不等于 0.06

    log.info ("====sum:[{}]", 0.05 + 0.01);

    // 结果不等于 0.58

    log.info ("====sum:[{}]", 1 - 0.42);

    // 结果不等于 401.5

    log.info ("====sum:[{}]", 4.015 * 100);

    // 结果不等于 1.233

    log.info ("====sum:[{}]", 123.3 / 100);

    }

    请读者看到结果不要惊讶,是的,你没有看错,运行结果真的不是你想象的那样,总会多一点或者少一点。1

    2

    3

    42020-01-24_00:38:55 [main] INFO javabug.LossPrecision:15: ====sum:[0.060000000000000005]

    2020-01-24_00:38:55 [main] INFO javabug.LossPrecision:17: ====sum:[0.5800000000000001]

    2020-01-24_00:38:55 [main] INFO javabug.LossPrecision:19: ====sum:[401.49999999999994]

    2020-01-24_00:38:55 [main] INFO javabug.LossPrecision:21: ====sum:[1.2329999999999999]

    在 Java 中的简单浮点数类型 float 和 double,有时候不能够进行运算,其实不光是在 Java 中,在其它很多编程语言中也有这样的问题【本质在于硬件寄存器存储二进制数字会有精度损失】。尽管在大多数的情况下,计算的结果是准确的,但是有时候会出现意想不到的精度损失问题,读者也需要注意。

    那么如何解决这个问题呢?【下面示例都以 4.015 这个数字演示】

    简单四舍五入

    我的第一个反应是做四舍五入【通过四舍五入把多余的数字尾巴清除掉,保留正确的数值】,Math 类中的 round 方法不能设置保留几位小数,只能像这样保留两位小数:1

    2

    3// 1-Math 四舍五入

    double val = 4.015;

    log.info ("====sum:[{}]", Math.round (val * 100) / 100.0);

    非常不幸,上面的代码并不能正常工作,得到的结果是错误的,给这个方法传入 4.015 它将返回 4.01 而不是 4.02,如我们在上面看到的:4.015 * 100 = 401.49999999999994。

    得到结果:12020-01-24_01:04:13 [main] INFO javabug.LossPrecision:25: ====round:[4.01]

    它只能保留 2 位小数,而且得到的结果还是错误的。

    因此,如果我们需要做到精确的四舍五入,不能利用简单类型做任何运算,要想想其它方法。

    数值格式化

    那么这种问题还有没有其它办法呢?当然有,可以使用 DecimalFormat 格式化,代码如下:1

    2

    3

    4// 2-DecimalFormat 格式化,四舍五入,保留 2 位小数

    DecimalFormat decimalFormat = new DecimalFormat ("0.00");

    decimalFormat.setRoundingMode (RoundingMode.HALF_UP);

    log.info ("====format:[{}]", decimalFormat.format (val));

    运行后读者又发现,并没有得到想象的结果,仍旧是错误的,因为计算过程还是涉及到数值的精度损失问题。12020-01-24_02:12:56 [main] INFO javabug.LossPrecision:33: ====format:[4.01]

    此时想必一些读者已经陷入了懵圈。

    大数值计算

    那么这种问题有没有其它办法可以彻底解决问题呢?当然有,可以使用 BigDecimal 计算。

    其实,float 和 double 只能用来做 科学计算或者是 工程计算【允许损失一定的数值精度】,而在 商业计算中我们要用 BigDecimal【不允许损失数值精度】,精度是可以保证的。

    但是要注意,BigDecimal 有 2 种构造方法,一个是:BigDecimal (double val),另外一个是:BigDecimal (String val),请确保使用 String 来构造,否则在计算时还是会出现精度丢失问题,这算是 BigDecimal 的一个坑,很多人应该也遇到过。

    代码示例:1

    2

    3

    4

    5

    6

    7

    8// 3-BigDecimal, 四舍五入,保留 2 位小数

    BigDecimal bigDecimal1 = new BigDecimal (double.toString (val));

    BigDecimal bigDecimal2 = new BigDecimal (double.toString (1D));

    log.info ("====multiply:[{}]", bigDecimal1.multiply (bigDecimal2).setScale (2, BigDecimal.ROUND_HALF_UP));

    // 如果直接使用 double 构造,得到的结果仍旧是错误的

    BigDecimal bigDecimal3 = new BigDecimal (val);

    BigDecimal bigDecimal4 = new BigDecimal (1D);

    log.info ("====multiply:[{}]", bigDecimal3.multiply (bigDecimal4).setScale (2, BigDecimal.ROUND_HALF_UP));

    运行结果:1

    22020-01-24_02:12:56 [main] INFO javabug.LossPrecision:37: ====multiply:[4.02]

    2020-01-24_02:12:56 [main] INFO javabug.LossPrecision:41: ====multiply:[4.01]

    可以看到,使用 String 构造 BigDecimal 对象可以准确计算结果,而使用 double 构造 BigDecimal 对象还是会损失精度。

    工具类

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

    但是想像一下,如果我们要做一个加法运算,需要先将两个浮点数转为 String 类型,然后再构造成 BigDecimal 对象,在其中一个对象上调用 add 方法,传入另一个 BigDecimal 对象作为参数。然后把运算的结果,也是一个 BigDecimal 对象,再转换为浮点数。

    我们能够忍受这么烦琐的过程吗?肯定不能,所以我在此提供一个工具类 BigDecimalUtil 来简化操作,它提供以下静态方法【参考下面的方法声明】,包括加减乘除和四舍五入,调用时可以传参从而灵活设置结果的精度和取舍的模式【四舍五入、去尾、进位等等】。

    代码已经被我上传至 Github:BigDecimalUtil ,在这里就只贴出方法声明【类注释中可以看到】,读者可以自行下载使用。1

    2

    3

    4

    5

    6

    7

    8

    9

    10/**

    * 大数值计算工具类

    *

    * @see #add (double, double, int, int)

    * @see #subtract (double, double, int, int)

    * @see #multiply (double, double, int, int)

    * @see #div (double, double)

    * @see #div (double, double)

    * @see #round (double, int, int)

    */

    试运行结果如下:

    备注

    小问题

    以下记录一个常见的判断差值为 0 的小问题。

    如果在项目中碰到了如下的业务逻辑计算:1

    2

    3

    4

    5

    6

    7

    8

    9

    10double val1 = 61.5;

    double val2 = 60.4;

    double dif = 1.1;

    // 判断差值结果为 0 的问题

    if (Math.abs (val1 - val2 - dif) == 0) {

    log.info ("==== 差值结果为 0");

    //do things

    } else {

    log.info ("==== 差值结果不为 0");

    }

    结果发现这一组数据:61.5、60.4、1.1 无法达到正确的预期结果,即结果不为 0,有些人可能想破了脑袋也无法发现问题所在【千万不要试图拿计算器计算的结果对比,因为这是精度损失的问题】。

    如果是有经验的开发人员一眼就可以发现问题所在,也知道应该采用如下的方式修改代码:1

    2

    3

    4

    5

    6

    7

    8// 加上允许精度损失的判断逻辑

    double exp = 10E-10;

    if (Math.abs (val1 - val2 - dif) > -1 * exp && Math.abs (val1 - val2 - dif) < exp) {

    log.info ("==== 差值结果为 0");

    //do things

    } else {

    log.info ("==== 差值结果不为 0");

    }

    这样的话,运行结果就会与期望一致了【同样的数值,只是更改了判断逻辑:允许精度损失】。

    引申问题

    除了精度损失的问题,还有一种 byte 类型自动转换的坑【当然,编译器有可能自动识别了问题代码,无法通过编译】。

    有数值或者变量参与的加法运算,结果会转为 int 类型,再赋值给 byte 类型的变量无法通过编译,问题代码如下:1

    2

    3

    4

    5

    6

    7

    8

    9

    10

    11

    12

    13

    14

    15

    16

    17

    18

    19

    20

    21

    22

    23

    24// 类型自动转换问题

    int num1 = 5;

    //1 - 类型无法强转,编译无法通过

    // byte num2 = num1;

    byte num3 = 5;

    byte num4 = 127;

    //2 - 溢出,编译无法通过

    // byte num5 = 128;

    byte num6 = 12;

    //3 - 类型无法强转,编译无法通过 (有数值参与加法运算,结果会转为 int 类型)

    // num6 = num6 + 1;

    num6 += 1;

    num6++;

    // 类型自动转换问题

    int i = 7;

    byte b = 5;

    // 1 - 类型无法强转,编译无法通过 (有数值参与加法运算,结果会转为 int 类型)

    // b = b + b;

    b += b;

    // 2 - 类型无法强转,编译无法通过 (有数值参与加法运算,结果会转为 int 类型)

    // b = b + 7;

    // 3 - 类型无法强转,编译无法通过 (有数值参与加法运算,结果会转为 int 类型)

    // b = b + i;

    b += i;

    展开全文
  • 下面小编就为大家带来一篇解决java数值范围以及float与double精度丢失的问题。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • java浮点型数值在运算中会出现精度损失的情况,在业务要求比较高比如交易等场景,一般使用BigDecimal来解决精度丢失的情况。最近一个同事在使用BigDecimal时仍然出现了精度损失,简略记录一下测试用例代码如下@Test...

    java浮点型数值在运算中会出现精度损失的情况,在业务要求比较高比如交易等场景,一般使用BigDecimal来解决精度丢失的情况。最近一个同事在使用BigDecimal时仍然出现了精度损失,简略记录一下

    测试用例

    代码如下

    @Test

    public void fd() {

    double abc = 0.56D;

    System.out.println("abc: " + abc);

    System.out.println("new BigDecimal(abc): " + new BigDecimal(abc));

    System.out.println("BigDecimal.valueOf(abc): " + BigDecimal.valueOf(abc));

    }

    输出

    abc: 0.56

    new BigDecimal(abc): 0.560000000000000053290705182007513940334320068359375

    BigDecimal.valueOf(abc): 0.56

    可以看到在使用BigDecimal构造器转化浮点型仍然会有损失,而使用valueOf方法则不会出现精度损失。

    深入源码

    BigDecimal构造器,核心代码(BigDecimal(double val))如下

    public BigDecimal(double val, MathContext mc) {

    .....

    long valBits = Double.doubleToLongBits(val);

    int sign = ((valBits >> 63) == 0 ? 1 : -1);

    int exponent = (int) ((valBits >> 52) & 0x7ffL);

    long significand = (exponent == 0

    ? (valBits & ((1L << 52) - 1)) << 1

    : (valBits & ((1L << 52) - 1)) | (1L << 52));

    exponent -= 1075;

    ...

    }

    划重点, Double.doubleToLongBits返回根据IEEE754浮点“双精度格式”位布局,返回指定浮点值的表示

    BigDecimal.valueOf核心代码

    public static BigDecimal valueOf(double val) {

    return new BigDecimal(Double.toString(val));

    }

    public BigDecimal(char[] in, int offset, int len, MathContext mc) {

    ....

    }

    可以看到使用valueOf方法实际上是把double转为String,再调用string构造器的。

    那么为什么使用Double.doubleToLongBits会出现精度损失,而使用string构造器不会呢。主要原因是BigDecimal使用十进制(BigInteger)+小数点(scale)位置来表示小数,而不是直接使用二进制,如101.001 = 101001 * 0.1^3,运算时会分成两部分,BigInteger间的运算以及小数点位置的更新,这里不再展开。

    原理浅析

    Double.doubleToLongBits为什么会出现精度损失呢,主要原因是因为浮点型不能用精确的二进制来表述,就如十进制不能准确描述无穷小数一样。

    浮点型转化为二进制的算法是乘以2直到没有了小数为止,举个栗子,0.8表示成二进制

    0.8*2=1.6 取整数部分 1

    0.6*2=1.2 取整数部分 1

    0.2*2=0.4 取整数部分 0

    0.4*2=0.8 取整数部分 0

    可以看到上述的计算过程出现循环了,所以说浮点型转化为二进制有时是不可能精确的。

    结论

    如果想要把浮点型转化为BigDecimal,尽量选择使用valueOf方法,而不是使用构造器。

    本文由 歧途老农 创作,采用 CC BY 4.0 CN 协议 进行许可。 可自由转载、引用,但需署名作者且注明文章出处。

    展开全文
  • Java 浮点数精度丢失问题引入昨天帮室友写一个模拟发红包抢红包的程序时,对金额统一使用的 double 来建模,结果发现在实际运行时程序的结果在数值上总是有细微的误差,程序运行的截图:输入依次为:红包个数,抢...
  • Java对象中的主键Id是long类型,API返回的数据里面Id数值是正确的,但是序列化后返回给浏览器的json串中,发现Id的值不对了。例如:Java中Id = 4616189619433466044,浏览器上变成了 4616189619433466000。原因原来...
  • 之前需要接收数据库小数点后保留四位的数值,刚开始使用BigDecimal 来接收,接收0.3568 这种数值没有问题,当数值为0.6220时候,接收到的数为0.622,由于数据保留的位数必须一致,所以采用了下面的方法来解决。...
  • 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...
  • java类型转换精度丢失

    千次阅读 2019-06-03 20:19:05
    从图中我们可以看出存储占用字节少的类型向存储占用字节多的类型转化不会丢失精度,而存储占用字节多的类型向存储占用字节少的类型(包括占用字节一样的类型)转换都可能发生精度丢失,那么为什么会造成精度丢失呢?...
  • BigDecimal在用double做入参的时候,二进制无法精确地表示十进制小数,编译器读到字符串"0.0000002"和“1.0000002”之后,必须把它转成8个...所以,运行的时候,实际传给BigDecimal构造函数的真正的数值是1.00000019...
  • 【小家javaJava中二进制与位运算(“^,&,>>,>>>”),使用移位算法写一个流水号生成器(订单号生成器) 每篇一句 人可以被打败,但不会被击垮 啥都不说,先看一个例子 public static ...
  • java浮点型数值在运算中会出现精度损失的情况,在业务要求比较高比如交易等场景,一般使用BigDecimal来解决精度丢失的情况。最近一个同事在使用BigDecimal时仍然出现了精度损失,简略记录一下 测试用例 代码如下 @...
  • 展开全部并不是java的浮点数精度丢失,而是所有用二进制存e69da5e6ba903231313335323631343130323136353331333365643662储中的浮点数都可能会精度丢失(部分特殊的小数数值可以精确表示),所以计算机中存储的浮点数...
  • Java采用32位IEEE 754表示float型,64位IEEE 754表示doubl型。在 IEEE 标准中,浮点数是将特定长度的连续字节的所有二进制位分割为特定宽度的符号域,指数域和尾数域三个域,其中保存的值分别用于表示给定二进制...
  • 在开发过程中,我们经常会发现浮点类型的数值精度丢失,比如:System.out.println(2.0 - 1.1)我们肯定觉得应该输出0.9,但是程序输出的是0.8999999999999999在java程序中,JDK提供了BigDecimal类,以解决精度丢失...

空空如也

空空如也

1 2 3 4 5 ... 11
收藏数 209
精华内容 83
关键字:

java数值丢失精度

java 订阅