A fact that violates intuition
The computer is called "Computing" machine is because it is mainly used to calculate the invention, "calculation" of course is its expertise, in everyone's impression, the calculation must be very accurate. But in fact, even in some very basic decimal operations, the results of the calculations are imprecise.
Like what:
float F = 0.1f*0.1f;
System.out.println (f);
The result seems to be self-evident, it should be 0.01, but in fact, the screen output is 0.010000001, followed by a 1.
How can a computer make a mistake when it looks like this simple operation?
Brief answers
In fact, not the operation itself will be wrong, but the computer can not accurately represent a very large number, such as 0.1.
The computer stores decimals in a binary format that does not accurately represent 0.1, and it can only represent a number that is very close to 0.1 but not equal to 0.1.
Numbers are not accurate, and it is not surprising that the results are inaccurate in imprecise numbers.
0.1 How can it not be accurately expressed? It's OK in the decimal world, but not in the binary world. Before we say binary, let's look at the familiar decimal.
In fact, decimal can only represent the number of times that can be expressed as 10, such as 12.345, which is actually expressed as: 1*10+2*1+3*0.1+4*0.01+5*0.001, similar to the representation of integers, each position after the decimal point has a bitwise right, from left to right, 0.1,0.01,0.001,... namely 10^ (-1), 10^ (-2), 10^ (-3).
Many numbers, the decimal is also can not be accurately expressed, such as 1/3, retain three decimal places, the decimal means 0.333, but no matter how many decimal places reserved, is not accurate, with 0.333 for the operation, such as multiplied by 3, the expected result is 1, but actually 0.999.
Binary is similar, but binary can only represent the number of squares and numbers that can be expressed as 2, and look at some examples of the 2 of the sub-side:
2 of the second party |
Decimal |
2^ (-1) |
0.5 |
2^ (-2) |
0.25 |
2^ (-3) |
0.125 |
2^ (-4) |
0.0625 |
The number of the sum of a sum that can be accurately expressed as 2 can be accurately expressed, while the other numbers are not accurate.
Why must we use binary?
Why can't we use the familiar decimal? At the very bottom, computers use electronic components that can only represent two states, usually low and high voltage, corresponding to 0 and 1, using binary easily to build hardware devices and perform calculations based on these electronic devices. If you do not want to use decimal, then the hardware is much more complex and inefficient.
What is the fractional calculation is accurate
If you write a program to experiment, you will find that some of the results are accurate. For example, I write in Java:
System.out.println (0.1f+0.1f);
System.out.println (0.1f*0.1f);
The first line outputs 0.2, the second line outputs 0.010000001. According to the above, the result of the first line should also be wrong ah?
In fact, this is only the Java language to create the illusion, the results are not accurate, but because the results and 0.2 close enough, in the output, Java chose the output 0.2 of this seemingly very concise number, rather than a middle has a lot of 0 decimal.
When the error is small enough, the result looks accurate, but inaccuracy is the norm.
How to deal with inaccurate calculations
Calculation is not accurate, how to do? In most cases, we do not need that high precision, can be rounded, or only a fixed number of decimal digits are left in the output.
If you really need a higher precision, one method is to convert decimals into integers, and then convert to decimal, the other method is generally using the decimal data type, this does not have a uniform specification, in Java is BigDecimal, the operation is more accurate, but the efficiency is low, This section will not be described in detail.
Binary representation
We've been using the word "decimal" to denote float and double type, in fact, this is not rigorous, "decimal" is the word used in mathematics, in the computer, we generally say "floating point". float and double are called floating-point data types, and fractional operations are called floating-point operations.
Why do you call a floating point? This is due to the decimal binary representation, which indicates that the point is not fixed, but floating.
We still use 10 binary analogy, 10 binary has scientific notation, such as 123.45 this number, directly so write, is fixed notation, if the scientific notation, only one digit before the decimal point, can be written as 1.2345E2 namely 1.2345* (10^2), that is, in the scientific notation, The decimal point floats two bits to the left.
In binary notation, a similar scientific notation is used to denote decimals, as in the form of m* (2^e). M is called the mantissa, and E is called an exponent. The index can be true or negative, and the negative exponent indicates which is close to 0 of the relatively small number. In binary, the number of parts of the tail and the exponent are represented separately, and a sign bit indicates positive or negative.
Almost all hardware and programming languages represent the same binary format for decimals, which is a standard, called the IEEE 754 standard, which defines two formats, one 32-bit, a float that corresponds to Java, and a 64-bit, double that corresponds to Java.
In 32-bit format, 1 bits represent the symbol, 23 bits represent the mantissa, and 8 bits represent the exponent. In 64-bit format, 1 bits represent the symbol, 52 bits represent the mantissa, and 11 bits represent the exponent.
In the two formats, in addition to the normal number, the standard also specifies that some special binary forms represent some special values, such as negative infinity, positive infinity, 0,nan (non-numeric, such as 0 times infinity).
The IEEE 754 standard has some complex details that are difficult to understand for the first time and are not commonly used in everyday applications.
If you want to see the specific binary form of a floating-point number, in Java, you can use the following code:
Integer.tobinarystring (float.floattointbits (value))
Long.tobinarystring (double.doubletolongbits (value));
Thinking logic of computer programs-why do decimal calculations go wrong?