Java 浮点危险可以通过舍入来避免吗?
众所周知,当需要任意精度时,不应使用 java 浮点原始值。 Goetz 在他的优秀文章中解释了这个问题。
想象一下,我们需要在某个项目中实现任意精度,但我们没有 BigDecimal 类(因为它在 API 中不可用,例如:JavaME),也没有时间开发自定义实现。假设我们事先知道只需要相对较小的精度(2 到 4 位小数),是否有可能使用 float 和 double 类型以及舍入函数来实现 100% 可靠的紧急解决方法?如果可以,可以使用 API 中的哪个函数? 如果这个功能不可用,但您仍然认为它可以解决问题,那么实现它有多复杂?
It is known that java floating point primitive values are not to be used when arbitrary precision is required. Goetz explained the problem in his excellent article.
Imagine we need to achieve arbitrary precision in a certain project and we don't have a BigDecimal
class (because it is not available in the API, e.g.: JavaME) nor have time to develop a custom implementation. Provided we know in advance that only a relatively small precision is required (2 to 4 decimals), would it be possible to implement a 100% reliable emergency workaround using float and double types and a rounding function? And if so, which function in the API could be used?
In case this function were not available, but still you think it could address the problem, how complex would it be to implement it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
不,这是不可能的,因为某些值无法使用浮点算术表示。
0.1
是最简单的例子。No, it wouldn't be possible because some values can't be represented using floating point arithmetic.
0.1
is the simplest example.定义“100% 可靠”。 IEEE 754 浮点值(几乎在所有语言中使用;这绝不是 Java 特有的问题)实际上非常可靠地完成了它们设计的任务。它们只是并不总是像人们期望的(十进制)小数那样表现。
如果您想要解决浮点数问题,您首先必须准确指定问题是什么以及这种新格式在这些情况下应如何表现。
Define "100% reliable". IEEE 754 floating point values (which are used in nearly all languages; this is by no means a Java-specific problem) actually do the things they are designed to do very reliably. They just don't always behave the way people expect (decimal) fractional numbers to behave.
If you want something that solves a problem you have with floating-point numbers you first have to specify exactly what the problem is and how this new format should behave in those instances.
不。
四舍五入到最接近的百分位后,0.15 的一半是多少?
在精确算术中,0.15/2 = 0.075,向上舍入为 0.08(假设向上舍入或偶数舍入规则)。
在 IEEE 754 算术中,0.15/2 = 0.07499999999999999722444243843710864894092082977294921875,向下舍入为 0.07。
No.
What's half of 0.15, rounded to the nearest hundredth?
In exact arithmetic, 0.15/2 = 0.075, which rounds up to 0.08 (assuming either round-half-up or round-half-even rules).
In IEEE 754 arithmetic, 0.15/2 = 0.07499999999999999722444243843710864894092082977294921875, which rounds down to 0.07.
在这种情况下,为什么还要费心浮点运算呢?只需使用
Integer
乘以您的精度系数即可。小精度值(例如
467.8142
)将由4,678,142
表示,并使用标准Integer
运算进行计算。没有精度损失。但是,话又说回来,就像 @TomaszNurkiewicz 提到的那样,这正是 BigDecimal 所做的。所以你的问题其实没有任何意义。浮点运算非常好,甚至可以处理您提到的情况,只要程序员知道她在做什么。
In this case, why bother with floating-point arithmetic at all? Just use an
Integer
multiplied by your precision factor.A small precision value, such as
467.8142
will be represented by4,678,142
and calculated using standardInteger
operations. No loss of precision.But, then again, like @TomaszNurkiewicz mentioned, this is exactly what
BigDecimal
does. So your question doesn't really make any sense. Floating point arithmetic is perfectly fine, and can handle even the cases you mentioned, granted the programmer knows what she's doing.我认为不会,除非您可以完全定义和控制所有数学,达到排除所有舍入的程度。
也许,另一种选择是使用有理数。这是我作为实验而敲出的一个。我怀疑它是否是最佳的,甚至是有效的,但它肯定是有可能的。
我在 java2 找到了 lcm 和 gcd 代码。它们可能还可以改进。
I think no, unless you can fully define and control all the maths to such an extent that you exclude all rounding.
An alternative could be, perhaps, using Rationals. Here's one I knocked up just as an experiment. I doubt if it is optimal, or even efficient, but it is certainly a possibility.
I found the lcm and gcd code at java2. They can probably be improved.