BigDecimal: never use float/double if decimals are important for you
authorgu.martinm@gmail.com <gu.martinm@gmail.com>
Sun, 27 Apr 2014 18:57:51 +0000 (20:57 +0200)
committergu.martinm@gmail.com <gu.martinm@gmail.com>
Sun, 27 Apr 2014 18:57:51 +0000 (20:57 +0200)
Allgemeines/BigDecimal/src/de/bigdecimal/test/MainTest.java [new file with mode: 0644]

diff --git a/Allgemeines/BigDecimal/src/de/bigdecimal/test/MainTest.java b/Allgemeines/BigDecimal/src/de/bigdecimal/test/MainTest.java
new file mode 100644 (file)
index 0000000..aeaf190
--- /dev/null
@@ -0,0 +1,352 @@
+package de.bigdecimal.test;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+
+/*
+ * REMEMBER, double/float THE IN MEMORY VALUES ARE NOT USUALLY WHAT YOU EXPECT
+ * BECAUSE OF THE IEE 754. SO, IF DECIMALS ARE IMPORTANT FOR YOU (LIKE WITH CURRENCY)
+ * NEVER USE double/float IN ANY POINT OF YOUR APPLICATION.
+ * 
+ */
+
+/*
+ * NICE EXPLANATIONS ABOUT WHEN TO USE DOUBLE/FLOAT AND WHEN BIGDECIMAL
+ *
+ * 1.
+ * http://stackoverflow.com/questions/2545567/in-net-how-do-i-choose-between-a-decimal-and-a-double
+ * I usually think about natural vs artificial quantities.
+ *
+ * Natural quantities are things like weight, height and time. These will never be measured absolutely
+ * accurately, and there's rarely any idea of absolutely exact arithmetic on it: you shouldn't generally
+ * be adding up heights and then making sure that the result is exactly as expected.
+ * Use double for this sort of quantity. Doubles have a huge range, but limited precision;
+ * they're also extremely fast.
+ *
+ * The dominant artificial quantity is money. There is such a thing as "exactly $10.52", and if you
+ * add 48 cents to it you expect to have exactly $11. Use decimal for this sort of quantity.
+ * Justification: given that it's artificial to start with, the numbers involved are artificial
+ * too, designed to meet human needs - which means they're naturally expressed in base 10.
+ * Make the storage representation match the human representation. decimal doesn't have the range of double,
+ * but most artificial quantities don't need that extra range either. It's also slower than double, but
+ * I'd personally have a bank account which gave me the right answer slowly than a wrong answer quickly :)
+ *
+ * For a bit more information, I have articles on .NET binary floating point types and the .NET decimal type.
+ * (Note that decimal is a floating point type too - but the "point" in question is a decimal point, not a binary point.)
+ * 
+ * FROM ME:
+ * BUT BE CAREFUL BECAUSE IF YOU HAVE MATH OPERATIONS WITH NATURAL QUANTITIES YOU COULD FINISH HAVING
+ * -0.0 or NaN, AND I GUESS YOU DO NOT WANT TO SHOW TO THE USER A WEIGHT OF -0.0 or NaN :/ SO DEPENDING ON
+ * THE NATURAL QUANTITIES (IF THEY ARE SMALL OR BIG NUMBERS) AND THE MATH OPERATIONS, PERHAPS
+ * YOU COULD WANT TO USE A FIXED-POINT NUMBER (LIKE BigDecimal IN JAVA OR decimal IN C#)
+ * SO, THIS IS A BIT COMPLICATED: PERHAPS EVEN IF YOU FINISH HAVING -0.0, YOU COULD DO SOMETHING
+ * LIKE THIS:
+ * public static boolean isNegative(double d) {
+ *    return Double.compare(d, 0.0) < 0;
+ * }
+ * AND IN THAT CASE YOU COULD SHOW 0.0 INSTEAD OF -0.0. ALL DEPENDS ON WHAT ARE YOU DOING.
+ * I GUESS IF YOU DO NOT KNOW ANYTHING ABOUT THE NUMBERS YOU ARE USING YOU MUST USE
+ * FIXED-POINT NUMBER BigDecimal, decimal OR BUILDING YOURSELF SOME INTEGER WHERE THE LAST 4 NUMBERS
+ * COULD BE DECIMALS AND THEN YOU ALWAYS *10000 AND /10000. THIS IS THE FASTEST SOLUTION BUT
+ * I DO NOT THINK IT IS THE BEST. THE BEST (IMHO) IF YOU KNOW NOTHIG IS BigDecimal AND decimal
+ * (FIXED-POINT NUMBER)
+ * 
+ * 2.
+ * SEE: http://randomascii.wordpress.com/2012/02/13/dont-store-that-in-a-float/
+ * WITH VIDEO GAMES WHEN DECIMALS ARE IMPORTANT (FOR EXAMPLE WHEN CALCULATING FPS) YOU MIGHT
+ * WANT TO USE DOUBLE. BUT AT THE END (IMHO) THE BEST WOULD BE A FIXED-POINT NUMBER (LIKE BigDecimal IN
+ * JAVA OR decimal IN C#)
+ * 
+ * 3. (fixed-point number)
+ * SEE: http://home.comcast.net/~tom_forsyth/blog.wiki.html#OffendOMatic link "A Matter of precision"
+ * YOU MUST ALWAYS USE FIXED-POINT NUMBER!!!!
+ * 
+ * (note to 1.)
+ * 
+ */
+
+public class MainTest {
+
+    public static void main(final String[] args) {
+        /**
+         * WARNING: if you write 165.01499999999998 after compiling this code
+         * javac will write "for you" the value 165.015 in your bytecode :/
+         * Use: javap -verbose BigDecimal/bin/de/bigdecimal/test/MainTest You will see 165.015d
+         * instead of something closer to what you would have using double.
+         * For example, gcc with that value writes in your assembly code 4064A07AE147AE14. By the way
+         * 165.015d and 165.01499999999998 are the same in memory: 4064A07AE147AE14. They
+         * are adjacent values and because of that javac and Double.toString transform it to 165.015d.
+         * 
+         * IF YOU READ THE Double.toString JAVADOC YOU WILL SEE THAT THE JAVA DEVELOPERS OF DOUBLE.TOSTRING
+         * DECIDED TO SHOW ALWAYS THE SMALLEST ADJACENT VALUE. THIS DECISION IS NOT GOOD OR BAD,
+         * IF FOR YOU IT IS A PROBLEM IT IS BECAUSE YOU SHOULD NOT HAVE USED SINCE THE FIRST
+         * VERY MOMENT DOUBLE OR FLOATS. IF DECIMALS ARE IMPORTANT FOR YOU MUST USE BigDecimal.
+         * SO, IF YOU THINK DOUBLE.TOSTRING IS WRONG, IT IS NOT THE PROBLEM OF DOUBLE.TOSTRING
+         * IT IS YOUR PROBLEM BECAUSE YOU ARE WORKING WHEN DOUBLE/FLOAT WHEN YOU SHOULD HAVE
+         * WORKED WITH BigDecimal.
+         **/
+
+        /**
+         * Double.toString TRANSFORMS 165.01499999999998 in 165.015d the same as javac does :/
+         */
+
+        /**
+         * If decimals are important for you never use double/float.
+         **/
+
+        /**
+         * new BigDecimal(double) tries to represent the real value of a float/double. When I write
+         * real value I mean the value in memory, which should use IEE 754.
+         * new BigDecimal(String) tries to represent what the user expects to see.
+         * 
+         * If you debug this code you will see that new BigDecimal(double) and new BigDecimal(String)
+         * are storing different values. The first one extracts (using Double.doubleToLongBits)
+         * the in memory value of some double/float and stores it in its internal instance variables.
+         * The second one takes the value in the string and stores it in its internal instance
+         * variables, it does no try to extract the in memory value (the IEE 754 value)
+         **/
+
+        final BigDecimal fromString = new BigDecimal("165.01499999999998");
+        /**
+         * 1. I write: "165.01499999999998"
+         * 2. javac writes: string "165.01499999999998"
+         * 3. BigDecimal stores: 165.01499999999998 (the String value without modifications)
+         */
+
+        /**
+         * 1. extracts the stored value in BigDecimal
+         */
+        System.out.println("fromString (BigDecimal stores the String value without modifications): " + fromString.toString());
+        /**
+         * 1. extracts the stored value in BigDecimal
+         * 2. transform that value in double using  FloatingDecimal.readJavaFormatString(s).doubleValue();
+         *    I do not know what FloatingDecimal.readJavaFormatString(s).doubleValue() returns but
+         *    even if it does 165.0149999999999863575794734060764312744140625 then if we want to represent it we must use
+         *    Double.toString and it ALWAYS TRANSFORM IT to 165.015 :(
+         * 3. create string with Double.toString() it ends up with 165.015 :(
+         * 
+         */
+        System.out.println("fromString using Double.toString (println): " + fromString.doubleValue());
+
+        /**
+         * Double.toHexString takes a double value and by means of Double.doubleToLongBits
+         * extracts the in memory values: (sign, mantissa and exponent)
+         * I do not know what FloatingDecimal.readJavaFormatString(s).doubleValue() returns but
+         * even if it does 165.0149999999999863575794734060764312744140625 it does not matter because
+         * 165.015, 165.01499999999998, 165.0149999999999863575794734060764312744140625 have the same in
+         * memory values: 165.0149999999999863575794734060764312744140625
+         */
+        System.out.println("fromString hexadecimal (IEE 754 value): " + Double.toHexString(fromString.doubleValue()));
+
+        /**
+         * SCALE 2, HALF_UP we have 165.01499999999998
+         * SCALE 2: 165.01
+         * HALF_UP: 0.499999999998 <---- less than 0.5 then we should see as result 165.01
+         */
+        System.out.println("fromString rounding two: " + fromString.setScale(2, RoundingMode.HALF_UP).toString());
+        System.out.println("fromString rounding two using Double.toString: " + fromString.setScale(2, RoundingMode.HALF_UP).doubleValue());
+        /**
+         * SCALE 4, HALF_UP we have 165.01499999999998
+         * SCALE 4: 165.0149
+         * HALF_UP: 0.9999999998 <---- more than 0.5 then we should see as result 165.0150
+         */
+        System.out.println("fromString rounding four: " + fromString.setScale(4, RoundingMode.HALF_UP).toString());
+        System.out.println("fromString rounding four using Double.toString: " + fromString.setScale(4, RoundingMode.HALF_UP).doubleValue());
+        
+
+        /**
+         * 1. I write: 165.01499999999998
+         * 2. javac writes: string 165.015
+         * 3. BigDecimal stores: 165.0149999999999863575794734060764312744140625 (the IEE 754 value,
+         * the in memory value) It uses Double.doubleToLongBits. As we know the in memory
+         * value of 165.015 is 165.0149999999999863575794734060764312744140625 and it is the same
+         * as the in memory value for 165.01499999999998
+         */
+        final BigDecimal fromDouble = new BigDecimal(165.01499999999998);
+
+        /**
+         * 1. extracts the stored value in BigDecimal and shows it as string.
+         */
+        System.out.println("fromDouble (BigDecimal stores the in memory value): " + fromDouble.toString());
+
+        /**
+         * 1. extracts the stored value in BigDecimal
+         * 2. transform that value in double using  FloatingDecimal.readJavaFormatString(s).doubleValue();
+         *    I do not know what FloatingDecimal.readJavaFormatString(s).doubleValue() returns but
+         *    even if it does 165.0149999999999863575794734060764312744140625 then if we want to represent it we must use
+         *    Double.toString and it ALWAYS TRANSFORM IT to "165.015" (Double.toString removes adjacent values
+         *    and shows the "smallest" adjacent value. :(
+         * 3. create string with Double.toString() it ends up with "165.015" :(
+         * 
+         */
+        System.out.println("fromDouble using Double.toString (println): " + fromDouble.doubleValue());
+
+        /**
+         * Double.toHexString takes a double value and by means of Double.doubleToLongBits
+         * extracts the in memory values: (sign, mantissa and exponent)
+         * I do not know what FloatingDecimal.readJavaFormatString(s).doubleValue() returns but
+         * even if it does 165.015 it does not matter because 165.015 has the same in
+         * memory value as 165.01499999999998: 165.0149999999999863575794734060764312744140625
+         */
+        System.out.println("fromDouble hexadecimal (IEE 754 value): " + Double.toHexString(fromDouble.doubleValue()));
+        System.out.println("fromDouble rounding two: " + fromDouble.setScale(2, RoundingMode.HALF_UP).toString());
+        System.out.println("fromDouble rounding two using Double.toString: " + fromDouble.setScale(2, RoundingMode.HALF_UP).doubleValue());
+        System.out.println("fromDouble rounding four: " + fromDouble.setScale(4, RoundingMode.HALF_UP).toString());
+        System.out.println("fromDouble rounding four using Double.toString: " + fromDouble.setScale(4, RoundingMode.HALF_UP).doubleValue());
+
+        /**
+         * 1. I write: 165.01499999999998
+         * 2. javac writes: string 165.015
+         * 3. BigDecimal stores: 165.015 Even if javac did not take away decimals, because
+         *    BigDecimal.valueOf is using Double.toString it will end up with 165.015 instead
+         *    of 165.01499999999998. This BigDecimal uses the constructor new BigDecimal(String)
+         *    which stores the value in String without transformations, the problem is
+         *    there is Double.toString transformation before the constructor BigDecimal(String)
+         *    IMHO this sucks, I do not understand why someone would want to use in this
+         *    way BigDecimal, for example Apache MathUtils is using it for the round method
+         *    and as you can see Double.toString applies some transformations, which in many cases
+         *    could break our results...
+         *    I would never use BigDecimal.valueOf, what I would try always to use is
+         *    new BigDecimal(String) in the edges of my application (I would receive numbers
+         *    as strings from the devices reading for example prices), then I would work in my
+         *    whole application with BigDecimal and at the end (to show values to user) I would
+         *    use BigDecimal.setScale(2, RoundingMode.HALF_UP).toString() IN THIS WAY MY APP
+         *    WILL NEVER FAIL AND EVERYTHING WILL WORK AS EXPECTED!!!!
+         */
+        final BigDecimal fromValueOf = BigDecimal.valueOf(165.01499999999998);
+
+        /**
+         * 1. extracts the stored value in BigDecimal and shows it as string.
+         *    I expected to see the same as in fromDouble.toString() but I WAS WRONG BECAUSE
+         *    BigDecimal STORES DIFFERENT VALUES DEPENDING ON THE CONSTRUCTOR WE USE.
+         */
+        System.out.println("fromValueOf (BigDecimal stores the Double.toString(double) value "
+                + "without modifications, it is Double.toString what would remove adjacent values. "
+                + "In this case, even before Double.toString, javac removed adjacent values and in byte code "
+                + "we have 165.015. Anyhow even without javac removing values, Double.toString would have "
+                + "finished with 165.015 instead of 165.01499999999998 WHEN DECIMALS ARE A PROBLEM, YOU MUST NO USE "
+                + "DOUBLE/FLOAT IF YOU ARE USING THEM, YOU ARE DOING IT WRONG): " + fromValueOf.toString());
+
+        /**
+         * 1. extracts the stored value in BigDecimal
+         * 2. transform that value in double using  FloatingDecimal.readJavaFormatString(s).doubleValue();
+         *    I do not know what FloatingDecimal.readJavaFormatString(s).doubleValue() returns but
+         *    even if it does 165.0149999999999863575794734060764312744140625 then if we want to represent
+         *    it we must use Double.toString and it ALWAYS TRANSFORM IT to 165.015 :(
+         * 3. create string with Double.toString() it ends up with 165.015 :(
+         * 
+         */
+        System.out.println("fromValueOf using Double.toString (println): " + fromValueOf.doubleValue());
+
+        /**
+         * Double.toHexString takes a double value and by means of Double.doubleToLongBits
+         * extracts the in memory values: (sign, mantissa and exponent)
+         * I do not know what FloatingDecimal.readJavaFormatString(s).doubleValue() returns but
+         * even if it does 165.0149999999999863575794734060764312744140625 it does not matter because
+         * 165.015 has the same in memory value: 165.0149999999999863575794734060764312744140625
+         */
+        System.out.println("fromValueOf hexadecimal (IEE 754 value): " + Double.toHexString(fromValueOf.doubleValue()));
+        System.out.println("fromValueOf rounding two: " + fromValueOf.setScale(2, RoundingMode.HALF_UP).toString());
+        System.out.println("fromValueOf rounding two using Double.toString: " + fromValueOf.setScale(2, RoundingMode.HALF_UP).doubleValue());
+        System.out.println("fromValueOf rounding four: " + fromValueOf.setScale(4, RoundingMode.HALF_UP).toString());
+        System.out.println("fromValueOf rounding four using Double.toString: " + fromValueOf.setScale(4, RoundingMode.HALF_UP).doubleValue());
+
+
+        /**
+         * 1. Double.toString ALWAYS TAKES AWAY DECIMALS (if they are not useful, as in our case
+         *    the in memory value is the same for 165.015 and for 165.01499999999998)
+         * 2. BigDecimal STORES DIFFERENT VALUES DEPENDING ON WHAT CONSTRUCTOR WE USE.
+         *    BECAUSE OF THAT BigDecimal.toString RETURNS DIFFERENT VALUES DEPENDING ON THE
+         *    CONSTRUCTOR.
+         * 3. BE CAREFUL, DEPENDING ON WHAT YOU WANT TO DO YOU MIGHT WANT TO USE BigDecimal(double)
+         *    instead of BigDecimal(String)
+         *    BigDecimal(String): stores the String as a BigDecimal value
+         *    BigDecimal(double): stores the in memory value of that double as a BigDecimal value.
+         * 4. JUST IN THE MOMENT YOU CONVERT DOUBLE TO STRING THERE ARE PROBLEMS IF YOU WANT
+         *    TO KEEP ALL YOUR DECIMALS. IN MEMORY:
+         *    - 165.015 is 165.0149999999999863575794734060764312744140625
+         *    - 165.01499999999998 is  165.0149999999999863575794734060764312744140625
+         *    SO IF DOUBLE.TOSTRING SHOWS 165.015 IT IS NOT WRONG, THE PROBLEM IS JUST IN THE MOMENT
+         *    YOU USE double YOUR REAL DECIMALS DISSAPEAR BECAUSE THE IEE 754 VALUE IS NOT WHAT YOU EXPECT!!!!
+         * 5. If you use double or float, some times will be impossible to retrieve the value
+         *    you wrote because the in memory value (the IEE 754) is not what you expect. Because
+         *    of that if Double.toString removes some decimals, it is not wrong because the in memory
+         *    value is the same for adjacent values, it does not know what you wanted to see,
+         *    it just know what there is in memory. Besides, the Java developers of Double.toString
+         *    decided to remove decimals when having adjacent values and to show to the user
+         *    the "smallest" adjacent value. That is why if you use
+         *    Double.toString(165.0149999999999863575794734060764312744140625) you finish having
+         *    "165.015" instead of "165.0149999999999863575794734060764312744140625".
+         */
+
+        /**
+         * AND OF COURSE, NEVER USE FLOAT OR DOUBLE IF THE DECIMALS ARE IMPORTANT FOR YOU!!!!!!!
+         * THAT IS WHY YOU MUST NEVER USE FLOAT/DOUBLE WITH CURRENCY. ROUND,DIVISION,SUM,REST
+         * OPERATIONS WILL BE WRONG FOR SURE IF YOU USE FLOAT/DOUBLE. THE WHOLE APPLICATION MUST USE
+         * BigDecimal WITH CURRENCY, JUST IN THE MOMENT YOU USE DOUBLE/FLOAT FOR ROUND,DIVISION,SUM,
+         * REST OPERATIONS YOUR APPLICATION WILL BE BROKEN.
+         * 
+         * AND NEVER EVER TRY TO USE double == double / double != double / float == float /
+         * float != float
+         */
+
+
+        /**
+         * I GUESS FOR SOME APPLICATION THE BEST WOULD BE TO USE IN THE EDGES (FOR EXAMPLE
+         * SOME DEVICE READING PRICES AND RETURNING PRICES AS STRINGS) STRING, THEN WE COULD USE
+         * STRING AS INPUT PARAMETER FOR BigDecimal, (WE WOULD USE new BigDecimal(String))
+         * THE WHOLE APPLICATION WOULD WORK WITH BigDecimal FOR CURRENCY AND IN THIS WAY NOTHING
+         * WRONG SHOULD HAPPEN.
+         */
+
+
+        final BigDecimal fromFunnyString = new BigDecimal("165.015");
+        System.out.println("fromFunnyString (BigDecimal stores the String value without modifications): " + fromFunnyString.toString());
+        System.out.println("fromFunnyString using Double.toString (println): " + fromFunnyString.doubleValue());
+        System.out.println("fromFunnyString hexadecimal (IEE 754 value): " + Double.toHexString(fromFunnyString.doubleValue()));
+        System.out.println("fromFunnyString rounding two: " + fromFunnyString.setScale(2, RoundingMode.HALF_UP).toString());
+        System.out.println("fromFunnyString rounding two using Double.toString: " + fromFunnyString.setScale(2, RoundingMode.HALF_UP).doubleValue());
+        System.out.println("fromFunnyString rounding four: " + fromFunnyString.setScale(4, RoundingMode.HALF_UP).toString());
+        System.out.println("fromFunnyString rounding four using Double.toString: " + fromFunnyString.setScale(4, RoundingMode.HALF_UP).doubleValue());
+
+
+        final BigDecimal fromFunnyDouble = new BigDecimal(165.015);
+        System.out.println("fromFunnyDouble (BigDecimal stores the in memory value): " + fromFunnyDouble.toString());
+        System.out.println("fromFunnyDouble using Double.toString (println): " + fromFunnyDouble.doubleValue());
+        System.out.println("fromFunnyDouble hexadecimal (IEE 754 value): " + Double.toHexString(fromFunnyDouble.doubleValue()));
+
+        /**
+         * BigDecimal stores the in memory value, and it will work with it. That is the reason
+         * we end up having "unexpected" results in the next two cases.
+         * There is not 165.015 value for IEE 754. The in memory value is: 165.0149999999999863575794734060764312744140625
+         */
+        System.out.println("(YOU DID NOT EXPECT THIS, DID YOU?) fromFunnyDouble rounding two: " + fromFunnyDouble.setScale(2, RoundingMode.HALF_UP).toString());
+        System.out.println("(YOU DID NOT EXPECT THIS, DID YOU?) fromFunnyDouble rounding two using Double.toString: " + fromFunnyDouble.setScale(2, RoundingMode.HALF_UP).doubleValue());
+        System.out.println("(YOU DID NOT EXPECT THIS, DID YOU?) fromFunnyDouble rounding four: " + fromFunnyDouble.setScale(4, RoundingMode.HALF_UP).toString());
+        System.out.println("(YOU DID NOT EXPECT THIS, DID YOU?) fromFunnyDouble rounding four using Double.toString: " + fromFunnyDouble.setScale(4, RoundingMode.HALF_UP).doubleValue());
+
+
+        final BigDecimal fromFunnyValueOf = BigDecimal.valueOf(165.015);
+        System.out.println("fromFunnyValueOf (BigDecimal stores the Double.toString(double) value "
+                + "without modifications, Double.toString removes adjacent values. In this case "
+                + "the Double.toString(165.015) is 165.015 but in other cases it could be a problem if "
+                + "Double.toString removes our decimals. Anyhow WHEN DECIMALS ARE A PROBLEM, YOU MUST NO USE "
+                + "DOUBLE/FLOAT IF YOU ARE USING THEM, YOU ARE DOING IT WRONG): " + fromFunnyValueOf.toString());
+        System.out.println("fromFunnyValueOf using Double.toString (println): " + fromFunnyValueOf.doubleValue());
+        System.out.println("fromFunnyValueOf hexadecimal (IEE 754 value): " + Double.toHexString(fromFunnyValueOf.doubleValue()));
+        System.out.println("fromFunnyValueOf rounding two: " + fromFunnyValueOf.setScale(2, RoundingMode.HALF_UP).toString());
+        System.out.println("fromFunnyValueOf rounding two using Double.toString: " + fromFunnyValueOf.setScale(2, RoundingMode.HALF_UP).doubleValue());
+        System.out.println("fromFunnyValueOf rounding four: " + fromFunnyValueOf.setScale(4, RoundingMode.HALF_UP).toString());
+        System.out.println("fromFunnyValueOf rounding four using Double.toString: " + fromFunnyValueOf.setScale(4, RoundingMode.HALF_UP).doubleValue());
+
+        final DecimalFormat tempFormatter = (DecimalFormat) NumberFormat.getNumberInstance(Locale.US);
+        tempFormatter.applyPattern("#####.#################");
+        System.out.println("DecimalFormat: " + tempFormatter.format(165.01499999999998));
+        System.out.println("String.format (printf style): " + String.format(Locale.US, "%.20f", 165.01499999999998));
+    }
+
+}