Comparing double values for equality in Java. - java

I would like some advice from people who have more experience working with primitive double equality in Java. Using d1 == d2 for two doubles d1 and d2 is not sufficient due to possible rounding errors.
My questions are:
Is Java's Double.compare(d1,d2) == 0 handling rounding errors to some degree? As explained in the 1.7 documentation it returns value 0 if d1 is numerically equal to d2. Is anyone certain what exactly they mean by numerically equal?
Using relative error calculation against some delta value, is there a generic (not application specific) value of delta you would recommend? Please see example below.
Below is a generic function for checking equality considering relative error. What value of delta would you recommend to capture the majority of rounding errors from simple operations +,-,/,* operations?
public static boolean isEqual(double d1, double d2) {
return d1 == d2 || isRelativelyEqual(d1,d2);
}
private static boolean isRelativelyEqual(double d1, double d2) {
return delta > Math.abs(d1- d2) / Math.max(Math.abs(d1), Math.abs(d2));
}

You could experiment with delta values in the order of 10-15 but you will notice that some calculations give a larger rounding error. Furthermore, the more operations you make the larger will be the accumulated rounding error.
One particularly bad case is if you subtract two almost equal numbers, for example 1.0000000001 - 1.0 and compare the result to 0.0000000001
So there is little hope to find a generic method that would be applicable in all situations. You always have to calculate the accuracy you can expect in a certain application and then consider results equal if they are closer than this accuracy.
For example the output of
public class Main {
public static double delta(double d1, double d2) {
return Math.abs(d1- d2) / Math.max(Math.abs(d1), Math.abs(d2));
}
public static void main(String[] args) {
System.out.println(delta(0.1*0.1, 0.01));
System.out.println(delta(1.0000000001 - 1.0, 0.0000000001));
}
}
is
1.7347234759768068E-16
8.274036411668976E-8
Interval arithmetic can be used to keep track of the accumulated rounding errors. However in practise the error intervals come out too pessimistic, because sometimes rounding errors also cancel each other.

You could try something like this (not tested):
public static int sortaClose(double d1, double d2, int bits) {
long bitMask = 0xFFFFFFFFFFFFFFFFL << bits;
long thisBits = Double.doubleToLongBits(d1) & bitMask;
long anotherBits = Double.doubleToLongBits(d2) & bitMask;
if (thisBits < anotherBits) return -1;
if (thisBits > anotherBits) return 1;
return 0;
}
"bits" would typically be from 1 to 4 or so, depending on how precise you wanted the cutoff.
A refinement would be to add 1 to the position of the first bit to be zeroed before masking (for "rounding"), but then you have to worry about ripple all the way up past the most significant bit.

From the javadoc for compareTo
Double.NaN is considered by this method to be equal to itself and greater than all other double values (including Double.POSITIVE_INFINITY).
0.0d is considered by this method to be greater than -0.0d.
You may find this article very helpful
If you want you can check like
double epsilon = 0.0000001;
if ( d <= ( 0 - epsilon ) ) { .. }
else if ( d >= ( 0 + epsilon ) ) { .. }
else { /* d "equals" zero */ }

Related

Why am I seeing a one-off error with Math.pow(11, 16)?

I have to compute 11^16 for a project at my Uni. Somehow Math.pow(11,16) computes a solution exactly 1 less than WolframAlpha or my other computation method.
My code is:
public class Test {
public static void main(String args[]) {
long a = 11;
long b = 16;
System.out.println("" + (long)Math.pow(a, b));
System.out.println("" + squareAndMultiply(a, b));
}
public static long squareAndMultiply(long b, long e){
long result = 1;
long sq = b;
while(e>0){
if(e%2==1){
result *= sq;
}
sq = sq * sq;
e /= 2;
}
return result;
}
}
The result from the code is:
math.pow(11,16):
45949729863572160
squareAndMultiply(11,16):
45949729863572161
With floating-point arithmetic, you're in that gray zone where the precision of a double is less than that of a long (even if the range of a double is much bigger).
A double has 53 bits of precision, whereas a long can devote all 64 bits to precision. When you're dealing with values as high as 1116, the difference between one double value and the next one up becomes noticeable.
Java has a built-in method Math.ulp ("unit in last place") that effectively gives the difference in values between consecutive representable values. (There's a double version and a float version.)
System.out.println(Math.ulp(Math.pow(11, 16)));
8.0
That means the least possible double value greater than 45949729863572160 is 45949729863572168.
The long value 45949729863572161 is correct, but the value you're getting with Math.pow, 45949729863572160, is as close as a double can get to the true answer, given its limited (but still large) precision.
Casting to a long makes no difference, because Math.pow already computes the result as a double, so the answer is off by one already. Your long method of computing the value is correct.
If you're computing values that would overflow long, then instead of using double, you can use BigDecimal, which has its own pow method to retain a precision of 1.0.
The root cause of this discrepancy is loss of precision because of Double precision numbers are accurate up to sixteen decimal places.
One way to demonstrate is this example.
System.out.println((double)999999999999999999L);
outputs:
1.0E18
The output of Math.pow(11, 16) is 4.594972986357216E16, which on casting to long gets converted into 45949729863572160.
If you are interested more in learning about the loss of precision, you can check this.

method is performing incorrect calculations on double argument

firstly, im sorry if this is a trivial question. I am a beginner and have been stuck on this for hours.
Below I have tried to create a unitizer method which has a series of if else statements. They are written in descending order, each time checking if a value can be divided by a given number, and if so, performing a division, rounding the value and adding an appropriate unit to the result.
in this question I have attempted to remove all unnecessary code, thus what i am presenting here is only a fragment of the unitizer method.
why is the unitizer method outputting values in hours, when the value should be in seconds?
For clarification, the expected value is ~ 4 seconds.
public class simplified
{
public static void main(String[] args)
{
int i = 5;
double n = Math.pow(2, (double) i);
System.out.println(a6(n)); // correctly displays the expected value.
System.out.println(unitizer(a6(n)));
}
public static double a6 (double n)
{
return Math.pow(2, n); // this value is in nanoseconds.
}
public static String unitizer (double x)
{
String time = "";
if (x/(60*60*1000*1000*1000) >= 1)
{
x = Math.round(x/(60*60*1000*1000*1000) * 100.0) / 100.0;
time = x + "hr ";
}
return time;
}
}
console output:
4.294967296E9
5.25hr
There is an int overflow at the expression 60*60*1000*1000*1000. This means, that the actual result 3,600,000,000,000 is too large to be stored as an int value and is therefore 'reduced' (mod 2^31) to 817,405,952.
This can be fixed by evaluating said expression in a 'larger' arithmetic, e.g. long. There is a nice little modifier, that will force exactly that:
60L*60*1000*1000*1000
^
In particular, it hints the compiler to interpret the preceding literal 60 as a long value and in consequence the whole calculation will be done in long arithmetic.
This modifier is by the way case-insensitive; however I prefer an upper-case L, because the lower-case letter l can easily be mistaken by the number 1.
With this change, the code will not enter the if-statement, because the value x is not larger than one hour. Most probably the omitted code of unitizer will deal with this case.
On a last side note, java has an in-built TimeUnit enum, which can do these conversions, too. However, it does so in long arithmetic and not in double arithmetic as it is required for this specific question.

How to determine if two doubles are nearly equal

I'm trying to find some Java code to determine if two doubles are nearly equal. I did a lot of Googling and found bits and pieces that I've put together here. Where it starts to escape me is the use of a "relative epsilon". This approach seems like what I'm looking for. I don't want to have to specify the epsilon directly but want to use an epsilon based on the magnitudes of the two arguments. Here is the code I put together, I need a sanity check on it. (P.S. I know just enough math to be dangerous.)
public class MathUtils
{
// http://stackoverflow.com/questions/3728246/what-should-be-the-
// epsilon-value-when-performing-double-value-equal-comparison
// ULP = Unit in Last Place
public static double relativeEpsilon( double a, double b )
{
return Math.max( Math.ulp( a ), Math.ulp( b ) );
}
public static boolean nearlyEqual( double a, double b )
{
return nearlyEqual( a, b, relativeEpsilon( a, b ) );
}
// http://floating-point-gui.de/errors/comparison/
public static boolean nearlyEqual( double a, double b, double epsilon )
{
final double absA = Math.abs( a );
final double absB = Math.abs( b );
final double diff = Math.abs( a - b );
if( a == b )
{
// shortcut, handles infinities
return true;
}
else if( a == 0 || b == 0 || absA + absB < Double.MIN_NORMAL )
{
// a or b is zero or both are extremely close to it
// relative error is less meaningful here
// NOT SURE HOW RELATIVE EPSILON WORKS IN THIS CASE
return diff < ( epsilon * Double.MIN_NORMAL );
}
else
{
// use relative error
return diff / Math.min( ( absA + absB ), Double.MAX_VALUE ) < epsilon;
}
}
}
I would use a library for this, the one I normally use is DoubleMath fro Googles Guava library. https://google.github.io/guava/releases/19.0/api/docs/com/google/common/math/DoubleMath.html
if (DoubleMath.fuzzyEquals(a, b, epsilon)) {
// a and b are equal within the tolerance given
}
there is also a fuzzyCompare.
The usual way to compare 2 floating values a,b is:
if ( Math.abs(a-b) <= epsilon ) do_stuff_if_equal;
else do_stuff_if_different;
where Math.abs() is absolute value. As I do not code in JAVA you need to use double variant if that is not the case. The epsilon is your difference. As mentioned ulp is too small for this. You need to use value that makes sense for the values you are comparing. So how to compute epsilon?
That is a bit tricky and yes it is possible to use magnitude of a,b but that is not robust way because if exponents of a,b are too different you can obtain false positives easily. Instead you should use a meaning-full value. For example if you are comparing position coordinates then the epsilon should be fraction of min detail or minimal distance you consider as the same point. For angles some minimal angle that is small enough like 1e-6 deg but the value depends on ranges and accuracy you work with. For normalized <-1,1> ranges I usually use 1e-10 or 1e-30.
As you can see the epsilon depends mostly on target accuracy and magnitude and change very from case to case so creating some uniform way (to get rid of epsilon like you want) is not safe and only would lead to head aches later on.
To ease up this I usually define a _zero constant or variable (in case of computational classes) that can be changed. Set it on default to value that is good enough for most cases and if cause problems at some point I know I can easily change it ...
If you want to do it your way anyway (ignoring above text) then you can do this:
if (Math.abs(a)>=Math.abs(b)) epsilon=1e-30*Math.abs(b);
else epsilon=1e-30*Math.abs(a);
but as I said this may lead to wrong results. If you persist on using ulp then I would use Min instead of Max.
You can use the class org.apache.commons.math3.util.Precision from the Apache Commons Math. Example:
if (Precision.equals(sum, price, 0.009)) {
// arguments are equal or within the range of allowed error (inclusive)
}

How can I accurately determine if a double is an integer? [duplicate]

This question already has answers here:
How to test if a double is an integer
(18 answers)
Closed 9 years ago.
Specifically in Java, how can I determine if a double is an integer? To clarify, I want to know how I can determine that the double does not in fact contain any fractions or decimals.
I am concerned essentially with the nature of floating-point numbers. The methods I thought of (and the ones I found via Google) follow basically this format:
double d = 1.0;
if((int)d == d) {
//do stuff
}
else {
// ...
}
I'm certainly no expert on floating-point numbers and how they behave, but I am under the impression that because the double stores only an approximation of the number, the if() conditional will only enter some of the time (perhaps even a majority of the time). But I am looking for a method which is guaranteed to work 100% of the time, regardless of how the double value is stored in the system.
Is this possible? If so, how and why?
double can store an exact representation of certain values, such as small integers and (negative or positive) powers of two.
If it does indeed store an exact integer, then ((int)d == d) works fine. And indeed, for any 32-bit integer i, (int)((double)i) == i since a double can exactly represent it.
Note that for very large numbers (greater than about 2**52 in magnitude), a double will always appear to be an integer, as it will no longer be able to store any fractional part. This has implications if you are trying to cast to a Java long, for instance.
How about
if(d % 1 == 0)
This works because all integers are 0 modulo 1.
Edit To all those who object to this on the grounds of it being slow, I profiled it, and found it to be about 3.5 times slower than casting. Unless this is in a tight loop, I'd say this is a preferable way of working it out, because it's extremely clear what you're testing, and doesn't require any though about the semantics of integer casting.
I profiled it by running time on javac of
class modulo {
public static void main(String[] args) {
long successes = 0;
for(double i = 0.0; i < Integer.MAX_VALUE; i+= 0.125) {
if(i % 1 == 0)
successes++;
}
System.out.println(successes);
}
}
VS
class cast {
public static void main(String[] args) {
long successes = 0;
for(double i = 0.0; i < Integer.MAX_VALUE; i+= 0.125) {
if((int)i == i)
successes++;
}
System.out.println(successes);
}
}
Both printed 2147483647 at the end.
Modulo took 189.99s on my machine - Cast took 54.75s.
if(new BigDecimal(d).scale() <= 0) {
//do stuff
}
Your method of using if((int)d == d) should always work for any 32-bit integer. To make it work up to 64 bits, you can use if((long)d == d, which is effectively the same except that it accounts for larger magnitude numbers. If d is greater than the maximum long value (or less than the minimum), then it is guaranteed to be an exact integer. A function that tests whether d is an integer can then be constructed as follows:
boolean isInteger(double d){
if(d > Long.MAX_VALUE || d < Long.MIN_VALUE){
return true;
} else if((long)d == d){
return true;
} else {
return false;
}
}
If a floating point number is an integer, then it is an exact representation of that integer.
Doubles are a binary fraction with a binary exponent. You cannot be certain that an integer can be exactly represented as a double, especially not if it has been calculated from other values.
Hence the normal way to approach this is to say that it needs to be "sufficiently close" to an integer value, where sufficiently close typically mean "within X %" (where X is rather small).
I.e. if X is 1 then 1.98 and 2.02 would both be considered to be close enough to be 2. If X is 0.01 then it needs to be between 1.9998 and 2.0002 to be close enough.

Value change when converting a long to a double and back

given the following code:
long l = 1234567890123;
double d = (double) l;
is the following expression guaranteed to be true?
l == (long) d
I should think no, because as numbers get larger, the gaps between two doubles grow beyond 1 and therefore the conversion back yields a different long value. In case the conversion does not take the value that's greater than the long value, this might also happen earlier.
Is there a definitive answer to that?
Nope, absolutely not. There are plenty of long values which aren't exactly representable by double. In fact, that has to be the case, given that both types are represented in 64 bits, and there are obviously plenty of double values which aren't representable in long (e.g. 0.5)
Simple example (Java and then C#):
// Java
class Test {
public static void main(String[] args) {
long x = Long.MAX_VALUE - 1;
double d = x;
long y = (long) d;
System.out.println(x == y);
}
}
// C#
using System;
class Test
{
static void Main()
{
long x = long.MaxValue;
double d = x;
long y = (long) d;
Console.WriteLine(x == y);
}
}
I observed something really strange when doing this though... in C#, long.MaxValue "worked" in terms of printing False... whereas in Java, I had to use Long.MAX_VALUE - 1. My guess is that this is due to some inlining and 80-bit floating point operations in some cases... but it's still odd :)
You can test this as there are a finite number of long values.
for (long l = Long.MIN_VALUE; l<Long.MAX_VALUE; l++)
{
double d = (double) l;
if (l == (long)d)
{
System.out.println("long " + l + " fails test");
}
}
Doesn't take many iterations to prove that;
l = -9223372036854775805
d = -9.223372036854776E18
(long)d = -9223372036854775808
My code started with 0 and incremented by 100,000,000. The smallest number that failed the test was found to be 2,305,843,009,300,000,000 (19 digits). So, any positive long less than 2,305,843,009,200,000,000 is representable exactly by doubles. In particular, 18-digit longs are also representable exactly by doubles.
By the way, the reason I was interested in this question is that I wondered if I can use doubles to represent timestamps (in milliseconds). Since current timestamps are on the order of 13 digits (and it will take for them rather long time to get to 18 digits), I'll do that.

Categories

Resources