Consider the floating point number 0.644696875. Let's convert it to a string with eight decimals using Java and C:
import java.lang.Math;
public class RoundExample{
public static void main(String[] args){
System.out.println(String.format("%10.8f",0.644696875));
}
}
result: 0.64469688
try it yourself: http://tpcg.io/oszC0w
#include <stdio.h>
int main()
{
printf("%10.8f", 0.644696875); //double to string
return 0;
}
result: 0.64469687
try it yourself: http://tpcg.io/fQqSRF
Why is the last digit different?
The number 0.644696875 cannot be represented exactly as a machine number. It is represented as the fraction 2903456606016923 / 4503599627370496 which has a value of 0.6446968749999999
This is admittedly an edge case. But I am really curious as to the source of the difference.
Related: https://mathematica.stackexchange.com/questions/204359/is-numberform-double-rounding-numbers
Double and float aren't used to represent values that require a very high precision. So, for instance, to store currency values, it's not a good idea to use double or float; instead Java has a class called BigDecimal that specifies the exact number of digits following the decimal, including rounding up or down.
In order to round float and double numbers in Java, we use the java. lang. Math. round() method.
float has 7 decimal digits of precision. double is a 64-bit IEEE 754 double precision Floating Point Number – 1 bit for the sign, 11 bits for the exponent, and 52* bits for the value. double has 15 decimal digits of precision.
Double is more precise than float and can store 64 bits, double of the number of bits float can store. Double is more precise and for storing large numbers, we prefer double over float. For example, to store the annual salary of the CEO of a company, double will be a more accurate choice.
The Java specification requires a troublesome double rounding in this situation. The number 0.6446968749999999470645661858725361526012420654296875 is first converted to 0.644696875 and then rounded to 0.64469688.
In contrast, the C implementation simply rounds 0.6446968749999999470645661858725361526012420654296875 directly to eight digits, producing 0.64469687.
For Double
, Java uses IEEE-754 basic 64-bit binary floating-point. In this format, the value nearest the number in the source text, 0.644696875, is 0.6446968749999999470645661858725361526012420654296875, and I believe this is the actual value to be formatted with String.format("%10.8f",0.644696875)
.1
The documentation for formatting with the Double
type and f
format says:
… If the precision is less than the number of digits which would appear after the decimal point in the string returned by
Float.toString(float)
orDouble.toString(double)
respectively, then the value will be rounded using the round half up algorithm. Otherwise, zeros may be appended to reach the precision…
Let’s consider “the string returned by … Double.toString(double)
”. For the number 0.6446968749999999470645661858725361526012420654296875, this string is “0.644696875”. This is because the Java specification says that toString
produces just enough decimal digits to uniquely distinguish the number within the set of Double
values, and “0.644696875” has just enough digits in this case.2
That number has nine digits after the decimal point, and "%10.8f"
requests eight, so the passage quoted above says “the value” is rounded. Which value does it mean—the actual operand of format
, which is 0.6446968749999999470645661858725361526012420654296875, or that string it mentions, “0.644696875”? Since the latter is not a numeric value, I would have expected “the value” to mean the former. However, the second sentence says “Otherwise [that is, if more digits are requested], zeros may be appended…” If we were using the actual operand of format
, we would show its digits, not use zeros. But, if we take the string as a numeric value, its decimal representation would have only zeros after the digits shown in it. So it seems this is the interpretation intended, and Java implementations appear to conform to that.
So, to format this number with "%10.8f"
, we first convert it to 0.644696875 and then round it using the round half up rule, which produces 0.64469688.
This is a bad specification because:
(Also, it is a shame they wrote zeros “may be” appended. Why not “Otherwise, zeros are appended to reach the precision”? With “may”, it seems like they are giving the implementation a choice, although I suspect they meant the “may” is predicated on whether zeros are needed to reach the precision, not on whether the implementor chooses to append them.)
1 When 0.644696875
in the source text is converted to Double
, I believe the result should be the nearest value representable in the Double
format. (I have not located this in the Java documentation, but it fits the Java philosophy of requiring implementations to behave identically, and I suspect the conversion is done in accordance with Double.valueOf(String s)
, which does require this.) The nearest Double
to 0.644696875 is 0.6446968749999999470645661858725361526012420654296875.
2 With fewer digits, the seven-digit 0.64469687 is insufficient because the Double
value closest to it is 0.6446968699999999774519210404832847416400909423828125. So eight digits are needed to uniquely distinguish 0.6446968749999999470645661858725361526012420654296875.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With