DecimalFormat - Format decimal to preserve variable amount of trailing zeros - java

I have a situation where I need to preserve the number of decimal places for a number when formatting to a String using DecimalFormat, specifically for trailing zeros. I need to use DecimalFormat because I also need to avoid scientific notation for large numbers, and as seen here, this is the best way to do so. However, as you can see in that post, the code provided also removes trailing zeros, whereas I would like to preserve them.
1.00 -> "1.00"
1.000 -> "1.000"
I've seen a lot of questions that involve a fixed number of decimal places, but in my situation I need to account for a variable amount of decimal places. I looked into counting the number of decimal places, but since my input can also be a Double (in addition to BigDecimal), there appears to be no reliable way to count the digits after the decimal point for all numbers. Double.toString() does not work since Doubles break into exponential notation for very small and very big numbers. See here for more info regarding why it is difficult to count the number of decimal places in a double.

"Preserve" trailing zeros?
A double value doesn't know how many trailing zeroes you want to see. It is just a number, and 1.00 and 1.000 are the same number, i.e. the number 1. What you are asking cannot be done with a double value.
Now, BigDecimal does remember the number of trailing zeroes, so if you want to print a BigDecimal value, retaining the scale of the number, but ensuring it never prints in scientific notation, don't use a DecimalFormat, but instead use toPlainString():
Returns a string representation of this BigDecimal without an exponent field.
UPDATE
If you want to print a double value with as many decimal fraction digits as needed (i.e. no trailing zeroes), and want to make sure it never prints in scientific notation, use a DecimalFormat with very high MaximumIntegerDigits and very high setMaximumFractionDigits.
"Very high" means values exceeding the range of a double, so 999 is a good "round" number, though 330 would be high enough.
Test
DecimalFormat fmt = new DecimalFormat("0");
fmt.setMaximumIntegerDigits(330);
fmt.setMaximumFractionDigits(330);
System.out.println("0.0123400 = " + 0.0123400 + " = " + fmt.format(0.0123400));
System.out.println("123400.00 = " + 123400.00 + " = " + fmt.format(123400.00));
System.out.println("NaN = " + Double.NaN + " = " + fmt.format(Double.NaN));
System.out.println("-INFINITY = " + Double.NEGATIVE_INFINITY + " = " + fmt.format(Double.NEGATIVE_INFINITY));
System.out.println("+INFINITY = " + Double.POSITIVE_INFINITY + " = " + fmt.format(Double.POSITIVE_INFINITY));
System.out.println("MIN_NORMAL = " + Double.MIN_NORMAL + " = " + fmt.format(Double.MIN_NORMAL));
System.out.println("MIN_VALUE = " + Double.MIN_VALUE + " = " + fmt.format(Double.MIN_VALUE));
System.out.println("MAX_VALUE = " + Double.MAX_VALUE + " = " + fmt.format(Double.MAX_VALUE));
Output
0.0123400 = 0.01234 = 0.01234
123400.00 = 123400.0 = 123400
NaN = NaN = �
-INFINITY = -Infinity = -∞
+INFINITY = Infinity = ∞
MIN_NORMAL = 2.2250738585072014E-308 = 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072014
MIN_VALUE = 4.9E-324 = 0.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000049
MAX_VALUE = 1.7976931348623157E308 = 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Either double (no precision, always approximating values), or BigDecimal.
Use BigDecimal as new BigDecimal("2.00") defining the correct scale (precision) of two digits. You can set the scale programmatically.
For database and calculation (like financial) BigDecimal is fine.
The scientific representation on output can be avoided in BigDecimal.toPlainString().
For double one could format with
s = String.format("%10.2f", 13.5789); // " 13.58"
All this is with a decimal point and no thousands separators.
Internationalized (localized) software will use a MessageFormat with a Locale (explicit or the default platform locale).
Locale.setDefault(new Locale("bg", "BG"));
s = MessageFormat.format("Number: {0, number, #.##}, "
+ "amount: {1, number, currency}",
42.125, 19.99);

Related

Java DecimalFormat losing precision while formatting double

When i execute the below code:
public class Test {
public static void main(String args[]){
DecimalFormat format = new DecimalFormat();
Double value = new Double(-1350825904190559999913623552.00);
StringBuffer buffer = new StringBuffer();
FieldPosition position = new FieldPosition(0);
format.format(new BigDecimal(value), buffer, position);
System.out.println(buffer);
}
}
This correctly prints -1,350,825,904,190,559,999,913,623,552.
I have code which does go through a lot of doubles so I dont want the conversion from double to bigdecimal. I figured the processing time for BigDecimal is large.
So i do format.format(value, buffer, position)
And i see the precision is lost.
The output I get is -1,350,825,904,190,560,000,000,000,000.
What am i doing wrong here? Is there a better way to deal with this and still retain the precision. I don't want to deal with BigDecimals here but just work with the decimals.
Any suggestions?
double doesn't have infinite precision, and you can't gain more precision than a double has by converting a double to a BigDecimal (like you can't gain more precision with an int when you do double r = 1/3; which is 0.0 because it widens an int to a double). Instead, you could use a String. Something like
DecimalFormat format = new DecimalFormat();
String value = "-1350825904190559999913623552.00";
System.out.println(format.format(new BigDecimal(value)));
It isn't lost during formatting. It is lost right here:
Double value = new Double(-1350825904190559999913623552.00);
A double only has about 15.9 significant decimal digits. It doesn't fit. There was a precision loss at compile time when the floating-point literal was converted.
The issue is in the output formatting, specifically how doubles are converted to strings by default. Each double number has an exact value, but it is also the result of string to double conversion for a range of decimal fractions. In this case, the exact value of the double is -1350825904190559999913623552, but the range is [-1350825904190560137352577024,-1350825904190559862474670080].
The Double toString conversion picks the number from that range with the fewest significant digits, -1.35082590419056E27. That string does convert back to the original value.
If you really want to see the exact value, not just enough digits to uniquely identify the double, your current BigDecimal approach works well.
Here is the program I used to calculate the numbers in this answer:
import java.math.BigDecimal;
public class Test {
public static void main(String args[]) {
double value = -1350825904190559999913623552.00;
/* Get an exact printout of the double by conversion to BigDecimal
* followed by BigDecimal output. Both those operations are exact.
*/
BigDecimal bdValue = new BigDecimal(value);
System.out.println("Exact value: " + bdValue);
/* Determine whether the range is open or closed. The half way
* points round to even, so they are included in the range for a number
* with an even significand, but not for one with an odd significand.
*/
boolean isEven = (Double.doubleToLongBits(value) & 1) == 0;
/* Find the lower bound of the range, by taking the mean, in
* BigDecimal arithmetic for exactness, of the value and the next
* exactly representable value in the negative infinity direction.
*/
BigDecimal nextDown = new BigDecimal(Math.nextAfter(value,
Double.NEGATIVE_INFINITY));
BigDecimal lowerBound = bdValue.add(nextDown).divide(BigDecimal.valueOf(2));
/* Similarly, find the upper bound of the range by going in the
* positive infinity direction.
*/
BigDecimal nextUp = new BigDecimal(Math.nextAfter(value,
Double.POSITIVE_INFINITY));
BigDecimal upperBound = bdValue.add(nextUp).divide(BigDecimal.valueOf(2));
/* Output the range, with [] if closed, () if open.*/
System.out.println("Range: " + (isEven ? "[" : "(") + lowerBound + ","
+ upperBound + (isEven ? "]" : ")"));
/* Output the result of applying Double's toString to the value.*/
String valueString = Double.toString(value);
System.out.println("toString result: " + valueString);
/* And use BigDecimal as above to print the exact value of the result
* of converting the toString result back again.
*/
System.out.println("exact value of toString result as double: "
+ new BigDecimal(Double.parseDouble(valueString)));
}
}
Output:
Exact value: -1350825904190559999913623552
Range: [-1350825904190560137352577024,-1350825904190559862474670080]
toString result: -1.35082590419056E27
exact value of toString result as double: -1350825904190559999913623552
You cannot represent 1350825904190559999913623552.00 accurately with a Double. If you would like to know why, explore this article.
Should you want to represent the value, I would advise using the code you have used in your question: new BigDecimal( value ), where value is actually a String representation.

How 0X00020000 get converted to 131072?

I am performing some bit wise operations(& and |) on hexadecimal numbers.
Integer number1 = 0X00020000;
Integer number2 = 0X00000001;
System.out.println(number1);
System.out.println(number1 | number2);
System.out.println(number1 + number2);
Output :
131072
131073
131073
0X00020000 automatically got converted to 131072.
I am getting right answer but I am curious to know how & WHY Java converts hexadecimal number to decimal number.
I know how to convert hexadecimal number to decimal number.
0X00020000 will be converted to decimal as follows,
(2 X 16^4) + (0 X 16^3) + (0 X 16^2) + (0 X 16^1) + (0 X 16^0)
= (2 X 65536) + 0 + 0 + 0 + 0
= 131072
The right way to look at this is not as something being converted, but rather the way something is being displayed.
There is only one value stored for the variable, no matter how to write it in your source code. Write it anyway you like; once it is stored in memory, Java neither knows or cares what it looked like in your source code.
System.out.println renders the integer value, as a string, using decimal by default. Try System.out.printf("%x\n", number1); to see it rendered in hex.
Just keep in mind there is a big difference between the integer value and its string representation. If there is anything "converted" here, it's a conversion from an string (in your source) to an integer value (represented in memory) to a string (written to standard output). The last step uses decimal numerals by default.

Android multiplying double with int [java]

I have a double value a = 0.00059
and an Integer value which gets incremented and multiplied with the double value (say b = 1)
when I set the answer to the textview
//for b = 1
view.setText(((double)(a*b)));
the answer I get is " 5.9E-4 " however it should be 0.00059.
am I multiplying the values correctly.?
In addition to the other answers provided, you can use a formatter:
NumberFormat formatter = new DecimalFormat("#.#####");
view.setText(formatter.format(a*b));
You are multiplying them correctly. The values 5.9E-4 and 0.00059 are equivalent, mathematically and programmatically. The representation 5.9E-4 is like scientific notation, i.e. 5.9x10^(-4) is equivalent to 0.00059.
You get the same value as you want to get, but formatted in a scientific notation. What you need to do is to explicitly convert it to String:
view.setText(String.format("%f", a*b));
And you could eventually specify the number of decimal places to print after the decimal separator in this way:
// displays two digits after the decimal separator
view.setText(String.format("%.2f", a*b));

Rounding to different decimal places using a GUI output box

I know this is a simple task, but I think I'm just having trouble with the formatting. I can get the GUI input box to ask for a number with 8 decimal places, but for the output box I can't figure out how to round the number the user input to 5 and 3 decimal places. This is what I'm stuck on:
import javax.swing.JOptionPane;
public class Ch3Assignment6 {
public static void main(String[] args) {
String Decimal_Eight = JOptionPane.showInputDialog ("Enter a decimal number with eight decimal places");
JOptionPane.showMessageDialog (null,
"The number you entered is: " + Decimal_Eight + "\n" +
"The number rounded to 5 decimal places is: " + String.format("%.5f", Decimal_Eight) + "\n" +
"The number rounded to 3 decimal places is: " + String.format("%.3f", Decimal_Eight));
}
}
It shows an error message/no output message box but i don't know how i'm entering the rounding part incorrectly. Thanks!
The String format you applied works on floating points, not on Strings. Try to parse it to a double first:
String input = JOptionPane.showInputDialog("Enter a decimal number with eight decimal places");
double Decimal_Eight = Double.parseDouble(input);
In addition to #MartijinCourteaux answer (+1), you can also use the NumberFormat class directly to make adjustments to how a number is formatted...
double value = 123.4567890123456789;
NumberFormat nf = NumberFormat.getNumberInstance();
nf.setMaximumFractionDigits(5);
System.out.println(nf.format(value));
nf.setMaximumFractionDigits(3);
System.out.println(nf.format(value));
Which outputs...
123.45679
123.457
Now, obviously, I've started with a double, if you have String, you would only need to following #MartijnCourteaux answer to parse the value to a double...

Why does Java BigDecimal return 1E+1?

Why does this code sometimes return 1E+1 whilst for other inputs (e.g. 17) the output is not printed in scientific notation?
BigDecimal bigDecimal = BigDecimal.valueOf(doubleValue).multiply(BigDecimal.valueOf(100d)).stripTrailingZeros();
System.out.println("value: " + bigDecimal);
use bigDecimal.toPlainString():
BigDecimal bigDecimal = BigDecimal.valueOf(100000.0)
.multiply(BigDecimal.valueOf(100d))
.stripTrailingZeros();
System.out.println("plain : " + bigDecimal.toPlainString());
System.out.println("scientific : " + bigDecimal.toEngineeringString());
outputs:
plain : 10000000
scientific : 10E+6
It's the implicit .toString() conversion that is happening when you pass the result into System.out.println().
The exact rationale for the behaviour of BigDecimal.toString() is explained in the API doc in great (and near incomprehensible) detail.
To get a consistent (and locale-sensitive) textual representation, you should use DecimalFormat.
It's basically because you don't have enough significant digits. If you multiply something that only has 1 significant digit with 100, then you get something with only 1 significant digit. If it shows "10" then that basically says that it has 2 significant digits. The way to show it only has 1 significant digit is to show "1 x 10^1".
The following two decimals have the same value (10), but different "scales" (where they start counting significant digits; the top has 2 sig figs, the bottom has 1):
System.out.println(new BigDecimal(BigInteger.TEN, 0)); // prints 10
System.out.println(new BigDecimal(BigInteger.ONE, -1)); // prints 1E+1

Categories