1. 踩坑经历

上周,一个用户反馈他创建的某个销售单无法打开,但其余销售单都可以正常打开,当时查看了生产环境的ERROR日志,发现抛了这样的异常:java.lang.NumberFormatException: For input string: "E"

相信大家对这个异常都不陌生,很显然,是因为将字符串转换为数字时抛出的,比如下面这样:

但仔细查看了用户报错的单据,也没有发现哪里有输入“E”这样的字符串(请原谅我第一时间没有想到是科学计数法造成的,哈哈),最后把生产环境的这条数据插入到了开发环境中,定位到原来是因为将金额转换为大写时导致的,报错的关键代码如下所示:

String totalAmountStr = String.valueOf(totalAmount / 100.0);

String amountCN = MoneyUtils.toChinese(totalAmountStr);

其中totalAmount是一个long类型的变量,之所以除以100.0,是因为我们数据库中存储金额都是按为单位存储的(相信很多小伙伴也是这么存储的),第2行代码主要是为了将金额转换为大写,比如将105000.50转换为壹拾万零伍仟元伍角。

用户报错的那个单据,totalAmount为2700万,转换为分就是:2700000000,执行完totalAmount / 100.0,输出结果竟然是2.7E7,而不是预期的27000000,因此导致了异常:java.lang.NumberFormatException: For input string: "E"

最后的解决方案是将金额转换为BigDecimal来处理,代码修改为如下所示:

String totalAmountStr = new BigDecimal(String.valueOf(totalAmount)).divide(new BigDecimal("100"),2, RoundingMode.HALF_UP).toString();
String amountCN = MoneyUtils.toChinese(totalAmountStr);

2. 原因分析

在Java中,当浮点数(float、double)的整数部分达到8位及以上,会以科学计数法表示,如下所示:

double firstAmount = 2700000D;
double secondAmount = 27000000D;
double thirdAmount = 2700000.25D;
double fourthAmount = 27000000.25D; System.out.println(firstAmount);
System.out.println(secondAmount);
System.out.println(thirdAmount);
System.out.println(fourthAmount);

默默数了下,整数部分8位的话,都是千万级别了,估计遇到这个问题的用户很豪,哈哈。

所以使用double来表示金额,当金额遇到科学计数法时,就会显示不正常、甚至造成一些意想不到的异常。

3. 解决方案

如果不想用科学计数法显示,而是显示金额本身,有以下2种解决方案:

  1. 使用NumberFormat
  2. 使用BigDecimal

3.1 方案一:使用NumberFormat

使用NumberFormat的方法如下所示:

NumberFormat numberFormat = NumberFormat.getInstance();
numberFormat.setGroupingUsed(false); double secondAmount = 27000000D;
double fourthAmount = 27000000.25D; System.out.println(numberFormat.format(secondAmount));
System.out.println(numberFormat.format(fourthAmount));

当将numberFormat.setGroupingUsed(false);注释掉或者修改为numberFormat.setGroupingUsed(true);时,输出结果就变为了:

3.2 方案二:使用BigDecimal(推荐)

使用BigDecimal的方法如下所示:

double secondAmount = 27000000D;
double fourthAmount = 27000000.25D; System.out.println(new BigDecimal(String.valueOf(secondAmount)).setScale(2,RoundingMode.HALF_UP).toString());
System.out.println(new BigDecimal(String.valueOf(fourthAmount)).setScale(2,RoundingMode.HALF_UP).toString());

相比而言,我更推荐使用BigDecimal的这种方案。

关于BigDecimal的更多用法,可以查看我写的另一篇博客:Java BigDecimal使用指南


最新文章

  1. nyoj133_子序列_离散化_尺取法
  2. Html.DropDownList 选中 mvc view 弱类型
  3. 如何让chrome始终运行插件
  4. 我常用的Mac快捷键
  5. 转数据库分库分表(sharding)系列(二) 全局主键生成策略
  6. POJ 3281
  7. HDU 1677
  8. 华为的JAVA面试题及答案(部分)
  9. 如何让tableView展示数据
  10. 移动端 前端框架 amaze ui
  11. MVC中的Views下面的视图放到Views文件夹外
  12. js 判断是不是手机访问
  13. js数组遍历some,foreach,map,filter,every对比
  14. spring mvc 之@requestmapping
  15. ansible的Filter
  16. TCP和UDP的对比
  17. 在windows上安装redis并设置密码
  18. css td hover 选择器无效
  19. Bamboo基础概念
  20. JavaScript中JSON对象和JSON字符串的相互转化

热门文章

  1. Vue 3.0 来了,我们该做些什么?
  2. 10.扩展:Zero Copy
  3. Spring学习(九)Spring 和数据库编程【了解】
  4. Java Web学习(二)数据加密
  5. 针对python字符串有哪些操作呢?本文详解!
  6. Python-开发规范-遵循PEP8规范
  7. Matlab中界面和注释---中英文切换问题
  8. [源码阅读] 阿里SOFA服务注册中心MetaServer(1)
  9. 手把手教你AspNetCore WebApi:数据验证
  10. git仓库之gitlab搭建使用