How can I round a double according to the next digit?
For example:
1,23-->1,2
3,6544767-->3,65458
Like we know in Math. If the next digit is five or more we round it to ceiling. If it's lower than 5 we do the opposite. Also I have to do it with 5 decimals.
"Rounding" changes the actual value of your double.
"Display to five places" is a "presentation issue" - the number of decimal places you show in a "print" statement doesn't have to match the actual precision of your double variable.
Having said that, you might consider using DecimalFormat:
It doesn't alter the underlying value
It will display the value to 5 decimal places (if you wish)
It can automatically round the displayed value for you
By default, the rounding method it uses, RoundingMode.HALF_EVEN, is exactly what you described above.
Related
I am working on an android application where for a part of the app, I have 2 floating point values and I cannot have them be exactly the same because this is causing a bug in one of my screens. Those numbers are being sent from a server and are out of my control (e.g. I cannot force them to be different).
The app is written in Kotlin, but I assume that this issue is similar (if not exactly the same) for Java, as Kotlin uses the JVM behind the scenes.
I thought of a "creative" way of solving this without changing my logic too much, by subtracting Float.MIN_VALUE from one of them, making them almost, but not exactly the same. What I actually need to happen is for if(a == b) to fail (where b is actually a - Float.MIN_VALUE).
But to my surprise, when the code runs, if(a == b) returns true. When I opened the "evaluate" window in Android Studio here is what I found out:
Let me reiterate that currentPayment is a Float, so there shouldn't be any auto-conversions or rounding going on (like when dividing Float by an Int for example).
Let me also point out that I can guarantee that currentPayment is not
Float.MAX_VALUE or -Float.MAX_VALUE (so the result of the operation is within the bounds of Float).
According to docs and this answer Float.MIN_VALUE is the smallest positive non-zero value of Float and has a value of 1.4E-45, which is confirmed here:
I also saw in another post (which I cannot find again for some reason), that this can also be thought of as the maximum precision of Float, which makes sense.
Since currentPayment is a Float, I would expect it should be able to hold any floating point value within the bounds of Float to it's maximum precision (i.e. Float.MIN_VALUE).
Therefore I would expect currentPayment to NOT equal currentPayment - Float.MIN_VALUE, but alas that is not the case.
Can anyone explain this please?
Since currentPayment is a Float, I would expect it should be able to hold any floating point value within the bounds of Float to it's maximum precision (i.e. Float.MIN_VALUE).
This is a wrong assumption. Float is called "float" because it has floating precision. The amount of precision depends on how big the number is that you're storing. The smallest possible float value is smaller than the precision of almost any other possible number, so it is too small to affect them if you add or subtract it. At the high end, Float numbers have precisions that are much greater than the integer 1. If you subtract 999,000,000 from Float.MAX_VALUE, it will still return Float.MAX_VALUE because the precision is so poor at the highest end.
Also, since floating point numbers are not stored in base-10, they are inappropriate for storing currency amounts, because you can never exactly represent a decimal fraction. (I mention that because your variable name has the word "payment" in it, which is a red flag.)
You should either use BigDecimal, Long, or Int to represent currency, so your currency amounts and arithmetic will be exact.
Edit:
Here's an analogy to help understand it, since it is hard to contemplate binary numbers. Floats are 32-bits in Java and Kotlin, but imagine we have a special kind of computer that can store a floating point number in base-10. Each bit on this computer is not just 0 or 1, but can be anything from 0 to 9. A Float on this computer can have 4 digits and a decimal place, but the decimal place is floating, so it can be placed anywhere relative to the four digits. So a Float on this computer is always five bits--four of the bits are the digits, and the fifth bit tells you where the decimal place goes.
In this imaginary computer's Float, the smallest possible number that can be represented is .0001 and the largest possible number is 9999.. You can't represent 9999.5 or even 1000.5 because there aren't enough digits available. There's no fixed amount of precision--the precision is determined by where the decimal place is in the current number. Precision is better for numbers with a decimal place farther to the left.
For the number storage format to be able to have a fixed precision, we would have to fix the decimal point in one place for all numbers. We would have to choose a precision. Suppose we chose a precision of 0.001. Our fifth bit that told us where the decimal place goes in the floating point can now just be used for a fifth digit. Now we know the precision is always 0.001, but the largest possible number we can represent is 99.999 and the smallest possible number is 0.001, a much smaller possible range than with floating point. This limitation is the reason floating points are used instead.
What would be the most efficient way to grab the, say, 207th decimal place of a number? Would it just be x * Math.pow(10,207) % 10?
How's this for python
int((x*(10**n)))%10
What you want is impossible.
The only things in java that work with Math.pow and basic operators are the primitives. The only floating point primitives are float and double. These are IEEE754 floating point numbers; doubles are 64 bits and floats are 32 bits.
A simple principle applies: If you have 64 bits, then you can only represent 2^64 different numbers (it's actually a little less). So, you get about 18446744073709551616 numbers, of all numbers in existence, which actually exist as far as the computer is concerned for doubles. all other numbers do not exist.
So what happens if a mathematical operation (say, 0.1 + 0.2) ends up being a number that doesn't exist? Well, java (this is predicated by the IEEE754 standard; most languages and chips do it this way) will return you the nearest number amongst all the 18446744073709551616 numbers that do exist.
The problem with wanting the 207th digit is that obviously, given that only 18446744073709551616 numbers exist, none of those 18446744073709551616 numbers have that kind of precision. Asking for the 207th digit is therefore completely random. It says nothing about the input number... whatsoever.
Let me repeat that: There are no double values that have a significant 207th digit AT ALL.
If you want 'perfect' representation, with no rounding whatsoever, you want BigDecimal, but note that demanding perfection is tricky. Imagine in basic decimal math (computers are binary, but lets stick with decimal as we're all much more familiar with it, what with our 10 fingers and all), I ask you to only give me perfect answers, and then I ask you to divide 1 by 3.
BigDecimal won't let you do that either, so the ops you can run on BigDecimals without telling BigDecimal in what ways it is allowed to be inprecise leads to exceptions.
If you've set it all up exactly how you wanted it, and you really have a BigDecimal with a 207th digit after the comma, you can use the scale feature, or just the power-of-10 feature to get what you want.
Note BigDecimal is not primitive and therefore does not support the +, %, etc operators.
***Special Note: There is no: "This will handle all situations" answer here as the arbitrary value such as 207 could take the calculations way outside the bounds of possible precision of the variable types involved. My answer as such will only work within the bounds of variable type precision for which 207 is really not possible...
To get the specific digit an arbitrary number (like 207) of places after the decimal point... if you just multiply by factor of 10.. and then take mod 10, the answer (in java) is still a floating point type... not a single digit...
To get a specific digit an arbitrary number (n) of places after the decimal point, without converting to string:
Math.floor(x*Math.pow(10,n)) % 10;
to get 4th digit after 2.987654321
x*Math.pow(10, 4) = 29876.54321
Math.floor(29876.54321) = 29876
29876 % 10 = 6
We are solving a numeric precision related bug. Our system collects some numbers and spits their sum.
The issue is that the system does not retain the numeric precision, e.g. 300.7 + 400.9 = 701.599..., while expected result would be 701.6. The precision is supposed to adapt to the input values so we cannot just round results to fixed precision.
The problem is obvious, we use double for the values and addition accumulates the error from the binary representation of the decimal value.
The path of the data is following:
XML file, type xsd:decimal
Parse into a java primitive double. Its 15 decimal places should be enough, we expect values no longer than 10 digits total, 5 fraction digits.
Store into DB MySql 5.5, type double
Load via Hibernate into a JPA entity, i.e. still primitive double
Sum bunch of these values
Print the sum into another XML file
Now, I assume the optimal solution would be converting everything to a decimal format. Unsurprisingly, there is a pressure to go with the cheapest solution. It turns out that converting doubles to BigDecimal just before adding a couple of numbers works in case B in following example:
import java.math.BigDecimal;
public class Arithmetic {
public static void main(String[] args) {
double a = 0.3;
double b = -0.2;
// A
System.out.println(a + b);//0.09999999999999998
// B
System.out.println(BigDecimal.valueOf(a).add(BigDecimal.valueOf(b)));//0.1
// C
System.out.println(new BigDecimal(a).add(new BigDecimal(b)));//0.099999999999999977795539507496869191527366638183593750
}
}
More about this:
Why do we need to convert the double into a string, before we can convert it into a BigDecimal?
Unpredictability of the BigDecimal(double) constructor
I am worried that such a workaround would be a ticking bomb.
First, I am not so sure that this arithmetic is bullet proof for all cases.
Second, there is still some risk that someone in the future might implement some changes and change B to C, because this pitfall is far from obvious and even a unit test may fail to reveal the bug.
I would be willing to live with the second point but the question is: Would this workaround provide correct results? Could there be a case where somehow
Double.valueOf("12345.12345").toString().equals("12345.12345")
is false? Given that Double.toString, according to javadoc, prints just the digits needed to uniquely represent underlying double value, so when parsed again, it gives the same double value? Isn't that sufficient for this use case where I only need to add the numbers and print the sum with this magical Double.toString(Double d) method? To be clear, I do prefer what I consider the clean solution, using BigDecimal everywhere, but I am kind of short of arguments to sell it, by which I mean ideally an example where conversion to BigDecimal before addition fails to do the job described above.
If you can't avoid parsing into primitive double or store as double, you should convert to BigDecimal as early as possible.
double can't exactly represent decimal fractions. The value in double x = 7.3; will never be exactly 7.3, but something very very close to it, with a difference visible from the 16th digit or so on to the right (giving 50 decimal places or so). Don't be mislead by the fact that printing might give exactly "7.3", as printing already does some kind of rounding and doesn't show the number exactly.
If you do lots of computations with double numbers, the tiny differences will eventually sum up until they exceed your tolerance. So using doubles in computations where decimal fractions are needed, is indeed a ticking bomb.
[...] we expect values no longer than 10 digits total, 5 fraction digits.
I read that assertion to mean that all numbers you deal with, are to be exact multiples of 0.00001, without any further digits. You can convert doubles to such BigDecimals with
new BigDecimal.valueOf(Math.round(doubleVal * 100000), 5)
This will give you an exact representation of a number with 5 decimal fraction digits, the 5-fraction-digits one that's closest to the input doubleVal. This way you correct for the tiny differences between the doubleVal and the decimal number that you originally meant.
If you'd simply use BigDecimal.valueOf(double val), you'd go through the string representation of the double you're using, which can't guarantee that it's what you want. It depends on a rounding process inside the Double class which tries to represent the double-approximation of 7.3 (being maybe 7.30000000000000123456789123456789125) with the most plausible number of decimal digits. It happens to result in "7.3" (and, kudos to the developers, quite often matches the "expected" string) and not "7.300000000000001" or "7.3000000000000012" which both seem equally plausible to me.
That's why I recommend not to rely on that rounding, but to do the rounding yourself by decimal shifting 5 places, then rounding to the nearest long, and constructing a BigDecimal scaled back by 5 decimal places. This guarantees that you get an exact value with (at most) 5 fractional decimal places.
Then do your computations with the BigDecimals (using the appropriate MathContext for rounding, if necessary).
When you finally have to store the number as a double, use BigDecimal.doubleValue(). The resulting double will be close enough to the decimal that the above-mentioned conversion will surely give you the same BigDecimal that you had before (unless you have really huge numbers like 10 digits before the decimal point - the you're lost with double anyway).
P.S. Be sure to use BigDecimal only if decimal fractions are relevant to you - there were times when the British Shilling currency consisted of twelve Pence. Representing fractional Pounds as BigDecimal would give a disaster much worse than using doubles.
It depends on the Database you are using. If you are using SQL Server you can use data type as numeric(12, 8) where 12 represent numeric value and 8 represents precision. similarly, for my SQL DECIMAL(5,2) you can use.
You won't lose any precision value if you use the above-mentioned datatype.
Java Hibernate Class :
You can define
private double latitude;
Database:
Why are the values of String.valueOf(5.6d + 5.8d) and String.format("%f", 5.6d + 5.8d) diffrent?
String.valueOf(5.6d + 5.8d) will print "11.399999999999999".
String.format("%f", 5.6d + 5.8d) will print "11.400000".
Why is it so?
Edit: The question differs to Is floating point math broken? , because String.format() round up (see answers)
From the documentation of Format Strings for String#format:
If the precision is less than the number of digits which would appear after the decimal point in the string returned by Float.toString(float) or Double.toString(double) respectively, then the value will be rounded using the round half up algorithm.
By default, String.format("%f", ...) uses 6 decimal digits of precision; because this is fewer digits than what would appear when used by Double.toString(double) (which is equivalent to String.valueOf(double)), then the value is rounded as specified above.
There are two parts to the explanation.
The result of 5.6d + 5.8d is not exactly 11.4 due to binary floating point representation, precision and rounding issues; see Is floating point math broken? for explanations of why that is so. (And no, it isn't broken!)
The reason that String.valueOf and String.format are outputting different answers for the same double value is down to the respective specifications:
For valueOf:
"How many digits must be printed for the fractional part of m or a? There must be at least one digit to represent the fractional part, and beyond that as many, but only as many, more digits as are needed to uniquely distinguish the argument value from adjacent values of type double."
The double value is closer to 11.399999999999999 than 14.0 so the output is the former.
For format:
"If the conversion is 'e', 'E' or 'f', then the precision is the number of digits after the decimal separator. If the precision is not specified, then it is assumed to be 6."
When you round 11.399999999999999 to 6 digits of precision after the decimal point you get 11.40000000.
(The above quotes are from the Java 9 javadocs.)
The first line below will print 0.8999999999999999 because of precision loss, this is clear. But the second line will print 0.9, I just do not understand why. Shouldn't there be the same problem with this calculation?
System.out.println(2.00-1.10);
System.out.printf("%f",2.00-1.10);
I think you are missing something as using System.out.printf(), if you do not explicit formatting widths then default behavior of printf in C (which is 6 decimal places if not explicitly specified)
So if you will not specify any number to %f then by default it will print only 1 character. However if you want to change the number after the decimal then you need to specify it like %.2f, this will print the number to 2 decimal places.
So it is similar to writing like
System.out.printf("%f",2.00-1.10);
or
System.out.printf("%.1f",2.00-1.10);
As the general syntax for format specifier for float is:
%[flags][width][.precision][argsize]typechar
On a side note:-
Also there is a formatter class in Java for this.
An interpreter for printf-style format strings. This class provides
support for layout justification and alignment, common formats for
numeric, string, and date/time data, and locale-specific output.
Common Java types such as byte, BigDecimal, and Calendar are
supported. Limited formatting customization for arbitrary user types
is provided through the Formattable interface.
From the Oracle Docs
If the precision is not specified then the default value is 6. If the
precision is less than the number of digits which would appear after
the decimal point in the string returned by Float.toString(float) or
Double.toString(double) respectively, then the value will be rounded
using the round half up algorithm.
This output is the due to reason of round half up operation of floating format %f. If you go through the docs, than you will get
If the precision is not specified then the default value is 6. If the
precision is less than the number of digits which would appear after
the decimal point in the string returned by Float.toString(float) or
Double.toString(double) respectively, then the value will be rounded
using the round half up algorithm.
according to me, the format %f prints 1 character.
the fact that it prints 0.9 is standard mathematical rounding behavior