Double Precision - Decimal Places

What's the maximum precision (after the decimal point) of a float in Javascript

Short answer: you can probably squeeze out 15 "safe" digits, and it doesn't matter where you place your decimal point.

It's anyone's guess how the JavaScript standard is going to evolve and use other number representations.

Notice how the MDN doc says "about 17 decimals"? Right, it's because sometimes you can represent that many digits, and sometimes less. It's because the floating point representation doesn't map 1-to-1 to our decimal system.

Even numbers with seemingly less information will give rounding errors.

For example
0.1 + 0.2 => 0.30000000000000004

console.log(0.1 + 0.2);

Precision of double after decimal point

As stated by the C# reference, the precision is from 15 to 16 digits (depending on the decimal values represented) before or after the decimal point.

In short, you are right, it depends on the values before and after the decimal point.

For example:

  • 12345678.1234567D //Next digit to the right will get rounded up
  • 1234567.12345678D //Next digit to the right will get rounded up

Full sample at: http://ideone.com/eXvz3

Also, trying to think about double value as fixed decimal values is not a good idea.

Maximum number of decimal digits that can affect a double

When you have a subnormal number with odd significand, that is, an odd multiple of 2^(-1074), you have a number whose last nonzero digit in the decimal representation is the 1074th after the decimal point. You then have around 300 zeros directly following the decimal point, so the number has around 750-770 significant decimal digits. The smallest positive subnormal, 2^(-1074) has 751 significant digits, and the largest positive subnormal, (2^52-1)*2^(-1074) has 767 significant digits (I think that is the maximum).

So there is at least one sequence d1, ..., d766 of decimal digits such that there is an IEEE754 double in the open interval

(d1.d2...d766E-308, d1.d2...(d766 + 1)E-308)

The answer does not change much if we consider "contains the midpoint of two consecutive IEEE754 doubles", since subnormal doubles have all roughly the same amount of significant decimal digits, and the midpoint of two consecutive such too.

In the worst case, the entire digit sequence must be consumed (consider "0.5000000...0001" with arbitrarily many zeros before the final 1 that determines that the result shall be 0.5 + 0.5^53 and not 0.5 when rounding away from zero or up).

However, there are only

floor(DBL_MANT_DIG * log 2 / log 10) + 2 = 17

significant decimal digits necessary to distinguish between different double values, so a relatively easy, albeit probably not very efficient, method of parsing would be to parse the first (at least 17) digits (and the exponent) to the closest double, and compare the input string with the exact representation of that double value (and its neighbour).

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.

How many decimal places does the primitive float and double support?

Those are the total number of "significant figures" if you will, counting from left to right, regardless of where the decimal point is. Beyond those numbers of digits, accuracy is not preserved.

The counts you listed are for the base 10 representation.

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.

Would casting a calculated double number into decimal correct the precision error?

Does anyone know how the computer know in this case that it should change the 1000.0000000000002 value to 1000 when casting to decimal?

First of all the cast:

(decimal)IncorrectPPM

is equivalent to the constructor call, see here on SO:

new decimal(IncorrectPPM)

If you read on the MSDN page about the decimal constructor you will find the following remark:

This constructor rounds value to 15 significant digits using rounding to nearest. This is done even if the number has more than 15 digits and the less significant digits are zero.

That means

1000.0000000000002 
^ ^
15th 17th significant digit

will be rounded to:

1000.00000000000 
^
15th significant digit

Can I rely on this trick to avoid the precision issue of double calculation?

No, you can't imagine the following result when calculating IncorrectPPM, see online at ideone:

1000.000000000006
^
15th significant digit

will be rounded to:

1000.00000000001
^
15th significant digit

To resolve your issue about the comparison with your threshold, you have in general 2 possibilities.

  1. Add a little epsilon to your threshold, e.g.:

    double threshold = 1000.0001;
  2. Change your cast of IncorrectPPM from:

    double CorrectedPPM = (double)((decimal)IncorrectPPM);

    to:

    /* 1000.000000000006 will be rounded to 1000.0000 */
    double CorrectedPPM = Math.Round(IncorrectPPM, 4);

    with the Math.Round() function, but be careful Math.Round() means fractional not significant digits

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.



Related Topics



Leave a reply



Submit