how to reduce a latitude to 4 decimal places - java

This is my second post on this subject. I have found dozens of posts on this subject and not a single solution that works. The location listener will provide you a latitude such as 3.734567E7. My goal is to reduce the accuracy of that latitude to four decimal places. I don't care if I truncate it or round it. the following code...
Double d = Double.parseDouble("3.734567E7");
NumberFormat fmtr = new DecimalFormat("###.####");
String f = fmtr.format(d);
Log.w(getClass().getName(), "result using DecimalFormat = " + f );
d = (double) Math.round(d * 10000.0) / 10000.0;
Log.w(getClass().getName(), "result using math round = " + d );
f = d.toString();
Log.w(getClass().getName(), "result string using math round = " + f );
produces three lines:
37345670
3.734567E7
3.734567E7
none of this processing has any affect on the number.
when I had to do this in C# on my windows phone app I did the following...
myExecution.latitude = Math.Round(newLatitude, 4); It took less than a minute of my time. I have over a weeks time trying to figure out how to do the same thing in java. the Java math.round, oddley enough, has no way to specify how many decimal places you desire to round to making it totally usless as far as I can understand.
I realize that scientific notation is only supposed to be a way of displaying values and does not have anything to do with how an amount is stored internally. This makes this problem all the more perplexing.
Can anyone modify the code above to make it round to 4 decimal places?
(but you can't change the 1st line of code. It must be specified in scientific notation. If you specify it in decimal notation it works wonderfully.
thanks, Gary

3.734567E7 means 3.734567 x 107 which is 37,345,670. So there are no decimals to round...
If you want 3.734600E7, you can use Math.round(d / 1000) * 1000).

One simple way:
System.out.println(String.format("%.4f", 1.23456789));
Output:
1.2346 (or 1,2346 depending on your locale)
Update: to show the rounding

Related

Matching Excel's floating point in Java

I have an .xlsx spreadsheet with a single number in the top-left cell of sheet 1.
The Excel UI displays:
-130.98999999999
This is visible in the formula bar, i.e. not affected by the number of decimal places the containing cell is set to show. It's the most accurate number Excel will display for this cell.
In the underlying XML, we have:
<v>-130.98999999999069</v>
When trying to read the workbook with Apache POI, it feeds the number from the XML through Double.valueOf and comes up with:
-130.9899999999907
Unfortunately, this is not the same number the user can see in Excel. Can anyone point me to an algorithm to obtain the same number the user sees in Excel?
My research so far suggests that the Excel 2007 file format uses a slightly non-standard version of IEE754 floating point, where the value space is different. I believe in Excel's floating point, this number falls the other side of the boundary for rounding and hence comes out as though rounded down instead of up.
I agree with jmcnamara's prior answer. This answer expands on it.
For each IEEE 754 64-bit binary floating point number, there is a range of decimal fractions that would round to it on input. Starting from -130.98999999999069, the closest representable value is -130.98999999999068677425384521484375. Under round to nearest with round half even rules, anything in the range [-130.9899999999907009851085604168474674224853515625, -130.9899999999906725633991300128400325775146484375] rounds to that value. (The range is closed because the binary representation of the central number is even. If it were odd, the range would be open). Both -130.98999999999069 and -130.9899999999907 are in range.
You do have the same floating point number as Excel.
You do have the same floating point number as was input to Excel. Unfortunately, further experiments suggest that Excel 2007 is only converting the most significant 15 digits of your input. I pasted -130.98999999999069 into an Excel cell. Not only was it displayed as -130.98999999999, arithmetic using it was consistent with the closest double to that value, -130.989999999990004653227515518665313720703125, rather than the original input.
To get the same effect as Excel you may need to use e.g. BigDecimal to truncate to 15 decimal digits, then convert to double.
Java's default string conversion for floating point values basically picks the decimal fraction with the fewest decimal places that would convert back to the original value. -130.9899999999907 has fewer decimal places than -130.98999999999069. Apparently, Excel is displaying fewer digits, but Apache POI is getting one of the representations of the same number as you have in Java.
Here is the program I used to obtain the numbers in this answer. Note that I am using BigDecimal only to obtain exact printouts of doubles, and to calculate the mid point between two consecutive doubles.
import java.math.BigDecimal;
class Test {
public static void main(String[] args) {
double d = -130.98999999999069;
BigDecimal dDec = new BigDecimal(d);
System.out.println("Printed as double: "+d);
BigDecimal down = new BigDecimal(Math.nextAfter(d, Double.NEGATIVE_INFINITY));
System.out.println("Next down: " + down);
System.out.println("Half down: " + down.add(dDec).divide(BigDecimal.valueOf(2)));
System.out.println("Original: " + dDec);
BigDecimal up = new BigDecimal(Math.nextAfter(d, Double.POSITIVE_INFINITY));
System.out.println("Half up: " + up.add(dDec).divide(BigDecimal.valueOf(2)));
System.out.println("Next up: " + up);
System.out.println("Original in hex: "+Long.toHexString(Double.doubleToLongBits(d)));
}
}
Here is its output:
Printed as double: -130.9899999999907
Next down: -130.989999999990715195963275618851184844970703125
Half down: -130.9899999999907009851085604168474674224853515625
Original: -130.98999999999068677425384521484375
Half up: -130.9899999999906725633991300128400325775146484375
Next up: -130.989999999990658352544414810836315155029296875
Original in hex: c0605fae147ae000
Unfortunately, this is not the same number the user can see in Excel. Can anyone point me to an algorithm to obtain the same number the user sees in Excel?
I don't think that it is using an algorithm here. Excel uses IEEE754 double internally and I'd guess that it is just using a printf style format when displaying the number:
$ python -c 'print "%.14g" % -130.98999999999069'
-130.98999999999
$ python -c 'print "%.14g" % -130.9899999999907'
-130.98999999999
You need to use BigDecimal for this (for not losing any precision).
E.g. read the value as a String, then construct a BigDecimal from it.
Here is an example where you don't lose any precision i.e. this
is the way to obtain exactly the same number which the user sees in Excel.
import java.math.BigDecimal;
public class Test020 {
public static void main(String[] args) {
BigDecimal d1 = new BigDecimal("-130.98999999999069");
System.out.println(d1.toString());
BigDecimal d2 = new BigDecimal("10.0");
System.out.println(d1.add(d2).toString());
System.out.println(d1.multiply(d2).toString());
}
}
As suggested by peter.petrov I would use BigDecimal for this. As mentioned it let's you import the data without loss and be always setting the scale to 15 you have the same behaviour as in Excel
I use this for calculating the same 15 digit display value.
private static final int EXCEL_MAX_DIGITS = 15;
/**
* Fix floating-point rounding errors.
*
* https://en.wikipedia.org/wiki/Numeric_precision_in_Microsoft_Excel
* https://support.microsoft.com/en-us/kb/214118
* https://support.microsoft.com/en-us/kb/269370
*/
private static double fixFloatingPointPrecision(double value) {
BigDecimal original = new BigDecimal(value);
BigDecimal fixed = new BigDecimal(original.unscaledValue(), original.precision())
.setScale(EXCEL_MAX_DIGITS, RoundingMode.HALF_UP);
int newScale = original.scale() - original.precision() + EXCEL_MAX_DIGITS;
return new BigDecimal(fixed.unscaledValue(), newScale).doubleValue();
}
This function should produce the same thing you see in the formula bar:
private static BigDecimal stringedDouble(Cell cell) {
BigDecimal result = new BigDecimal(String.valueOf(cell.getNumericCellValue())).stripTrailingZeros();
result = result.scale() < 0 ? result.setScale(0) : result;
return result;
}

Formatting Double to exactly 2 Decimal places IF required

I require a DecimalFormat or a better equivalent of representing a Double value (in Java) which could be:
25 or 25.5
I need for that to be represented as either a whole number (25) or to two decimal places if it has any (25.50). This is because i'm printing it out as money.
I have the following format already:
DecimalFormat decFormat = new DecimalFormat("##,###.##");
This works perfectly if the Double is a whole number; I get the output $25,000. Except if the value is 25,000.5; it prints $25,000.5 when I need it to be printed as $25,000.50. The problem is as stated in the docs:
# a digit, zero shows as absent
So essentially the last zero is dropped off since it is optional.
I cannot do:
DecimalFormat decFormat = new DecimalFormat("##,###.#0");
as that is not allowed.
How can I achieve this?
Note:
These questions are related but do not cover what I need specifically with the DecimalFormat. Most of the answers suggest using a BigDecimal or printf. Is this the best thing to do? I don't have to use DecimalFormat but prefer to since i've started on that path (lots of code everywhere already using it).
Best way to Format a Double value to 2 Decimal places
How do I round a double to two decimal places in Java?
Round a double to 2 decimal places
This is definitely a bit of a hack, but I don't know if the DecimalFormat syntax allows for anything better. This simply checks to see if the number is real, and formats based on the spec you asked for.
double number = 25000.5;
DecimalFormat df;
if(number%1==0)
df = new DecimalFormat("##,###");
else
df = new DecimalFormat("##,###.00");
System.out.println(df.format(number));
When you need to return Decimal Format value this works
import java.text.DecimalFormat;
/**
* #return The weight of this brick in kg.
*/
public double getWeight()
{
DecimalFormat df = new DecimalFormat("#.##");
double number = ( getVolume() * WEIGHT_PER_CM3 ) / 1000;
//System.out.println(df.format(number));
return Double.valueOf ( df.format( number ) );
}

How to get the numbers after the decimal point? (java) [duplicate]

This question already has answers here:
How do I get whole and fractional parts from double in JSP/Java?
(18 answers)
Closed 9 years ago.
double d = 4.321562;
Is there an easy way to extract the 0.321562 on it's own from d? I tried looking in the math class but no luck. If this can be done without converting to string or casting to anything else, even better.
Well, you can use:
double x = d - Math.floor(d);
Note that due to the way that binary floating point works, that won't give you exactly 0.321562, as the original value isn't exactly 4.321562. If you're really interested in exact digits, you should use BigDecimal instead.
Another way to get the fraction without using Math is to cast to a long.
double x = d - (long) d;
When you print a double the toString will perform a small amount of rounding so you don't see any rounding error. However, when you remove the integer part, the rounding is no longer enough and the rounding error becomes obvious.
The way around this is to do the rounding yourself or use BigDecimal which allows you to control the rounding.
double d = 4.321562;
System.out.println("Double value from toString " + d);
System.out.println("Exact representation " + new BigDecimal(d));
double x = d - (long) d;
System.out.println("Fraction from toString " + x);
System.out.println("Exact value of fraction " + new BigDecimal(x));
System.out.printf("Rounded to 6 places %.6f%n", x);
double x2 = Math.round(x * 1e9) / 1e9;
System.out.println("After rounding to 9 places toString " + x2);
System.out.println("After rounding to 9 places, exact value " + new BigDecimal(x2));
prints
Double value from toString 4.321562
Exact representation 4.321562000000000125510268844664096832275390625
Fraction from toString 0.3215620000000001
Exact value of fraction 0.321562000000000125510268844664096832275390625
Rounded to 6 places 0.321562
After rounding to 9 places toString 0.321562
After rounding to 9 places, exact value 0.32156200000000001448796638214844278991222381591796875
NOTE: double has limited precision and you can see representation issue creep in if you don't use appropriate rounding. This can happen in any calculation you use with double esp numbers which are not an exact sum of powers of 2.
Use modulo:
double d = 3.123 % 1;
assertEquals(0.123, d,0.000001);

Round a double to 2 decimal places [duplicate]

This question already has answers here:
How to round a number to n decimal places in Java
(39 answers)
Closed 8 years ago.
If the value is 200.3456, it should be formatted to 200.34.
If it is 200, then it should be 200.00.
Here's an utility that rounds (instead of truncating) a double to specified number of decimal places.
For example:
round(200.3456, 2); // returns 200.35
Original version; watch out with this
public static double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
long factor = (long) Math.pow(10, places);
value = value * factor;
long tmp = Math.round(value);
return (double) tmp / factor;
}
This breaks down badly in corner cases with either a very high number of decimal places (e.g. round(1000.0d, 17)) or large integer part (e.g. round(90080070060.1d, 9)). Thanks to Sloin for pointing this out.
I've been using the above to round "not-too-big" doubles to 2 or 3 decimal places happily for years (for example to clean up time in seconds for logging purposes: 27.987654321987 -> 27.99). But I guess it's best to avoid it, since more reliable ways are readily available, with cleaner code too.
So, use this instead
(Adapted from this answer by Louis Wasserman and this one by Sean Owen.)
public static double round(double value, int places) {
if (places < 0) throw new IllegalArgumentException();
BigDecimal bd = BigDecimal.valueOf(value);
bd = bd.setScale(places, RoundingMode.HALF_UP);
return bd.doubleValue();
}
Note that HALF_UP is the rounding mode "commonly taught at school". Peruse the RoundingMode documentation, if you suspect you need something else such as Bankers’ Rounding.
Of course, if you prefer, you can inline the above into a one-liner:
new BigDecimal(value).setScale(places, RoundingMode.HALF_UP).doubleValue()
And in every case
Always remember that floating point representations using float and double are inexact.
For example, consider these expressions:
999199.1231231235 == 999199.1231231236 // true
1.03 - 0.41 // 0.6200000000000001
For exactness, you want to use BigDecimal. And while at it, use the constructor that takes a String, never the one taking double. For instance, try executing this:
System.out.println(new BigDecimal(1.03).subtract(new BigDecimal(0.41)));
System.out.println(new BigDecimal("1.03").subtract(new BigDecimal("0.41")));
Some excellent further reading on the topic:
Item 48: "Avoid float and double if exact answers are required" in Effective Java (2nd ed) by Joshua Bloch
What Every Programmer Should Know About Floating-Point Arithmetic
If you wanted String formatting instead of (or in addition to) strictly rounding numbers, see the other answers.
Specifically, note that round(200, 0) returns 200.0. If you want to output "200.00", you should first round and then format the result for output (which is perfectly explained in Jesper's answer).
If you just want to print a double with two digits after the decimal point, use something like this:
double value = 200.3456;
System.out.printf("Value: %.2f", value);
If you want to have the result in a String instead of being printed to the console, use String.format() with the same arguments:
String result = String.format("%.2f", value);
Or use class DecimalFormat:
DecimalFormat df = new DecimalFormat("####0.00");
System.out.println("Value: " + df.format(value));
I think this is easier:
double time = 200.3456;
DecimalFormat df = new DecimalFormat("#.##");
time = Double.valueOf(df.format(time));
System.out.println(time); // 200.35
Note that this will actually do the rounding for you, not just formatting.
The easiest way, would be to do a trick like this;
double val = ....;
val = val*100;
val = Math.round(val);
val = val /100;
if val starts at 200.3456 then it goes to 20034.56 then it gets rounded to 20035 then we divide it to get 200.34.
if you wanted to always round down we could always truncate by casting to an int:
double val = ....;
val = val*100;
val = (double)((int) val);
val = val /100;
This technique will work for most cases because for very large doubles (positive or negative) it may overflow. but if you know that your values will be in an appropriate range then this should work for you.
Please use Apache commons math:
Precision.round(10.4567, 2)
function Double round2(Double val) {
return new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue();
}
Note the toString()!!!!
This is because BigDecimal converts the exact binary form of the double!!!
These are the various suggested methods and their fail cases.
// Always Good!
new BigDecimal(val.toString()).setScale(2,RoundingMode.HALF_UP).doubleValue()
Double val = 260.775d; //EXPECTED 260.78
260.77 - WRONG - new BigDecimal(val).setScale(2,RoundingMode.HALF_UP).doubleValue()
Double val = 260.775d; //EXPECTED 260.78
260.77 - TRY AGAIN - Math.round(val * 100.d) / 100.0d
Double val = 256.025d; //EXPECTED 256.03d
256.02 - OOPS - new DecimalFormat("0.00").format(val)
// By default use half even, works if you change mode to half_up
Double val = 256.025d; //EXPECTED 256.03d
256.02 - FAIL - (int)(val * 100 + 0.5) / 100.0;
double value= 200.3456;
DecimalFormat df = new DecimalFormat("0.00");
System.out.println(df.format(value));
If you really want the same double, but rounded in the way you want you can use BigDecimal, for example
new BigDecimal(myValue).setScale(2, RoundingMode.HALF_UP).doubleValue();
double d = 28786.079999999998;
String str = String.format("%1.2f", d);
d = Double.valueOf(str);
For two rounding digits. Very simple and you are basically updating the variable instead of just display purposes which DecimalFormat does.
x = Math.floor(x * 100) / 100;
Rounding a double is usually not what one wants. Instead, use String.format() to represent it in the desired format.
In your question, it seems that you want to avoid rounding the numbers as well? I think .format() will round the numbers using half-up, afaik?
so if you want to round, 200.3456 should be 200.35 for a precision of 2. but in your case, if you just want the first 2 and then discard the rest?
You could multiply it by 100 and then cast to an int (or taking the floor of the number), before dividing by 100 again.
200.3456 * 100 = 20034.56;
(int) 20034.56 = 20034;
20034/100.0 = 200.34;
You might have issues with really really big numbers close to the boundary though. In which case converting to a string and substring'ing it would work just as easily.
value = (int)(value * 100 + 0.5) / 100.0;

Java using Mod floats

I am working on an exercise in Java. I am supposed to use / and % to extract digits from a number. The number would be something like 1349.9431. The output would be something like:
1349.9431
1349.943
1349.94
1349.9
I know this is a strange way to do but the lab exercise requires it.
Let's think about what you know. Let say you have the number 12345. What's the result of dividing 12345 by 10? What's the result of taking 12345 mod 10?
Now think about 0.12345. What's the result of multiplying that by 10? What's the result of that mod 10?
The key is in those answers.
if x is your number you should be able to do something like x - x%0.1 to get the 1349.9, then x - x%.0.01 to get 1349.94 and so on. I'm not sure though, doing mod on floats is kind of unusual to begin with, but I think it should work that way.
x - x%10 would definetly get you 1340 and x - x%100 = 1300 for sure.
Well the work will be done in background anyway, so why even bother, just print it.
float dv = 1349.9431f;
System.out.printf("%8.3f %8.2f %8.1f", dv, dv, dv);
Alternatively this could be archived with:
float dv = 1349.9431f;
System.out.println(String.format("%8.3f %8.2f %8.1f", dv, dv, dv));
This is a homework question so doing something the way you would actually do in the real world (i.e. using the format method of String as Margus did) isn't allowed. I can see three constraints on any answer given what is contained in your question (if these aren't actually constraints you need to reword your question!)
Must accept a float as an input (and, if possible, use floats exclusively)
Must use the remainder (%) and division (/) operator
Input float must be able to have four digits before and after the decimal point and still give the correct answer.
Constraint 1. is a total pain because you're going to hit your head on floating point precision quite easily if you have to use a number with four digits before and after the decimal point.
float inputNumber = 1234.5678f;
System.out.println(inputNumber % 0.1);
prints "0.06774902343743147"
casting the input float to a double casuses more headaches:
float one = 1234.5678f;
double two = (double) one;
prints "1234.5677490234375" (note: rounding off the answer will get you 1234.5677, which != 1234.5678)
To be honest, this had me really stumped, I spent way too much time trying to figure out how to get around the precision issue. I couldn't find a way to make this program work for 1234.5678f, but it does work for the asker's value of 1349.9431f.
float input = 1349.9431f;
float inputCopy = input;
int numberOfDecimalPoints = 0;
while(inputCopy != (int) inputCopy)
{
inputCopy = inputCopy * 10;
numberOfDecimalPoints++;
}
double inputDouble = (double) input;
double test = inputDouble * Math.pow(10, numberOfDecimalPoints);
long inputLong = Math.round(test);
System.out.println(input);
for(int divisor = 10; divisor < Math.pow(10, numberOfDecimalPoints); divisor = divisor * 10)
{
long printMe = inputLong - (inputLong % divisor);
System.out.println(printMe / Math.pow(10, numberOfDecimalPoints));
}
Of my three constraints, I've satisfied 1 (kind of), 2 but not 3 as it is highly value-dependent.
I'm very interested to see what other SO people can come up with. If the asker has parsed the instructions correctly, it's a very poor exercise, IMO.

Categories