Use floating point numbers and techniques and traps

xiaoxiao2021-03-06  48

Many programmers have never used fixed-point or floating point in its entire development career, and possible exceptions are occasionally used in the timing test or reference test program. Java language and class library support two types of non-integer types - IEEE 754 floating point (

Float and

Double, Wrapper Class

Float and

Double), as well as the decimal of any precision (

Java.math.bigDecimal). In this month

In Java Theory and Practice, Brian Goetz explores the traps and "gotcha" that often encounters when using non-integer types in the Java program. Please in this article

In the forum, you propose your ideas to this article, with a pen and other readers. (You can also access the forum by clicking the discussion on top or bottom this article).

Although almost every processor and programming language support floating point operations, most programmers pay less attention to it. This is easy to understand - most of us rarely need to use non-integer types. In addition to scientific calculations and occasional timing tests or reference test procedures, almost all in other cases don't use it. Similarly, most developers are also easy to ignore the decisions of any precision provided by Java.math.BigDecimal - Most applications do not use them. However, in a program-based program, it is sometimes unexpectedly desirable to represent non-integer data. For example, JDBC uses BigDecimal as the preferred interchange format of the SQL Decimal column.

IEEE floating point Java language supports two basic floating point types: float and double, and packaging float and double with their corresponding packaging float. They all define binary standards for 32-bit floating point and 64-bit double-precision floating-point second-binary scores.

The IEEE 754 uses scientific notes to represent the number of floating point in the decimal number of 2. The IEEE floating point number uses 1 bits to represent the number symbol, indicates the index with 8, and use 23 points to indicate the mantissa, that is, the fractional portion. As a symbol integer, there is a positive and negative points. The decimal part expressed with binary (base 2) decimal, which means the highest bit corresponding to the value? (2-1), the second bit corresponds to? (2-2), so on. For double precision floating point, use 11 bits to represent an index, 52 bits indicate the mantissa. The format of the IEEE floating point value is shown in Figure 1.

Figure 1. Format of the IEEE 754 floating point number

Because there are several ways to use scientific notes, there is a given number, so the number of floats is standardized so that the decimation index can be determined with a decimation index with a decimation index with a decimation index in which the bottom number is 2 and a decimation index. Therefore, for example, the number 1.25 can be expressed as 1.01, index is 0: (- 1) 0 * 1.012 * 20

10.0 can be represented as 1.01, index is 3: (- 1) 0 * 1.012 * 23

Special numbers in addition to the standard range allowed by the encoding (for float, from 1.4e-45 to 3.4028235e 38), there are also some expressions that are infinite, infinite, -0 and nan (which represent "not a number") Special value. The presence of these values ​​is to indicate the result of the floating point value set in order to occur in the event of an error condition (such as arithmetic overflow, the negative number, divided by 0, etc.).

These special numbers have some unusual features. For example, 0 and -0 are different values, but is considered equal when comparing whether they are equal. Use a non-zero number to be infinite, the result is equal to 0. Special digital nan is disorderless; when using ==, operators compare NANs with other floating point values, the result is false. If F is NAN, false even if (f == f) is obtained. If you want to compare floating point values ​​with nan, use the float.isnan () method. Table 1 shows some properties of infinity and NAN. Table 1. Properties of special floating point values

Expression results Math.SQRT (-1.0) -> na 0.0 / 0.0-> NAN1.0 / 0.0-> infinity big -1.0 / 0.0-> negative infinity NAN 1.0-> NAN infinity 1.0-> infinity infinity infinity > Infinity nan> 1.0-> falsenan == 1.0-> falsenan <1.0-> falsenan == nan-> false0.0 == -0.01-> true

Basic floating-point types and packaging floating points have different comparison behaviors to make things worse, between basic float types and packaging float, for comparing NAN and -0 rules. For FLOAT values, compare whether the two NAN values ​​will get FALSE, and use float.equals () to compare two NAN FLOAT objects to get true. The reason for this phenomenon is that if not, it is impossible to use the Nan Float object as the key in the HashMap. Similarly, although 0 and -0 are considered equal when it is represented as a floating point value, but using float.Compareto () to compare 0 and -0 as Float objects, -0 is less than 0.

Danger in floating point due to infinity, NAN and 0 special behavior For example, although it seems that 0.0-f is obviously equal to -F, it is incorrect when F is 0. There are other similar Gotcha, Table 2 shows some of these Gotcha.

Table 2. Invalid floating point hypothesis

This expression is not necessarily equal to ... when ... 0.0 - ff is 0f = g) f or g is nanf == ftruef is NANF G - GFG is infinity or NAN

Floating the floating point operation is very accurate. Although some numbers (such as 0.5) can be accurately expressed as binary (base 2) decimal (because 0.5 is equal to 2-1), other numbers (such as 0.1) cannot be accurately represented. Therefore, floating point calculations may result in rounding errors, resulting near - but not equal - the result of you may wish. For example, this simple calculation will get 2.600000000000001, not 2.6:

Double s = 0;

For (int i = 0; i <26; i )

S = 0.1;

System.out.println (s);

Similarly, the result produced by. 1 * 26 is not equal to .1 itself adds 26 results. When the floating point number is forced into an integer, the resulting rounding error is even more serious because the forced conversion into an integer type will discard the non-integer part, and even for those "look" should be calculated, there is also this class. problem. For example, the following statement: Double D = 29.0 * 0.01;

System.out.println (d);

System.out.println (INT) (D * 100));

Will get the following output:

0.29

Twist

This may not be what you expect.

Floating-Point Comparison Guide Due to the unusual comparison behavior of NAN, the results of the comparison operators that explain the floating-point value are more troublesome in almost all floating point calculations.

It is best to avoid the use of floating point numbers. Of course, this is not always possible, but you should realize that it is to limit floating point comparisons. If you have to compare the floating point, you should look at whether they are equal, you should compare them with some pre-selected little positive numbers so that you do is to test whether they are "close". (If you don't know the basic calculation range, you can use Test "ABS (A / B - 1)

The disorder nature of NAN makes it easier to occur when comparing floating point numbers. When comparing floating point numbers, a mobility to avoid Gotcha is explicitly tested, which is explicitly tested, which is an explicit test value. In Listing 1, there are two possible SETTER implementations for features, which can only accept non-negative numbers. The first implementation will accept NAN, the second is not. The second form is better because it explicitly detects the range of values ​​you think.

Listing 1. Need good ways and poor ways for non-negative floating point values

// Trying to test by Exclusion - this doesn't catch nan or infinity

Public void setfoo (float foo) {

IF (foo <0)

Throw new IllegalargumentException (FLOAT.TOSTRING (F));

THIS.FOO = foo;

}

// Testing by inclusion - this does catch nan

Public void setfoo (float foo) {

IF (foo> = 0 && foo

THIS.FOO = foo;

Else

Throw new IllegalargumentException (FLOAT.TOSTRING (F));

}

Do not use floating point values ​​to indicate that some non-integer values ​​(such as a few dollars and a few decisions) need to be precise. The floating point is not a precise value, so use them will result in rounding errors. Therefore, using floating point numbers to try to indicate that the quantity of the amount of money is not a good idea. The use of floating point is used for dollar and coding calculations to get catastrophic consequences. The floating point number is best used to represent the value of the measured value, which is not accurate from the beginning.

BigDecimal for smaller BigDecimal from JDK 1.3, Java developers have another numerical representation to represent non-integers: BigDecimal. BigDecimal is a standard class that does not require special support in the compiler, which can represent the decimal of any precision and calculate them. Internally, the value of any precision can be used to represent the value of any range and a conversion factor, and the conversion factor indicates how many bits of the left-shifter decimal point, thereby obtaining the value within the desired range. Therefore, in the form of a number represented by BigDecimal is UnscaledValue * 10-Scale. Method for adding, minus, multiplication, and division provides arithmetic operations for BigDecimal values. Since the BigDecimal object is not variable, each of these methods produces a new BigDecimal object. Therefore, because the cost of creating an object, BigDecimal is not suitable for a large number of mathematical calculations, but the purpose of designing it is to accurately represent the decimal. If you are looking for a value that can accurately indicate such a currency, BigDecimal can be better enough to do this task.

All equals methods cannot really test equal such as floating point types, BigDecimal also has some strange behavior. Especially if you use the equals () method to detect whether the values ​​are equal, be careful. The equals () method believes that both BigDecimal values ​​indicating the same number but different (for example, 100.00 and 100.000) are unequal. However, the Compareto () method will think that both numbers are equal, so compare () instead of equals () should be used when comparing two BigDecimal values ​​from the value.

There are also some cases, and the decimal operation of any precision is still unable to indicate precise results. For example, 1 is divided into decimal decimal in which an infinite cycle is generated. 111111 ... For this reason, BigDecimal allows you to explicitly control. The MovePointLeft () method supports the precise division of the power of 10.

Using BigDecimal as the interchange type SQL-92 includes a Decimal data type, it is used to indicate an exact digital type of a fixed point decimal, which can make a basic arithmetic operation for the decimal. Some SQL languages ​​like this type of Numeric type, and some other SQL languages ​​introduced the Money data type, and the Money data type is defined as two decimals with two digits on the right side of the decimal point.

If you want to store numbers to the Decimal field in the database, or retrieve values ​​from the Decimal field, how to ensure accurate conversion? You may not want to use the setfloat () and getFloat () methods provided by the JDBC PreparedStatement and ResultSet classes, because the conversion between floats and decimals may lose accuracy. Instead, use PreparedStatement and ResultSet's setBigDecimal () and getBigDecimal () methods.

Similarly, XML data binding tools such as CaStor uses BigDecimal to generate small numerical properties and elements (supporting this basic data type in XSD mode).

Constructing the number of BigDecimal for BigDecimal, there are several available constructor. One of the constructor is used as input, and the other is an integer and conversion factor as an input, and there is an input as an input in a decimal String. Be careful with the BigDecimal (Double) constructor, because if it does not understand it, a rounding error is generated during the calculation process. Use constructor based on integer or string. If you use the BigDecimal (Double) constructor, it is not appropriate to pass to the JDBC setBigDecimal () method, it will cause an exception in the JDBC driver. For example, consider the following JDBC code, the code wants to store the number 0.01 to the decimal field:

PreparedStatement PS =

Connection.PrepareStatement ("INSERT INTO FOO SET NAME = ?, value =?");

Ps.setstring (1, "penny");

Ps.SetBigDecimal (2, New BigDecimal (0.01));

ps.executeUpdate ();

When performing this seemingly harmless code, it will throw some confusing anomalies (depending on the specific JDBC driver), because the two precision approximation of 0.01 will result in a large conversion value, which may make JDBC The driver or database is confused. The JDBC driver will produce an exception, but may not indicate where the code is actually wrong, unless the limitations of binary floating point numbers. Instead, use BigDecimal ("0.01") or BigDecimal (1, 2) to construct BigDecimal to avoid such problems, because both methods can accurately represent decimals.

Conclusion The use of floating point numbers and decimals in the Java program are full of traps. The floating point number and the decimal are not as an integer "follow-up", and cannot assume that the floating point calculation must have an integer or precise result, although they do "should". It is best to reserve floating point operations as a value that is not accurate, such as measurement. Use BigDecimal if you need to represent a fixed point (such as a few dollars and a few cents).

Reference

转载请注明原文地址:https://www.9cbs.com/read-117513.html

New Post(0)