Formatting Double to print without scientific notation using DecimalFormat - java

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.
...

Related

How do i convert a double into an int when converting minutes to seconds in java

Trying to convert minutes to seconds with the starting data being a double,
so for example 33.51 seconds (33 mins 51 seconds)
How to convert that to an int when converting to seconeds only
My code for now would accept it if there was no double point to begin with, so i only used ints only
and it seems to work, but when i have that double in there it doesnt, is therre a better approach to it .
Heres my code for now
public class Runner {
//fields setting up the variables
String MembershipID;
String name;
int time ;
//constructor1 filling in the details
public Runner(String Mem, String na, int ti) {
MembershipID = Mem;
name = na; //This
time = ti;
}
public String getMembershipID() {
return MembershipID;
}
public String getName() {
return name;
} //setting it up for the main method from the constructor fields above
public int getTime() {
int mins = time *60;
return mins;
}
public static void main(String[] args) { //initializing the membership name and time,
Runner a = new Runner("RF23", "George Formsby", 33); //Creating a new instance of
Runner, filling out the details from the sample data provided.
Runner b = new Runner("RG89", "Neil Innes", 32);
Runner c = new Runner("ST200", "Sandy Denny", 30); // With mem and na being
represented from my constructor
System.out.println("MembershipID is: " + a.getMembershipID());
System.out.println("Name is: " + a.getName());
System.out.println("Time in seconds: " + a.getTime());
System.out.println("MembershipID is: " + b.getMembershipID());
System.out.println("Name is: " + b.getName());
System.out.println("Time is: " + b.getTime());
System.out.println("MembershipID is: " + c.getMembershipID());
System.out.println("Name is: " + c.getName());
System.out.println("Time is: " + c.getTime());
}
}
Seems like simple enough math.
Convert minutes + seconds to just seconds
int minutes = ...;
int seconds = ...;
int totalSeconds = minutes * 60 + seconds;
Convert minutes (as a double) to seconds
double time = 12.33;
int seconds = (int) (0.5 + time * 60);
Explanation: when rounding to an int, java lops off the decimal parts. We want to round to the nearest second, which can be trivially accomplished (for positive numbers!) by adding 0.5 to it and then casting to int.
NB: See note below!
Convert seconds (as int) to minutes (as double)
int totalSeconds = ....;
double time = time / 60.0;
Explanation: In java, The syntactic construct x / y is considered to be 'integer division' if x and y are both integral data types (byte, short, int, long, or char). It is considered 'floating point division' if either x or y is a floating point type (float or double). integer division will lop off the decimal digits (so, it rounds down for positive results, and rounds up for negative results). Thus, 90 / 2, for example, resolves to 1. Not 1.5. On the other hand, 90 / 2.0 resolves to 1.5, because at least one of the two numbers is a double (2.0 is a double constant, 2 is an int constant). Hence why we divide by 60.0 and not 60.
NB: Important thing to think about: PRECISION.
Computers aren't magical, and double is precisely defined as consisting of exactly 64 bits.
You can't store one of an infinite sequence of options in a finite storage space, so, computers cannot store numbers perfectly. A 64-bit storage space has the ability to give you at most 2^64 different 'options'. If the storage space is storing a number, that means there are at most 2^64 numbers it could possibly store there, and all other numbers therefore simply cannot be represented by it. Somebody needs to go out and define which numbers are 'blessed' - capable of being stored. Then someone needs to define what happens if you attempt to store a non-blessed number in them.
For the integer data types, this is easy: int (32-bit) can store 2^32 numbers. Which numbers are blessed? Simply -2^31 to +2^31 -1. When you attempt to store something above or below it, the numbers just loop around:
int reallyLarge = Integer.MAX_VALUE; // this is 2^31-1.
int surelyThisIsEvenLarger = reallyLarge + 1;
Actually, surelyThisIsEvenLarger is negative number instead. It looped around.
For double and float it is way more complicated. Even between just 0 and 1 there are infinite numbers! The blessed numbers are chosen by more or less throwing darts at the numberline, focusing about half the darts close to 1.0, with fewer and fewer darts hitting the number line as you move away from 1.0. Eventually, at around 2^52, the 'distance' between any 2 darts is higher than 1.0, even.
It's a bit like how we humans do it: We cannot 'represent' 1 divided by 3, at all. 0.333333.... it never ends.
To make matters worse, computers count in binary and not decimal. So, where we humans can e.g. do '1 divided by 10' (that's 0.1, so it is a blessed number in the system of 'a human writes it down in decimal on a bit of paper that has room for about 10 digits'), computers cannot do that, either.
Thus, most of the 'take these number of seconds and turn them into a double' values, are not actually blessed, so it is important to realize what happens when you try to make a double that isn't blessed: The computer will round it off to the closest blessed number. You can't ask for the error (the amount it rounded by), or ask it not to do this; not with double, anyway.
If you do enough back-and-forth math on them, those errors compound and eventually will be flat out wrong. That's one of the many reasons why you should most definitely never, ever use double to store money values. For race monitoring you're running into a similar situation here, best not to use them. Better to pick an atomic unit and store in those. For example, why not store in millis? The current record for fastest mile is 3:43.13. In 'millis', that becomes long fastestMile = 223130; - no need to involve those nasty doubles with their bizarro rounding behaviours.

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 round a large decimal value in 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

How to format a number in Java and also keep the decimals?

I'm trying to show milliseconds as seconds while also keeping the decimals e.g. I have 1234 milliseconds and I want to show this as 1.234 seconds.
Decimal duration = 1234;
NumberFormat formatter = new DecimalFormat("#0.000");
String durationStr = formatter.format(duration / 1000);
Any suggestions how I could do this?
It sounds like you should be using BigDecimal - create a BigDecimal from the long, and then scale it by 3 places:
BigDecimal bd = new BigDecimal(duration).scaleByPowerOfTen(-3);
String durationStr = formatter.format(bd);
By using BigDecimal instead of double, you know that you'll still have exactly the value you're really considering, rather than simply "the nearest approximation that double can hold".
You may well also want:
formatter.setMinimumFractionDigits(3);
formatter.setMaximumFractionDigits(3);
... to ensure that you always get exactly 5 digits. (Assuming you want 1 second to be "1.000" for example.)
Use a double to divide the number 1000d.
String durationStr = formatter.format(duration / 1000d);

Format a double to omit unnecessary ".0" and never round off [duplicate]

This question already has answers here:
How to nicely format floating numbers to string without unnecessary decimal 0's
(29 answers)
Closed 9 years ago.
I know how to format a double to keep only the available decimal places (DP), up to a certain number of DPs. This example keeps up to 4 DPs.
double d = 1.0;
DecimalFormat df = new DecimalFormat("#.####");
System.out.print(df.format(d)); // returns "1"
double d = 1.23;
DecimalFormat df = new DecimalFormat("#.####");
System.out.print(df.format(d)); // returns "1.23"
double d = 1.2345678;
DecimalFormat df = new DecimalFormat("#.####");
System.out.print(df.format(d)); // returns "1.2346", rounding off: bad!
Now I want whole numbers e.g. 1.0 to return "1" without the unnecessary .0, and the # format character does provide that functionality. But how do I make sure that the number never gets rounded off? Is there any other way other than an arbitrarily long chain of # such as "#.###########################################"?
Or should I just use the default conversion of double to string, and truncate the ".0" if it appears at the end:
String s = "" + d;
if(s.substring(s.length()-2).equals(".0")) {
s=s.substring(0, s.length()-2);
}
Both ways seems terribly clumsy.
I use the following
double d =
String s = (long) d == d ? "" + (long) d : "" + d;
if you need Double instead for double. (Personally I would avoid using the wrapper if you can)
Double d =
String s = d.longValue() == d ? "" + d.longValue() : "" + d;
By default the rounding mode is Up for DecimalFormat. You can set different rounding format as per your requiremnet using setRoundingMode() method
Sample Code to set Rounding mode
double d = 1.2345678;
DecimalFormat df = new DecimalFormat("#.####");
df.setRoundingMode(RoundingMode.DOWN);
System.out.print(df.format(d));//result 1.2345 because of rounding down
You could use Double.doubleToLongBits() and extract the exponent and mantissa. From api doc:
Bit 63 (the bit that is selected by the mask 0x8000000000000000L)
represents the sign of the floating-point number. Bits 62-52 (the bits
that are selected by the mask 0x7ff0000000000000L) represent the
exponent. Bits 51-0 (the bits that are selected by the mask
0x000fffffffffffffL) represent the significand (sometimes called the
mantissa) of the floating-point number.
The exponent can be used to determine the number of significant digits, but you will always have to round at some point as some binary doulbe values will be endless in decimal.
For a binary representation you could simply use the mantissa and put the decimal dot at the right position (padding with zeros if necessary.

Categories

Resources