I am trying to determine the double
machine epsilon in Java, using the definition of it being the smallest representable double
value x
such that 1.0 + x != 1.0
, just as in C/C++. According to wikipedia, this machine epsilon is equal to 2^-52
(with 52 being the number of double
mantissa bits - 1).
My implementation uses the Math.ulp()
function:
double eps = Math.ulp(1.0);
System.out.println("eps = " + eps);
System.out.println("eps == 2^-52? " + (eps == Math.pow(2, -52)));
and the results are what I expected:
eps = 2.220446049250313E-16
eps == 2^-52? true
So far, so good. However, if I check that the given eps
is indeed the smallest x
such that 1.0 + x != 1.0
, there seems to be a smaller one, aka the previous double
value according to Math.nextAfter()
:
double epsPred = Math.nextAfter(eps, Double.NEGATIVE_INFINITY);
System.out.println("epsPred = " + epsPred);
System.out.println("epsPred < eps? " + (epsPred < eps));
System.out.println("1.0 + epsPred == 1.0? " + (1.0 + epsPred == 1.0));
Which yields:
epsPred = 2.2204460492503128E-16
epsPred < eps? true
1.0 + epsPred == 1.0? false
As we see, we have a smaller than machine epsilon such which, added to 1, yields not 1, in contradiction to the definition.
So what is wrong with the commonly accepted value for machine epsilon according to this definition? Or did I miss something? I suspect another esoteric aspect of floating-point maths, but I can't see where I went wrong...
EDIT: Thanks to the commenters, I finally got it. I actually used the wrong definition! eps = Math.ulp(1.0)
computes the distance to the smallest representable double > 1.0
, but -- and that's the point -- that eps
is not the smallest x
with 1.0 + x != 1.0
, but rather about twice that value: Adding 1.0 + Math.nextAfter(eps/2)
is rounded up to 1.0 + eps
.
The smaller the epsilon's value, the greater the comparison accuracy. However, if we specify the tolerance value too small, we'll get the same false result as in the simple == comparison. In general, epsilon's value with 5 and 6 decimals is usually a good place to start.
Epsilon is a family of Java-based scripting languages for automating common model-based software engineering tasks, such as code generation, model-to-model transformation and model validation, that work out of the box with EMF (including Xtext and Sirius), UML, Simulink, XML and other types of models.
The EPSILON property has a value of approximately 2.2204460492503130808472633361816E-16 , or 2-52.
In C, machine epsilon is specified in the standard header with the names FLT_EPSILON, DBL_EPSILON, and LDBL_EPSILON. Those three macros give the machine epsilon for the float, double, and long double types, respectively.
using the definition of it being the smallest representable double value x such that 1.0 + x != 1.0, just as in C/C++
This has never been the definition, not in Java and not in C and not in C++.
The definition is that the machine epsilon is the distance between one and the smallest float/double larger than one.
Your “definition” is wrong by a factor of nearly 2.
Also, the absence of strictfp
only allows a larger exponent range and should not have any impact on the empirical measurement of epsilon, since that is computed from 1.0
and its successor, each of which and the difference of which can be represented with the standard exponent range.
I'm not sure your experimental method / theory is sound. The documentation for the Math class states:
For a given floating-point format, an ulp of a specific real number value is the distance between the two floating-point values bracketing that numerical value
The documentation for the ulp
method says:
An ulp of a double value is the positive distance between this floating-point value and the double value next larger in magnitude
So, if you want the smallest eps
value such that 1.0 + eps != 1.0
, your eps really should generally be less than Math.ulp(1.0)
, since at least for any value greater than Math.ulp(1.0) / 2
, the result will be rounded up.
I think the smallest such value will be given by Math.nextAfter(eps/2, 1.0)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With