简体   繁体   English

输入字符串的 NumberFormatException - Java

[英]NumberFormatException for input String - Java

I'm trying to round the cents of a value.我正在尝试四舍五入一个值的美分。 The rounding seems to work, but there's an exception:四舍五入似乎有效,但有一个例外:

double amount = 289.42;
String f= String.format("%.1f", amount);
  
System.out.println(new DecimalFormat("##0.00").format(Double.valueOf(f)));

This is the error: java.lang.NumberFormatException: For input string: "289,4"这是错误: java.lang.NumberFormatException: For input string: "289,4"

Your question posits an impossibility.你的问题是不可能的。

Here's the thing: You cannot represent currency amounts with double .事情是这样的:你不能double表示货币金额。 At all.完全没有。 Thus, 'how do I render these cents-in-a-double appropriately' isn't a sensible concept in the first place.因此,“我如何适当地渲染这些美分”首先不是一个明智的概念。 Because cents cannot be stored in a double.因为美分不能存储在 double 中。

The problem lies in what double are , fundamentally.从根本上说,问题在于double什么。 double is a numeric storage system that is defined to consist of exactly 64 bits. double是一个数字存储系统,被定义为正好由 64 位组成。 That's a problem right there: It's a mathematical fact that 64 bits can store at most 2^64 unique things, because, well, math.这就是一个问题:这是一个数学事实,64 位最多可以存储 2^64 个独特的东西,因为,嗯,数学。 Think about it.想想看。

The problem is, There are in fact an infinite amount of numbers between 0 and 1, let alone between -infinity and +infinity which double would appear to represent.问题是,实际上在 0 和 1 之间存在无限数量的数字,更不用说在double似乎代表的 -infinity 和 +infinity 之间了。 So, how do you square that circle?那么,你如何把那个圆平方? How does one represent one specific value chosen from an infinite amount of infinities, with only 64 bits?一个只有 64 位的无穷大数如何代表一个特定值?

The answer is simple.答案很简单。 You don't .你没有

doubles do not in fact store arbitrary values at all.双打实际上根本存储任意值。 And that is why you cannot use them to store currencies .这就是为什么你不能用它们来存储货币 Instead, take the number line and mark off slightly less than 2^64 specific values on it.取而代之的是,取数轴并在其上标出略小于 2^64 的特定值。 We'll call these 'the blessed numbers'.我们称这些为“有福的数字”。 A double can only store blessed numbers. double只能存储祝福的数字。 They can't store anything else.他们不能存储其他任何东西。 In addition, any math you do to doubles is silently rounded to the nearest blessed value as doubles can't store anything else.此外,您对双精度数所做的任何数学运算都会默默地四舍五入到最接近的祝福值,因为双精度数无法存储任何其他内容。 So, 0.1 + 0.1?那么,0.1 + 0.1? Not actually 0.2.实际上不是0.2。 Instead, 0.1 isn't even blessed, so that's really round-to-blessed(0.1) + round-to-blessed(0.1), so actually that's 0.0999999999975 + 0.0999999999975 = 0.2000000000018 or whatever.相反,0.1 甚至没有被祝福,所以这实际上是圆形到祝福(0.1)+ 圆形到祝福(0.1),所以实际上是 0.0999999999975 + 0.0999999999975 = 0.2000000000018 或其他。 The blessed numbers are distributed unequally - there are a ton of blessed numbers in the 0-1 range, and as you move away from the 0, the distance between 2 blessed numbers grows larger and larger.受祝福的数字分布不均——0-1范围内有很多受祝福的数字,随着你远离0,2个受祝福的数字之间的距离越来越大。 Their distribution makes sense, but, computers count in binary, so they fall on neat boundaries in binary , not in decimal (0.1 looks neat in decimal. It's similar to 1 divided by 3, ie endlessly repeating, and therefore not precisely representable no matter how many bits you care to involve, in binary).它们的分布是有道理的,但是,计算机以二进制计数,因此它们落在二进制的整齐边界上,而不是十进制(0.1 在十进制中看起来很整齐。它类似于 1 除以 3,即无休止地重复,因此无论如何都不能精确表示您希望涉及多少位,以二进制形式)。

That rounding is precisely what you absolutely don't want to happen when representing currency.这种四舍五入正是您在表示货币时绝对不希望发生的事情。 You don't want a cent to randomly appear or disappear and yet that is exactly what will happen if you use double to store finance info.您不希望一分钱随机出现或消失,但这正是您使用double存储财务信息时会发生的情况。

Hence, you're asking about how to render 'cents in a double' appropriately but in fact that question cannot possibly be answered - you can't store cents in a double, hence, it is not possible to render it properly.因此,您在询问如何适当地呈现“双倍中的美分”,但实际上这个问题可能无法回答 - 您不能将美分存储在双倍中,因此无法正确呈现它。

Instead..反而..

Use cents-in-int使用整数整数

The easiest way to do currency correctly is to first determine the accepted atomary unit for your currency, and then store those , in long or int as seems appropriate.正确处理货币的最简单方法是首先确定您的货币可接受的原子单位,然后将它们存储在longint中。 For euros, that's eurocents.对于欧元,那是欧分。 For bitcoin, that's satoshis.对于比特币,这就是聪。 For yen, it's just yen.对于日元,它只是日元。 For dollars, its dollarcents.对于美元,它的美分。 And so on.等等。

$5.45 is best represented as the int value 545. Not as the double value 5.45, because that's not actually a value a double can represent. $5.45 最好表示为 int 值 545。而不是 double 值 5.45,因为这实际上不是 double 可以表示的值。

Why do doubles show up as 0.1?为什么双打显示为 0.1?

Because System.out.println knows that doubles are wonky and that you're highly likely to want to see 0.1 and not 0.09999999999991238 and thus it rounds inherently.因为System.out.println知道双精度数是不稳定的,并且您很可能希望看到 0.1 而不是 0.09999999999991238,因此它本质上是四舍五入的。 That doesn't magically make it possible to use double to represent finance amounts.这并不能神奇地使使用double来表示财务金额成为可能。

I need to divide, or multiply by complex factors我需要除以或乘以复杂的因素

Division for currency is always nasty.货币划分总是令人讨厌的。 Imagine a cost of 100 dollars that needs to be paid by each 'partner' in a coop.想象一个鸡舍中的每个“合作伙伴”需要支付 100 美元的成本。 The coop has 120 shares, and each partner has 40 shares, so each partner must pay precisely 1/3 of the cost.合作社有 120 股,每个合伙人有 40 股,所以每个合伙人必须支付恰好 1/3 的成本。

Now what?怎么办? 100 dollars does not neatly divide into threes. 100 美元不整齐地分成三份。 You can't very well charge everybody 33 dollars, 33 cents, and a third of a cent.你不能很好地向每个人收取 33 美元、33 美分和 1/3 美分。 You could charge everybody 33.33, but now the bank needs to eat 1 cent.你可以向每个人收取 33.33,但现在银行需要吃掉 1 美分。 You could also charge everybody 33.34, and the bank gets to keep the 2 cents.你也可以向每个人收取 33.34 美元,银行可以保留 2 美分。 Or, you could get a little creative, and roll some dice to determine 'the loser'.或者,您可以发挥一点创意,掷一些骰子来确定“输家”。 The loser pays 33.34, the other 2 pay 33.33.失败者支付 33.34,其他 2 支付 33.33。

The point is: There is no inherently correct answer.关键是:没有本质上正确的答案。 Each situation has its own answer.每种情况都有自己的答案。 Hence, division in general is impossible without first answering that question.因此,如果不首先回答这个问题,一般来说划分是不可能的。 There is no solving this problem unless you have code that knows how to apply the chosen 'division' algorithm.除非您有知道如何应用所选“除法”算法的代码,否则无法解决此问题。 a / b cannot be used in any case (as the operation has at least 3 params: The dividend, the divisor, and the algorithm to apply to it). a / b在任何情况下都不能使用(因为该操作至少有 3 个参数:被除数、除数和应用于它的算法)。

For foreign exchange, 'multiply by this large decimal value' comes up a lot.对于外汇,“乘以这个大的十进制值”出现了很多。 You can store arbitrary precision values exactly using the java.math.BigDecimal class.您可以使用java.math.BigDecimal class 精确存储任意精度值。 However, this is not particularly suitable for storing currencies (all multiplication-by-a-factor will mean the BDs grow ever larger, they still can't divide eg 1 by 3 (anything that has repeating digits), and they don't solve the more fundamental issue: Any talk with other systems, such as a bank, can't deal with fractions of atomary units).但是,这并不特别适合存储货币(所有乘以因子将意味着 BD 变得越来越大,它们仍然不能除以 1 除以 3(任何具有重复数字的东西),而且它们不会解决更根本的问题:与其他系统(例如银行)的任何对话都无法处理原子单位的分数)。 Stick with BD-space math (as that is perfect, though, can throw exceptions if you divide, and grows ever slower and more complicated over time), until the system you are programming for enforces a rounding to atomary units, at which point, you round, resetting the growth.坚持使用 BD 空间数学(因为这是完美的,但是,如果你除法会抛出异常,并且随着时间的推移变得越来越慢和越来越复杂),直到你正在编程的系统强制四舍五入到原子单位,此时,你轮回,重置成长。 If you never need to multiply by fractions this doesn't come up, and there's no need to use BigDecimal for anything currency related.如果您永远不需要乘以分数,则不会出现这种情况,并且无需将 BigDecimal 用于与货币相关的任何内容。

How do I format cents-in-a-long?如何格式化 cents-in-a-long?

String.format("€%d.%02d", cents / 100, cents % 100);

It gets very slightly more complicated for negative numbers (% returns negative values, so you need to do something about this. Math.abs can help), but not very.对于负数,它变得稍微复杂一些(% 返回负值,所以你需要对此做一些事情Math.abs可以提供帮助),但不是很复杂。 cents / 100 gives you the "whole" part when you integer-divide by 100, and % 100 gives you the remainder, which precisely boils down to 'euros' and 'eurocents'. cents / 100当您整数除以 100 时为您提供“整体”部分,而% 100为您提供余数,这恰好归结为“欧元”和“欧元分”。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM