Retain Precision With Double in Java

Retain precision with double in Java

As others have mentioned, you'll probably want to use the BigDecimal class, if you want to have an exact representation of 11.4.

Now, a little explanation into why this is happening:

The float and double primitive types in Java are floating point numbers, where the number is stored as a binary representation of a fraction and a exponent.

More specifically, a double-precision floating point value such as the double type is a 64-bit value, where:

  • 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).

These parts are combined to produce a double representation of a value.

(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 Values of the Java Language Specification.

The byte, char, int, long types are fixed-point numbers, 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.

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.

As has been mentioned several times already, Java has a BigDecimal class which will handle very large numbers and very small numbers.

From the Java API Reference for the BigDecimal class:

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).

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:

  • 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?

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.

Can I retain precision in Java if I make no operations to a double?

Java uses the IEEE-754 binary32 format for its double type. This type has the property that, for numbers within its exponent range, if any decimal numeral of 15 or fewer significant digits is converted to double (using round-to-nearest, ties-to-even), and the resulting double is converted back to a 15-digit decimal numeral, the result equals the original number.

For default formatting of floating-point numbers, Java produces just enough digits to uniquely distinguish the number within the floating-point format.

A consequence of these two properties is that converting a decimal numeral with 15 or fewer significant digits to double and then converting that back to decimal using the default formatting will produce something equal to the original number. It may differ in some respects. For example, the original numeral may have trailing zeros that the produced numeral does not, such as “3.00” to “3” or “123.4500” to “123.45”.

Java: How to set Precision for double value?

You can't set the precision of a double (or Double) to a specified number of decimal digits, because floating-point values don't have decimal digits. They have binary digits.

You will have to convert into a decimal radix, either via BigDecimal or DecimalFormat, depending on what you want to do with the value later.

See also my answer to this question for a refutation of the inevitable *100/100 answers.

How to resolve a Java Rounding Double issue

To control the precision of floating point arithmetic, you should use java.math.BigDecimal. Read The need for BigDecimal by John Zukowski for more information.

Given your example, the last line would be as following using BigDecimal.

import java.math.BigDecimal;

BigDecimal premium = BigDecimal.valueOf("1586.6");
BigDecimal netToCompany = BigDecimal.valueOf("708.75");
BigDecimal commission = premium.subtract(netToCompany);
System.out.println(commission + " = " + premium + " - " + netToCompany);

This results in the following output.

877.85 = 1586.6 - 708.75

How to determine the max precision for double

@PeterLawrey states max precision in 15.

That's actually not what he stated at all. What he stated was:

double has 15 decimal places of accuracy

and he is wrong. They have 15 decimal digits of accuracy.

The number of decimal digits in any number is given by its log to the base 10. 15 is the floor value of log10(253-1), where 53 is the number of bits of mantissa (including the implied bit), as described in the Javadoc and IEEE 754, and 253-1 is therefore the maximum possible mantissa value. The actual value is 15.954589770191003298111788092734 to the limits of the Windows calculator.

He is quite wrong to describe it as 'decimal places of accuracy'. A double has 15 decimal digits of accuracy if they are all before the decimal point. For numbers with fractional parts you can get many more than 15 digits in the decimal representation, because of the incommensurability of decimal and binary fractions.

Precision loss with java.lang.Double

I'm using a decimal floating point arithmetic with a precision of three decimal digits and (roughly) with the same features as the typical binary floating point arithmetic. Say you have 123.0 and 4.56. These numbers are represented by a mantissa (0<=m<1) and an exponent: 0.123*10^3 and 0.456*10^1, which I'll write as <.123e3> and <.456e1>. Adding two such numbers isn't immediately possible unless the exponents are equal, and that's why the addition proceeds according to:

 <.123e3>   <.123e3>
<.456e1> <.004e3>
--------
<.127e3>

You see that the necessary alignment of the decimal digits according to a common exponent produces a loss of precision. In the extreme case, the entire addend could be shifted into nothingness. (Think of summing an infinite series where the terms get smaller and smaller but would still contribute considerably to the sum being computed.)

Other sources of imprecision result from differences between binary and decimal fractions, where an exact fraction in one base cannot be represented without error using the other one.

So, in short, addition and subtraction between numbers from rather different orders of magnitude are bound to cause a loss of precision.

Is there Java data type exceeding digit precision of double?

As far as I know, Java type double can store numbers in range 1.0E+38 to 1.0E-45.

Incorrect; it can go as far as 1.7976931348623157 * 10^308 and 4.9406564584124654 x 10^-324.

However, there are only at most 2^64 numbers that can be stored in a double (reason: Think about it. Pigeon hole principle). There are an infinite amount of numbers between 0 and 1, let alone between those 2 extremes. doubles work by silently rounding, all the time, to the nearest number that is one of the chosen few 2^64. Let's call those blessed numbers.

These numbers are not equally distributed. Near 0, there are A LOT of these, as you move away from 0 there are fewer and fewer. Eventually (at around 2^52), the distance between any 2 blessed numbers is more than 1.0.

BigDecimal is one solution. There are others (for starters, double CAN represent 1e-108 - you're doing something wrong in your 'translate input data to a double value' code), but keep in mind that doubles at those extremes are incredibly inaccurate.

BD with numbers like that are incredibly slow, and out of the box, BDs cant divide out of the box (for the same reason you can't divide 1 by 3 and get a perfect result: 0.333333... and where does that stop?) - you need to configure it so it cuts off at some point. They're hard to use, but perhaps your only option.

After converting bits to Double, how to store actual float/double value without using BigDecimal?

There is no actual loss of precision; the issue is your incorrect expectations about how doubles are converted to String (e.g. when printed).

From the documentation of Double.toString:

How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double. That is, suppose that x is the exact mathematical value represented by the decimal representation produced by this method for a finite nonzero argument d. Then d must be the double value nearest to x; or if two double values are equally close to x, then d must be one of them and the least significant bit of the significand of d must be 0.

So when a double gets printed, it is printed only with enough digits to uniquely identify that double value, not with the number of digits needed to describe the precise value as a real number.

If you want to get the precise value of a double with all possible digits, new BigDecimal(theDouble).toPlainString() is how you do it -- and, as you demonstrate, it gets the correct result.

Java Double Precision: Different formula leads to different result

To control the precision of floating point arithmetic, you should use java.math.BigDecimal.
You can do something like this.

BigDecimal xBigdecimal = BigDecimal.valueOf(7d);
BigDecimal yBigdecimal = BigDecimal.valueOf(10d);
System.out.println(BigDecimal.valueOf(1).subtract(xBigdecimal.divide(yBigdecimal)));

Can anyone explain it for me?

The float and double primitive types in Java are floating point numbers, where the number is stored as a binary representation of a fraction and a exponent.

More specifically, a double-precision floating point value such as the double type is a 64-bit value, where:

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).
These parts are combined to produce a double representation of a value.

Check this

For a detailed description of how floating point values are handled in Java, follow Floating-Point Types, Formats, and Values of the Java Language Specification.

The byte, char, int, long types are fixed-point numbers, 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 0.30000000000000004 in the result of 1 - (x / y​).

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.

As I've already showed in the above example, Java has a BigDecimal class which will handle very large numbers and very small numbers.



Related Topics



Leave a reply



Submit