How to round a large decimal value in Java? - java

I'm trying in the following code to round a decimal number based on the decimal format that I set at the beginning:
DecimalFormat df = new DecimalFormat("#.000");
df.setRoundingMode(RoundingMode.FLOOR);
double a = Double.parseDouble(df.format(43.473684210526315));
double b = Math.pow(a, 10);
double c = Double.parseDouble(df.format(b));
System.out.println(a + " ** " + b + " ** " + c);
The result that I got is:
43.473 ** 2.4109939006965688E16 ** 2.4109939006965688E16
As you see, the value of a is formatted properly. While the other values are not. After spending a long time trying to understand what is going on, I found out the value 2.4109939006965688E16 can not be formatted. I tested the same value after removing E16 and it worked.
My question is how can I round such a large decimal so that it works as the a?

It's pure luck that a works. If the initial value was 43.4701 you would be seeing 43.47. And in some cases you would see more than 3 decimal places due to the inaccuracy of double.
You only use the DecimalFormatter to create a String briefly before turning back into a double again. You want to keep and use that string.
DecimalFormat df = new DecimalFormat("#.000");
double b = Math.pow(43.473684210526315, 10);
String bFormatted = df.format(b);
System.out.println(bFormatted);
Gives you your desired output 24115485538109308.000

For your "big" decimals you can use the following format:
DecimalFormat df2 = new DecimalFormat("#.000E0");

I see several issues here:
You do not specify a locale for your DecimalFormat. So the combination of DecimalFormat.format and Double.parseDouble is error prone, as e.g. with german locale, DecimalFormat.format will use a comma as decimal separator, which is unsupported by Double.parseDouble, and will throw a NumberFormatException. You should use both format and parse of your DecimalFormat.
Also, you use your double values directly for System.out.println. Why not concatenating the results of DecimalFormat.format, to achieve exactly what you want? E.g. System.out.println(df.format(a) + " ** " + df.format(b) + " ** " + df.format(c));
If you want to do real mathematical rounding (e.g. needing the double value for additional calculations), use Math.round. You can multiply and divide by 10, 100, 1000 etc. to achieve the desired precision. Note that, with the double data type, you can't do exact rounding.

For large numbers, you'll need to use BigInteger or BigDecimal
DecimalFormat df = new DecimalFormat("#.000");
df.setRoundingMode(RoundingMode.FLOOR);
BigDecimal a = new BigDecimal(43.473);
BigDecimal b = a.pow(10);
System.out.println(df.format(a) + " ** " + b.doubleValue() + " ** " + df.format(b));
This results in the following output
43.472 ** 2.4109939006965688E16 ** 24109939006965686.655

Related

Fomat float and double into scientific notation in specific format given below

I have already asked a question here and it was marked duplicate although it didn't solve my question and I tried to reopen but it got in vain.
Forcing BigDecimals to use scientific notation
format decimal number to specific exponential format in java
So before marking my question duplicate please see that it is related to my problem.
Question :
Given a float or double number op into specific format
eg : 13.33 = 0.1333 E+02
0.00023 = 0.23 E-03
The non zero digit of the number starts from just after the decimal.
How to use Decimal Format to achieve this output.
As you want the non zero digit of the number starts from just after the decimal.
you can do
BigDecimal x = new BigDecimal("13.33");
DecimalFormat frmt = new DecimalFormat(".00E00");
String formatted = "0" + frmt.format(x.doubleValue());
System.out.println("result: " + formatted);
results
13.33
result: 0.13E02
0.00023
result: 0.23E-03

How to keep trailing 0s while converting a string to BigDecimal without exponent

I need to convert this two strings "0.0000000" and "0.0000008" to BigDecimal without losing trailing 0s.
I tried String.format
String format = "%." + precision + "f";
BigDecimal divisor = new BigDecimal(Math.pow(10, precision));
formattedAmount = amountValue.divide(divisor).setScale(precision, BigDecimal.ROUND_DOWN);
String str= String.format(format, formattedAmount);
System.out.println(str);
BigDecimal strb = new BigDecimal(str);
System.out.println(strb);
I am expecting these two strings "0.0000000" and "0.0000008" to be returned as BigDecimal without exponent like 0.0000000 and 0.0000008. But i am getting 0E-7 and 8E-7.
I need 0.0000000 and 0.0000008 as BigDecimal not as String.
When you're trying to print BigDecimal in System.out.println(strb) the toString() method on strb object will be invoked. So the output in console is also string.
See more The connection between 'System.out.println()' and 'toString()' in Java
So the only way to tell the java not to use exponent here System.out.println(strb) is to change toString() method. You can't change it directly, but you could create your own class that extends BigDecimal and override toString() method you need. Obviously it doesn't make sense.. just use toPlainString() method. From java doc:
return a string representation of this {#code BigDecimal} without an exponent field.
First of all, no matter how you output the value of a BigDecimal, it stays the same in an instance of it (as long as you don't do calculations on it).
However, the output may differ a lot.
Here are the methods BigDecimal is offering
public static void main(String[] args) {
String zero = "0.0000000";
String zeroDotsEight = "0.0000008";
BigDecimal z = new BigDecimal(zero);
BigDecimal zE = new BigDecimal(zeroDotsEight);
// play around with the precision and have a look at the output
int precision = 7;
System.out.println("String:\t\t\t\t\t" + zero);
System.out.println("BigDecimal:\t\t\t\t" + z.setScale(precision, BigDecimal.ROUND_DOWN));
System.out.println("BigDecimal.toString():\t\t\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).toString());
System.out.println("BigDecimal.toPlainString():\t\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).toPlainString());
System.out.println("BigDecimal.toEngineeringString():\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).toEngineeringString());
System.out.println("BigDecimal.doubleValue():\t\t"
+ z.setScale(precision, BigDecimal.ROUND_DOWN).doubleValue());
System.out.println("————————————————————————————————————————————————————————————————");
System.out.println("String:\t\t\t\t\t" + zeroDotsEight);
System.out.println("BigDecimal:\t\t\t\t" + zE.setScale(precision, BigDecimal.ROUND_DOWN));
System.out.println("BigDecimal.toString():\t\t\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).toString());
System.out.println("BigDecimal.toPlainString():\t\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).toPlainString());
System.out.println("BigDecimal.toEngineeringString():\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).toEngineeringString());
System.out.println("BigDecimal.doubleValue():\t\t"
+ zE.setScale(precision, BigDecimal.ROUND_DOWN).doubleValue());
}
and this is the output on my system
String: 0.0000000
BigDecimal: 0E-7
BigDecimal.toString(): 0E-7
BigDecimal.toPlainString(): 0.0000000
BigDecimal.toEngineeringString(): 0.0E-6
BigDecimal.doubleValue(): 0.0
————————————————————————————————————————————————————————————————
String: 0.0000008
BigDecimal: 8E-7
BigDecimal.toString(): 8E-7
BigDecimal.toPlainString(): 0.0000008
BigDecimal.toEngineeringString(): 800E-9
BigDecimal.doubleValue(): 8.0E-7
As you can see, #Ruslan is right. You have to create your own class or use toPlainString() in order to keep the format and not lose any trailing zeros (which should only matter for the human eye, but I even doubt they really do).
In addition, you were right as well when you commented that doubleValue() is not the way to go.
As stated in the only code comment, play around with the precision, maybe calculate some values before printing and/or storing the results in variables.

How to format a number based on locale, while retaining all decimal points?

I'm trying to use DecimalFormat to convert the Decimal separator of a double value while retaining all decimals of the original number. DecimalFormatter accepts a pattern in the format: "0.##", for example. Since I have to use numbers with varying decimals, this will not work, however, since the number of decimals always needs to be specified in the pattern.
I am looking for a way to get around this.
I've tried String.format. DecimaFormatter and NumberFormatter
What I would like ideally is something along the lines of:
private static final ThreadLocal< DecimalFormat > formatter = new ThreadLocal< DecimalFormat >()
{
#Override
protected DecimalFormat initialValue()
{
// n should be any number of decimals without having to specify them.
return new DecimalFormat("0.0#n");
}
};
Some examples:
DecimalFormat df = new DecimalFormat("0.0##");
System.out.println(df.format(2.456))
System.out.println(df.format(2.1));
Result:
2,456 -> Good
2,100 -> Not good
I want to set a pattern/regex which will work for doubles of any number of digits after the decimal separator like:
2,456 -> Good
2,1 -> Good
3,3453456345234 -> Good
Numbers in Java (and just numbers, generally) don't have a set number of decimal places. 1.1, 1.10, and 1.100 are all exactly the same number.
You could find out how many places default formatting would use, e.g.:
String str = num.toString();
int decimal = str.indexOf('.');
int places = decimal <= 0 ? 0 : str.length - decimal;
...and then specify that many places when using the formatter.
So you have
double[] floats = { 3.20, 4.500, 6.34, 1.0000 };
BigDecimal[] fixeds = {
new BigDecimal("3.20"),
new BigDecimal("4.500"),
new BigDecimal("6.34"),
new BigDecimal("1.0000")
};
and want to format them as such, but localized.
The good news is that fixed point numbers with BigDecimal maintain a precision (using the string constructor). The bad news is that floating point is alwaya an approximation, by a sum of (negative) powers of 2. So 3.20 might actually be 3.19999987 or 3.20000043.
They have no fixed decimals. Even without approximation error 3.2 == 3.20 == 3.200.
So convert to BigDecimal (quite ugly), and get rid of the approximation errors: 1000*3.2 != 3200.0.
BigDecimal value = new BigDecimal("5.1000");
NumberFormat df = NumberFormat.getInstance(Locale.KOREA);
df.setMinimumFractionDigits(value.getScale()); // 4
String s = df.format(value);
DecimalFormat has 16 setter methods for controlling the output. The pattern string you specify in the constructor is just a convenient way to set most of those values.
To control the locale of the output, you can specify the DecimalFormatSymbols, either in the constructor or in a setter method.
Since you want unlimited decimal places, and it seems you want at least one decimal place, you need to call a setter method, because specifying unlimited is not possible with the pattern string.
Example
private static void test(Locale locale) {
DecimalFormat fmt = new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(locale));
//fmt.setMaximumIntegerDigits(Integer.MAX_VALUE); // already set by pattern
//fmt.setMinimumIntegerDigits(1); // already set by pattern
//fmt.setMinimumFractionDigits(1); // already set by pattern
fmt.setMaximumFractionDigits(Integer.MAX_VALUE);
//fmt.setGroupingUsed(false); // already set by pattern
System.out.println(locale.toLanguageTag() + " (" + locale.getDisplayLanguage(Locale.US) +
" - " + locale.getDisplayCountry(Locale.US) + ")");
System.out.println(" " + fmt.format(123456789));
System.out.println(" " + fmt.format(2.456));
System.out.println(" " + fmt.format(2.1));
System.out.println(" " + fmt.format(3.3453456345234));
}
public static void main(String[] args) {
test(Locale.US);
test(Locale.GERMANY);
test(Locale.forLanguageTag("ar-EG"));
}
Output (OpenJDK 11.0.1)
en-US (English - United States)
123456789.0
2.456
2.1
3.3453456345234
de-DE (German - Germany)
123456789,0
2,456
2,1
3,3453456345234
ar-EG (Arabic - Egypt)
١٢٣٤٥٦٧٨٩٫٠
٢٫٤٥٦
٢٫١
٣٫٣٤٥٣٤٥٦٣٤٥٢٣٤

Round off Double to 2 decimal without changing the datatype [duplicate]

This question already has answers here:
How to round a number to n decimal places in Java
(39 answers)
Closed 5 years ago.
double value1 = 1000
double value2 = 2563.59
System.out.println("value1 ="+String.format("%.2f",value1));
System.out.println("value2 ="String.format("%.2f",value2));
Output
value1 = 1000.00
value2 = 2563.60
This returns 1000.00 and 2563.60 but the type of output changes to string.
I want 1000.00 and 2563.60 as the output without changing the datatype.
Can anyone help me?
The dtatype must be double itself
Note that double types store values, not strings. There are gonna be some cases where rounding it to 2 decimal places changes nothing. For example, the value 1. If you do a String.format on 1, it will become 1.00. However, 1 and 1.00 are the same value, so turning 1 to 2 decimal places makes it 1 again. double thinks that 1 and 1.00 are the same value (I mean, they really are, it's just formatted differently).
Also note that double values are inaccurate. It might appear as though it is 1.23 but in reality it might be something like 1.2300000000000001.
To correct a double to 2 d.p., all you have to do is to convert the formatted string back to a double:
String str = String.format("%.2f",value1));
double result = Double.parseDouble(str);
Don't use String.format(..) if you don't want it to change to a string.
Please refer to Decimal Format
// 2 places of decimal
DecimalFormat formatter = new DecimalFormat( "#.00" );
System.out.println(formatter.format(1.23)); // 1.23
System.out.println(formatter.format(1)); // 1.00
System.out.println(formatter.format(1.234)); // 1.23
why you are using string.format in first place?
DecimalFormat f = new DecimalFormat("##.00");
System.out.println(f.format(value1));
you need to import java.text.DecimalFormat; for this
Using BigDecimal.setScale() and using rounding mode ceiling
double value1 = 1000;
double value2 = 2563.59;
System.out.println("Before");
System.out.println("value1 = " + value1);
System.out.println("value2 = " + value2);
value1 = BigDecimal.valueOf(value1).setScale(1, RoundingMode.CEILING).doubleValue();
value2 = BigDecimal.valueOf(value2).setScale(1, RoundingMode.CEILING).doubleValue();
System.out.println("After");
System.out.println("value1 = " + value1);
System.out.println("value2 = " + value2);
In anything that uses limited-precision, binary floating-point numbers -- which is the default for Java and most programming languages -- the concept "number of decimal places" only ever applies to display. The internal binary representation has no concept of "decimal places" because the data is not decimal.
Consequently, the number of decimal places applies only to how data is displayed, and it is irrelevant what data types may be used to do the display.
When questions arise about rounding decimal numbers, it's often because the programmer has not fully understood how float-point math works.

Formatting Double to print without scientific notation using DecimalFormat

I have been reading timestamp values from sensor readings, but since they are provided in nanoseconds, I thought I would cast them to double and make the conversion. The resulting number is a 17 digit value, plus the separator.
Trying to print it directly results in scientific notation, which I don't want, so I use a DecimalFormat class to output it to an expected value of 4 decimal places. The problem is, even though the debugger shows a number of 17 decimal digits, even after the 'doubleValue()' call, the output string shows me a number of 15 digits.
Code:
...
Double timestamp = (new Date().getTime()) + // Example: 1.3552299670232847E12
((event.timestamp - System.nanoTime()) / 1000000D);
DecimalFormat dfmt = new DecimalFormat("#.####");
switch(event.sensor.getType()){
case Sensor.TYPE_LINEAR_ACCELERATION:
case Sensor.TYPE_ACCELEROMETER:
accel = event.values.clone();
String line = "A" + LOGSEPARATOR +
dfmt.format(timestamp.doubleValue()) + // Prints: 1355229967023.28
...
I thought this might be an android precision problem, but the debugger has the formatter showing the wrong precision as well. I have tested this in a local java program and both calls have the same amount of digits.
Is this a DecimalFormat bug/limitation? Or am I doing something wrong?
A double in Java has a mantissa of only 52 bit (counting the hidden 1 its 53 bit). This is equivalent to 15-16 decimal places (53*log10(2)). Every digit after this is kind of random and therefore it makes sense for the conversion function to cut the output after 15 decimal places.
Since you do not need the large number range that double provides, why not keep the value as long? This would give you 63 significant bits (64 -1 for the sign).
Was doing some research with String.format aswell with same results.
Double timestamp = 1.3552299670232847E12;
System.out.println("it was " + timestamp);
System.out.println("and now " + String.format("%.4f", timestamp));
And this is the output:
12-12 15:48:58.255: I/System.out(2989): it was 1.3552299670232847E12
12-12 15:48:58.255: I/System.out(2989): and now 1355229967023,2800
Maybe you're right and it's an Android precision problem as if you try it in Java, the output is correct: http://ideone.com/PBOiet
I'll keep googling...
There is indeed a difference between Java's and Android's DecimalFormat class, and they output different results, despite taking the exact same arguments.
This was enough for me to try Henry's approach, and now that I have I see that I have gained an extra 2 places of precision. I am also confident that the values are calculated accurately, as only sums and multiplications are involved.
This is the modified code I ended up using:
...
long javaTime = new Date().getTime();
long nanoTime = System.nanoTime();
long newtimestamp = javaTime * 1000000 + // Compute the timestamp
(event.timestamp - nanoTime); // in nanos first
String longStr = Long.valueOf(newtimestamp).toString();
String tsString = longStr.substring(0, longStr.length()-6) +// Format the output string
"." + longStr.substring(longStr.length()-6); // to have the comma in the
// correct space.
...

Categories

Resources