解析货币串安全的最佳方法?

时间:2015-04-23 12:21:24

标签: java string currency

在服务器上我接受String作为参数。这个字符串代表钱。

可能是(例如)

0  
12  
12,  
12,34  
,12

现在我这样削减它:

long centAmount = (long) (NumberFormat.getInstance(Locale.FRANCE).parse(outSum).doubleValue() * 100)
我担心我会在某处丢失分钱。

请告知如何重写我的代码。

1 个答案:

答案 0 :(得分:5)

使用(long)时,会截断数字。这意味着0.999999999999999将被截断为0,这将在这里和那里失去你一分钱。更好的方法是使用舍入。

double input = NumberFormat.getInstance(Locale.FRANCE).parse(outSum).doubleValue();
long centAmount = Math.round(input * 100);

这将适用于价值高达70万亿美元的价值。如果您需要准确存储更大的数量,则需要使用BigDecimal&的BigInteger。

使用BigDecimal的一个优点是它会提醒你什么时候应该使用舍入(并不总是如果你不确定它会更容易出错)缺点是代码更加麻烦。

如果您使用BigDecimal,则可以执行此操作

DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(Locale.FRANCE);
df.setParseBigDecimal(true);
BigDecimal bd = (BigDecimal) df.parseObject(outSum);
BigDecimal bd100 = bd.multiply(BigDecimal.valueOf(100))
                     .setScale(0, RoundingMode.HALF_UP);
long centAmount = bd100.longValue();

添加一个简单示例,说明为什么BigDecimal不能解决所有问题,但它确实隐藏了它们。

    BigDecimal add = new BigDecimal("1.00")
              .divide(BigDecimal.valueOf(3), 2, BigDecimal.ROUND_HALF_UP)
              .multiply(BigDecimal.valueOf(3)).add(new BigDecimal("0.01"));
    System.out.println(add);
    double add2 = 1.0 / 3 * 3 +  0.01;
    System.out.println(add2);

打印

1.00
1.01

哪一个更容易出错,你可以争论任何一种方式(这可能是基于你的团队,他们发现哪些不容易出错),但哪一个更难理解它想要做什么?

我对BigDecimal的问题是黑色和白色评估,double有缺陷,BigDecimal会解决你所有的问题,实际上,你得到了所有相同的问题但是使用BigDecimal它们可能更难发现,所以你可能永远不会知道你有问题。

使用double,如果你认为1.0999999999999998看起来不对,但是使用BigDecimal,你可能会看到1.00并认为看起来不错。