在 Java 中使用 double 保持精度
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/322749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Retain precision with double in Java
提问by Deinumite
public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
The above code prints:
上面的代码打印:
11.399999999999
How would I get this to just print (or be able to use it as) 11.4?
我如何才能让它打印(或能够用作)11.4?
采纳答案by coobird
As others have mentioned, you'll probably want to use the BigDecimal
class, if you want to have an exact representation of 11.4.
正如其他人所提到的BigDecimal
,如果您想准确表示 11.4 ,您可能想要使用该类。
Now, a little explanation into why this is happening:
现在,稍微解释一下为什么会发生这种情况:
The float
and double
primitive types in Java are floating pointnumbers, where the number is stored as a binary representation of a fraction and a exponent.
Java 中的float
和double
基本类型是浮点数,其中数字存储为分数和指数的二进制表示。
More specifically, a double-precision floating point value such as the double
type is a 64-bit value, where:
更具体地说,双精度浮点值(例如double
type)是 64 位值,其中:
- 1 bit denotes the sign (positive or negative).
- 11 bits for the exponent.
- 52 bits for the significant digits (the fractional part as a binary).
- 1 位表示符号(正或负)。
- 11 位用于指数。
- 有效数字为 52 位(小数部分为二进制)。
These parts are combined to produce a double
representation of a value.
这些部分组合在一起以产生double
一个值的表示。
(Source: Wikipedia: Double precision)
(来源:维基百科:双精度)
For a detailed description of how floating point values are handled in Java, see the Section 4.2.3: Floating-Point Types, Formats, and Valuesof the Java Language Specification.
有关 Java 中如何处理浮点值的详细说明,请参阅Java 语言规范的第 4.2.3 节:浮点类型、格式和值。
The byte
, char
, int
, long
types are fixed-pointnumbers, which are exact representions of numbers. Unlike fixed point numbers, floating point numbers will some times (safe to assume "most of the time") not be able to return an exact representation of a number. This is the reason why you end up with 11.399999999999
as the result of 5.6 + 5.8
.
的byte
,char
,int
,long
类型的定点数字,这是数字的精确representions。与定点数不同,浮点数有时(假设“大部分时间”是安全的)无法返回数字的精确表示。这就是你最终11.399999999999
得到5.6 + 5.8
.
When requiring a value that is exact, such as 1.5 or 150.1005, you'll want to use one of the fixed-point types, which will be able to represent the number exactly.
当需要一个精确的值时,例如 1.5 或 150.1005,您需要使用定点类型之一,它能够精确地表示数字。
As has been mentioned several times already, Java has a BigDecimal
class which will handle very large numbers and very small numbers.
正如已经多次提到的,Java 有一个BigDecimal
类可以处理非常大的数字和非常小的数字。
From the Java API Reference for the BigDecimal
class:
来自BigDecimal
该类的 Java API 参考:
Immutable, arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10^-scale).
不可变的、任意精度的有符号十进制数。BigDecimal 由一个任意精度的整数非标度值和一个 32 位整数标度组成。如果为零或正数,则刻度是小数点右侧的位数。如果为负数,则数字的未换算值乘以 10 的取反乘幂。因此,由 BigDecimal 表示的数字的值是 (unscaledValue × 10^-scale)。
There has been many questions on Stack Overflow relating to the matter of floating point numbers and its precision. Here is a list of related questions that may be of interest:
Stack Overflow 上有很多关于浮点数及其精度的问题。以下是您可能感兴趣的相关问题列表:
- Why do I see a double variable initialized to some value like 21.4 as 21.399999618530273?
- How to print really big numbers in C++
- How is floating point stored? When does it matter?
- Use Float or Decimal for Accounting Application Dollar Amount?
- 为什么我看到一个双变量初始化为某个值,如 21.4 为 21.399999618530273?
- 如何在 C++ 中打印非常大的数字
- 浮点数是如何存储的?什么时候重要?
- 会计应用程序美元金额使用浮点数还是小数?
If you really want to get down to the nitty gritty details of floating point numbers, take a look at What Every Computer Scientist Should Know About Floating-Point Arithmetic.
如果您真的想深入了解浮点数的具体细节,请查看每个计算机科学家应该了解的关于浮点运算的知识。
回答by Paul Tomblin
Multiply everything by 100 and store it in a long as cents.
将所有内容乘以 100 并将其存储为美分。
回答by Dustin
Pretty sure you could've made that into a three line example. :)
很确定你可以把它变成一个三行的例子。:)
If you want exact precision, use BigDecimal. Otherwise, you can use ints multiplied by 10 ^ whatever precision you want.
如果您想要精确的精度,请使用 BigDecimal。否则,您可以使用整数乘以 10 ^ 任何您想要的精度。
回答by S.Lott
Doubles are approximationsof the decimal numbers in your Java source. You're seeing the consequence of the mismatch between the double (which is a binary-coded value) and your source (which is decimal-coded).
双精度数是 Java 源代码中十进制数的近似值。您会看到 double(二进制编码值)和源(十进制编码)之间不匹配的后果。
Java's producing the closest binary approximation. You can use the java.text.DecimalFormat to display a better-looking decimal value.
Java 产生最接近的二进制近似值。您可以使用 java.text.DecimalFormat 来显示更好看的十进制值。
回答by Adam Jaskiewicz
Use a BigDecimal. It even lets you specify rounding rules (like ROUND_HALF_EVEN, which will minimize statistical error by rounding to the even neighbor if both are the same distance; i.e. both 1.5 and 2.5 round to 2).
使用 BigDecimal。它甚至可以让您指定舍入规则(如 ROUND_HALF_EVEN,如果两者的距离相同,它将通过舍入到偶数邻居来最小化统计误差;即 1.5 和 2.5 都舍入为 2)。
回答by Steve Jessop
Observe that you'd have the same problem if you used limited-precision decimal arithmetic, and wanted to deal with 1/3: 0.333333333 * 3 is 0.999999999, not 1.00000000.
请注意,如果您使用有限精度的十进制算术,并且想要处理 1/3,您会遇到同样的问题:0.333333333 * 3 是 0.999999999,而不是 1.00000000。
Unfortunately, 5.6, 5.8 and 11.4 just aren't round numbers in binary, because they involve fifths. So the float representation of them isn't exact, just as 0.3333 isn't exactly 1/3.
不幸的是,5.6、5.8 和 11.4 不是二进制的整数,因为它们涉及五分之一。所以它们的浮点表示并不准确,就像 0.3333 不完全是 1/3 一样。
If all the numbers you use are non-recurring decimals, and you want exact results, use BigDecimal. Or as others have said, if your values are like money in the sense that they're all a multiple of 0.01, or 0.001, or something, then multiply everything by a fixed power of 10 and use int or long (addition and subtraction are trivial: watch out for multiplication).
如果您使用的所有数字都是非循环小数,并且您想要精确的结果,请使用 BigDecimal。或者正如其他人所说,如果你的价值就像钱一样,它们都是 0.01、0.001 或其他东西的倍数,那么将所有东西乘以 10 的固定幂并使用 int 或 long(加法和减法是微不足道:注意乘法)。
However, if you are happy with binary for the calculation, but you just want to print things out in a slightly friendlier format, try java.util.Formatter
or String.format
. In the format string specify a precision less than the full precision of a double. To 10 significant figures, say, 11.399999999999 is 11.4, so the result will be almost as accurate and more human-readable in cases where the binary result is very close to a value requiring only a few decimal places.
但是,如果您对二进制计算感到满意,但您只想以稍微友好的格式打印出来,请尝试java.util.Formatter
或String.format
。在格式字符串中指定一个小于双精度全精度的精度。对于 10 个有效数字,例如 11.399999999999 是 11.4,因此在二进制结果非常接近只需要几个小数位的值的情况下,结果几乎同样准确且更易读。
The precision to specify depends a bit on how much maths you've done with your numbers - in general the more you do, the more error will accumulate, but some algorithms accumulate it much faster than others (they're called "unstable" as opposed to "stable" with respect to rounding errors). If all you're doing is adding a few values, then I'd guess that dropping just one decimal place of precision will sort things out. Experiment.
指定的精度在一定程度上取决于你对数字做了多少数学运算——通常你做的越多,累积的错误就越多,但有些算法的累积速度比其他算法快得多(它们被称为“不稳定”)与舍入误差方面的“稳定”相反)。如果您所做的只是添加一些值,那么我猜想只删除一位小数点的精度就可以解决问题。实验。
回答by Draemon
As others have noted, not all decimal values can be represented as binary since decimal is based on powers of 10 and binary is based on powers of two.
正如其他人所指出的,并非所有十进制值都可以表示为二进制,因为十进制基于 10 的幂,而二进制基于 2 的幂。
If precision matters, use BigDecimal, but if you just want friendly output:
如果精度很重要,请使用 BigDecimal,但如果您只想要友好的输出:
System.out.printf("%.2f\n", total);
Will give you:
会给你:
11.40
回答by Dark Castle
Check out BigDecimal, it handles problems dealing with floating point arithmetic like that.
查看 BigDecimal,它处理处理浮点运算的问题。
The new call would look like this:
新调用将如下所示:
term[number].coefficient.add(co);
Use setScale() to set the number of decimal place precision to be used.
使用 setScale() 设置要使用的小数位数精度。
回答by Kevin Crowell
Use java.math.BigDecimal
使用 java.math.BigDecimal
Doubles are binary fractions internally, so they sometimes cannot represent decimal fractions to the exact decimal.
双精度数在内部是二进制小数,因此它们有时无法将十进制小数表示为精确的十进制数。
回答by dan04
You can't, because 7.3 doesn't have a finite representation in binary. The closest you can get is 2054767329987789/2**48 = 7.3+1/1407374883553280.
你不能,因为 7.3 没有二进制的有限表示。你能得到的最接近的是 2054767329987789/2**48 = 7.3+1/1407374883553280。
Take a look at http://docs.python.org/tutorial/floatingpoint.htmlfor a further explanation. (It's on the Python website, but Java and C++ have the same "problem".)
查看http://docs.python.org/tutorial/floatingpoint.html以获得进一步的解释。(它在 Python 网站上,但 Java 和 C++ 有相同的“问题”。)
The solution depends on what exactly your problem is:
解决方案取决于您的问题究竟是什么:
- If it's that you just don't like seeing all those noise digits, then fix your string formatting. Don't display more than 15 significant digits (or 7 for float).
- If it's that the inexactness of your numbers is breaking things like "if" statements, then you should write if (abs(x - 7.3) < TOLERANCE) instead of if (x == 7.3).
- If you're working with money, then what you probably really want is decimal fixed point. Store an integer number of cents or whatever the smallest unit of your currency is.
- (VERY UNLIKELY) If you need more than 53 significant bits (15-16 significant digits) of precision, then use a high-precision floating-point type, like BigDecimal.
- 如果您只是不喜欢看到所有这些噪音数字,那么请修复您的字符串格式。不要显示超过 15 个有效数字(或 7 个浮点数)。
- 如果您的数字不准确会破坏诸如“if”语句之类的东西,那么您应该编写 if (abs(x - 7.3) < TOLERANCE) 而不是 if (x == 7.3)。
- 如果您正在处理金钱,那么您可能真正想要的是十进制定点。存储整数美分或任何货币的最小单位。
- (非常不可能)如果您需要超过 53 个有效位(15-16 个有效数字)的精度,则使用高精度浮点类型,如 BigDecimal。