09 | 数值计算:注意精度、舍入和溢出问题
该思维导图由 AI 生成,仅供参考
“危险”的 Double
- 深入了解
- 翻译
- 解释
- 总结
数值计算中存在精度、舍入和溢出问题,可能导致计算结果与预期不符。浮点数计算的精度损失根源在于计算机以二进制存储数值,无法精确表示浮点数。使用Double进行浮点数运算可能导致精度问题,而BigDecimal类型可以解决这一问题,但在使用时需注意scale和precision的影响。此外,浮点数的字符串格式化也可能产生意想不到的结果,因此建议通过BigDecimal进行格式化。对于数值运算,还需小心处理溢出问题。文章提供了解决这些问题的方法,包括使用Math类的xxExact方法进行数值运算,以及使用BigInteger类处理大数运算。总之,了解数值计算中的精度、舍入和溢出问题,并采用合适的数据类型和格式化方式,可以避免出现不符合预期的计算结果。
《Java 业务开发常见错误 100 例》,新⼈⾸单¥59
全部留言(23)
- 最新
- 精选
- Darren精度问题遇到的比较少,可能与从事非金融行业有关系,试着回答下问题 第一种问题 1、 ROUND_UP 舍入远离零的舍入模式。 在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。 注意,此舍入模式始终不会减少计算值的大小。 2、ROUND_DOWN 接近零的舍入模式。 在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。 注意,此舍入模式始终不会增加计算值的大小。 3、ROUND_CEILING 接近正无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同; 如果为负,则舍入行为与 ROUND_DOWN 相同。 注意,此舍入模式始终不会减少计算值。 4、ROUND_FLOOR 接近负无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同; 如果为负,则舍入行为与 ROUND_UP 相同。 注意,此舍入模式始终不会增加计算值。 5、ROUND_HALF_UP 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。 如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。 注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。 6、ROUND_HALF_DOWN 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。 如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。 7、ROUND_HALF_EVEN 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。 如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同; 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。 注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。 此舍入模式也称为“银行家舍入法”,主要在美国使用。四舍六入,五分两种情况。 如果前一位为奇数,则入位,否则舍去。 以下例子为保留小数点1位,那么这种舍入方式下的结果。 1.15>1.2 1.25>1.2 8、ROUND_UNNECESSARY 断言请求的操作具有精确的结果,因此不需要舍入。 如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。 第二个问题 在MySQL中,整数和浮点数的定义都是有多种类型,整数根据实际范围定义,浮点数语言指定整体长度和小数长度。浮点数类型包括单精度浮点数(float型)和双精度浮点数(double型)。定点数类型就是decimal型。定点数以字符串形式存储,因此,其精度比浮点数要高,而且浮点数会出现误差,这是浮点数一直存在的缺陷。如果要对数据的精度要求比较高,还是选择定点数decimal比较安全。
作者回复: 👍🏻
2020-03-28965 - 👽想请教一下。关于金额。 还存在 使用Long类型的分存储,以及封装的money对象存储的方式。这两种方式适合解决金额类的精度丢失问题嘛?
作者回复: 用分存储是可以(解决精度问题),但是容易出错,万一读的时候忘记/100或者是存的时候忘记*100,可能会引起重大问题,还是使用DECIMAL(13, 2) /DECIMAL(13, 4) 存比较好。
2020-03-28316 - Jerry Wu感谢老师,看完这篇文章,改了BigDecimal工具类,避免了一个事故。
作者回复: 赞
2020-04-01310 - pedro第一个问题,BigDecimal 的 8 中 Round模式,分别是 1.ROUND_UP:向上取整,如 5.1 被格式化后为 6,如果是负数则与直观上不一致,如 -1.1 会变成 -2。2.ROUND_DOWN:向下取整,与 ROUND_UP 相反。 3.ROUND_CEILING:正负数分开版的取整,如果是正数,则与 ROUND_UP 一样,如果是负数则与 ROUND_DOWN 一样。 4.ROUND_FLOOR:正负数分开版的取整,与 ROUND_CEILING 相反。 5.ROUND_HALF_UP:四舍五入版取整,我们直观上最为理解的一种模式,如 5.4 小数部分小于 0.5,则舍位为 5,如果是 5.6 则进位变成 6,如果是负数,如 -5.4 => -5,-5.6 => -6。 6.ROUND_HALF_DOWN:五舍六入版取整,必须大于 0.5 才可进位,其它与 ROUND_HALF_UP 一致。 7.ROUND_HALF_EVEN:奇偶版四舍五入取整,如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP;如果它为偶数,则作ROUND_HALF_DOWN,会根据舍弃部分的奇偶性来选择进位的是四舍五入还是五舍六入。 8. ROUND_UNNECESSARY:要求传入的数必须是精确的,如 1 和 1.0 都是精确的,如果为 1.2 或者 1.6 之类的均会报 ArithmeticException 异常。 第二个问题,MySQL 是支持 bigint 和 bigdecimal 数据类型存储的,当然还有 numberic,numberic 的作用与 bigdecimal 一致,当然如果这些数据类型在数据库中计算我觉得是不妥的,应该查询后在代码层面中计算,当然如果有人补充一下如何在数据库中科学计算,也可让大家涨涨见识😄。
作者回复: 👍🏻
2020-03-283 - 吴国帅真棒 get到知识了!
作者回复: 觉得好可以多转发分享
2020-03-292 - 岳宜波一般用的比较多的就是,向上取整,向下取整,四舍五入和舍位四种,在我们项目里因为有国际化,会有币种档案,在币种中定义金额精度和价格精度以及舍入方式,在商品的计量单位上定义数量精度以及舍入方式。
作者回复: 👍🏻
2020-05-251 - 美美请教老师string.valueof替代bigdecimal.valueof可否呢
作者回复: 可以比较一下,主要是要小心scale: BigDecimal bigDecimal1 = new BigDecimal("100"); BigDecimal bigDecimal2 = new BigDecimal(String.valueOf(100d)); BigDecimal bigDecimal3 = BigDecimal.valueOf(100d); BigDecimal bigDecimal4 = new BigDecimal(Double.toString(100d)); System.out.println(bigDecimal1.multiply(new BigDecimal("4.015"))); System.out.println(bigDecimal2.multiply(new BigDecimal("4.015"))); System.out.println(bigDecimal3.multiply(new BigDecimal("4.015"))); System.out.println(bigDecimal4.multiply(new BigDecimal("4.015")));
2020-04-081 - 珅珅君我想补充一点,之所以DecimalFormat也会导致精度的问题,是因为 format.format(num) 这个方法参数是double类型,传float会导致强转丢失精度。所以无论怎么样,浮点数的字符串格式化通过 BigDecimal 进行就行
作者回复: 不错
2020-07-10 - Monday手机计算器把 10%+10% 算成了 0.11 而不是 0.2。 读到这里,吓得我赶快掏出安卓机算了下2020-03-2911
- Geek_3b1096用equals对两BigDecimal判等...之前就被坑了2020-03-2929