I am confused about using expm1 function in java
The Oracle java doc for Math.expm1 says:
Returns exp(x) -1. Note that for values of x near 0, the exact sum of
expm1(x) + 1 is much closer to the true result of ex than exp(x).
but this page says:
However, for negative values of x, roughly -4 and lower, the algorithm
used to calculate Math.exp() is relatively ill-behaved and subject to
round-off error. It's more accurate to calculate ex - 1 with a
different algorithm and then add 1 to the final result.
should we use expm1(x) for negative x values or near 0 values?
The implementation of double at the bit level means that you can store doubles near 0 with much more precision than doubles near 1. That's why expm1 can give you much more accuracy for near-zero powers than exp can, because double doesn't have enough precision to store very accurate numbers very close to 1.
I don't believe the article you're citing is correct, as far as the accuracy of Math.exp goes (modulo the limitations of double). The Math.exp specification guarantees that the result is within 1 ulp of the exact value, which means -- to oversimplify a bit -- a relative error of at most 2^-52, ish.
You use expm1(x) for anything close to 0. Positive or negative.
The reason is because exp(x) of anything close to 0 will be very close to 1. Therefore exp(x) - 1 will suffer from destructive cancellation when x is close to 0.
expm1(x) is properly optimized to avoid this destructive cancellation.
From the mathematical side: If exp is implemented using its Taylor Series, then expm1(x) can be done by simply omitting the first +1.
Related
I know that for primitive floating point types (floats and doubles), you're not supposed to compare them directly via ==. But what if you're using the wrapper class for double? Will something like
Double a = 5.05;
Double b = 5.05;
boolean test = a.equals(b);
compare the two values properly?
You need to fully understand the reason for why == comparison is a bad idea. Without understanding, you're just fumbling in the dark.
Let's talk about how computers (and doubles) work.
Imagine you enter a room; it has 3 lightswitches, otherwise it is bare. You will enter the room, you can fiddle with the switches, but then you have to leave. I enter the room later and can look at the switches.
How much information can you convey?
The answer is: You can convey 8 different states: DDD, DDU, DUD, DUU, UDD, UDU, UUD, and UUU. That's it.
Computers work exactly like this when they store a double. Except instead of 3 switches, you get 64 switches. That means 2^64 different states you can convey with a single double, and that's a ton of states: That's a 19 digit number.
But it's still a finite amount of states, and that's problematic: There are an infinite amount of numbers between 0 and 1. Let alone between -infinity and +infinity, which double dares to cover. How do you store one of an infinite infinity of choices when you only get to represent 2^64 states? That 19-digit number starts to look pretty small when it's tasked to differentiate from an infinite infinity of possibilities, doesn't it?
The answer, of course, is that this is completely impossible.
So doubles don't actually work like that. Instead, someone took some effort and hung up a gigantic numerline, from minus infinite to plus infinite, in a big room, and threw 2^64 darts at this line. The numbers they landed on are the 'blessed numbers' - these are representable by a double value. That does mean there are an infinite amount of numbers between any 2 darts that therefore are not representable. The darts aren't quite random: The closer you are to 0, the denser the darts. Once you get beyond about 2^52 or so, the distance between 2 darts exceeds 1.0 even.
Here's a trivial example of a non-representable number: 0.3. Amazing, isn't it? Something that simple. It means a computer literally cannot calculate 0.1 + 0.2 using double. So what happens when you try? The rules state that the result of any calculation is always silently rounded to the nearest blessed number.
And therein lies the rub: You can run the math:
double x = 0.1 + 0.2;
and later do:
double y = 0.9 - 0.8 + 0.15 + 0.05;
and us humans would immediately notice that x and y are naturally identical. But not so for computers - because of that silent rounding to the nearest blessed number, it's possible that x is 0.29999999999999999785, and y is 0.300000000000000000012.
Thus we get to four crucial aspects when using double (or float which is just about worse in every fashion, don't ever use those):
If you need absolute precision, don't use them at all.
When printing them, always round them down. System.out.println does this out of the box, but you should really use .printf("%.5f") or similar: Pick the # of digits you need.
Be aware that the errors will compound, and it gets worse as you are further away from 1.0.
Do not ever compare with ==, instead always use a delta-compare: The notion of "lets consider the 2 numbers equal if they are within 0.0000000001 of each other".
There is no universal magic delta value; it depends on your precision needs, how far you away from 1.0, etc. Therefore, just asking the computer: Hey, just figure this stuff out I just wanna know if these 2 doubles are equal is impossible. The only definition available that doesn't require your input as to 'how close' they can be, is the notion of sheer perfection: They are equal only if they are precisely identical. This definition would fail you in that trivial example above. It makes not one iota of difference if you use Double.equals instead of double == double, or any other utility class for that matter.
So, no, Double.equals is not suitable. You will have to compare Math.abs(d1 - d2) < epsilon, where epsilon is your choice. Mostly, if equality matters at all you're already doing it wrong and shouldn't be using double in the first place.
NB: When representing money, you don't want unpredictable rounding, so never use doubles for these. Instead figure out what the atomic banking unit is (dollarcents, eurocents, yen, satoshis for bitcoin, etc), and store that as a long. You store $4.52 and long x = 452;, not as double x = 4.52;.
I have an unit test failing on a Math.Tan(-PI/2) returning the wrong version in .NET.
The 'expected' value is taken from Wolfram online (using the spelled-out constant for -Pi/2).
See for yourselves here.
As correctly observed in the comments, the mathematical result of tan(-pi/2) is infinity. However, the constant Math.PI does not perfectly represent PI, so this is a 'near the limit' input.
Here's the code.
double MINUS_HALF_PI = -1.570796326794896557998981734272d;
Console.WriteLine(MINUS_HALF_PI == -Math.PI/2); //just checking...
double tan = Math.Tan(MINUS_HALF_PI);
Console.WriteLine("DotNET {0:E20}", tan);
double expected = -1.633123935319534506380133589474e16;
Console.WriteLine("Wolfram {0:E20}", expected);
double off = Math.Abs(tan-expected);
Console.WriteLine(" {0:E20}", off);
This is what gets printed:
True
DotNET -1.63317787283838440000E+016
Wolfram -1.63312393531953460000E+016
5.39375188498000000000E+011
I thought it's an issue of floating-point representation.
Strangely though, the same thing in Java DOES return the same value as Wolfram, down to the last digit - see it evaluated in Eclipse. (The expressions are cropped - you'll have to believe me they use the same constant as MINUS_HALF_PI above.)
True
DotNET -1.63317787283838440000E+016
Wolfram -1.63312393531953460000E+016
Java -1.63312393531953700000E+016
As you can see, the difference is:
between Wolfram and .NET: ~5.39 * 10^11
between Wolfram and Java: =2.40 * 10^1
That's ten orders of magnitude!
So, any ideas why the .NET and Java implementations differ so much? I would expect them both to just defer the actual computing to the processor. Is this assumption unrealistic for x86?
Update
As requested, I tried running in Java with strictfp. No change:
The entire question is constructed to create a tendentious result. The double value closest to half PI is -1.5707963267948966; the other digits are just ignored. So it’s no wonder that neither C# nor Java detect that the remaining 14 more digits are not turning the result closer to -PI/2, but carefully chosen to trick Wolfram Alpha to return a value close to the result of Java.
-1.570796326794896557998981734272 // the number from the question
-1.57079632679489661923132169163975… // the real digits of -PI/2
↑
the end of the double precision
Any other number within the range that would get rounded to the same double number including the exact double value as used by Java yields to a value on Wolfram Alpha having nothing in common with neither, the C# nor Java result.
I have been having a lot of problems with NaN values propagating in a very long program I am having to look after. After much single stepping I have been able to find that at some point there is a variable whose value is shown by the Debugger as Infinity, there is another variable that gets divided by this Infinity variable, which results in NaN. Is this behaviour correct, or should it have resulted in 0? All the variables are double variables.
Is division by infinity a NaN in java?
The short answer is No.
The Java Language Specification (JLS 15.17.2) says:
"Division of a finite value by an infinity results in a signed zero."
It also mentions that this is "determined by the rules of IEEE 754 arithmetic".
The only case where division by infinity gives a NaN is when you divide an infinity by an infinity. (Same reference as above.)
If you (really) see differently, then there is a bug in your Java "platform"1. But that would be an extraordinary thing, so you need to check your evidence and methodology really thoroughly before calling "bug".
1 - ... most likely in the floating point hardware!
Think about the nature of division.
4/5
Means you've got four of something, and you're splitting it into 5 pieces. The outcome is the size of one of those pieces.
4/infinity
Means you've got four of something, and you're splitting it into infinitely small pieces. It can never be 0 because numbers are continuous, ergo it's NaN because you never stop handing out those pieces of 4, to measure one of those pieces.
Reading the Javadocs, I see that Math.E is "The double value that is closer than any other to e, the base of the natural logarithms.". The printed value for Math.E is 2.718281828459045 while the value of Math.exp(1.0), which should be the same value is: 2.7182818284590455 (one more 5 at the end).
From the docs, it sounds like the bits in Math.E have been "manually adjusted" to get closer to the actual value of e than the calculation produced by Math.exp(1.0). Is this correct, or am I reading the docs incorrectly?
If that is correct, then is using Math.pow(Math.E, n) more accurate than Math.exp(n), or less? I've Googled and search SO, but can't find anything on this particular issue.
The actual value to 16 decimal places is 2.7182818284590452; 2 is closer to 0 than to 5, so the constant is closer.
Note that when doing floating point calculations with either number it's quite likely the error in the floating point representation of your answer will make which one you use largely
irrelevant.
Math.E
2.718281828459045
Math.exp(1.0)
2.7182818284590455
So this is the value from Wikipedia, 2.7182818284590452 The only difference I can see is a rounding error on the last digit of the Math.exp(1.0) where the value is 5 instead of 2. So strictly speaking Math.E is more accurate but unless you're doing some really crazy stuff, it won't matter for precision.
There may be speed considerations for using the precalculated Math.E instead of Math.exp(1.0). You might want to check that out too.
Ok so I'm trying to use Apache Commons Math library to compute a double integral, but they are both from negative infinity (to around 1) and it's taking ages to compute. Are there any other ways of doing such operations in java? Or should it run "faster" (I mean I could actually see the result some day before I die) and I'm doing something wrong?
EDIT: Ok, thanks for the answers. As for what I've been trying to compute it's the Gaussian Copula:
So we have a standard bivariate normal cumulative distribution function which takes as arguments two inverse standard normal cumulative distribution functions and I need integers to compute that (I know there's a Apache Commons Math function for standard normal cumulative distribution but I failed to find the inverse and bivariate versions).
EDIT2: as my friend once said "ahhh yes the beauty of Java, no matter what you want to do, someone has already done it" I found everything I needed here http://www.iro.umontreal.ca/~simardr/ssj/ very nice library for probability etc.
There are two problems with infinite integrals: convergence and value-of-convergence. That is, does the integral even converge? If so, to what value does it converge? There are integrals which are guaranteed to converge, but whose value it is not possible to determine exactly (try the integral from 1 to infinity of e^(-x^2)). If it can't be exactly returned, then an exact answer is not possible mathematically, which leaves only approximation. Apache Commons uses several different approximation schemes, but all require the use of finite bounds for correctness.
The best way to get an appropriate answer is to repeatedly evaluate finite integrals, with ever increasing bounds, and compare the results. In pseudo-code, it would look something like this:
double DELTA = 10^-6//your error threshold here
double STEP_SIZE = 10.0;
double oldValue=Double.MAX_VALUE;
double newValue=oldValue;
double lowerBound=-10; //or whatever you want to start with--for (-infinity,1), I'd
//start with something like -10
double upperBound=1;
do{
oldValue = newValue;
lowerBound-= STEP_SIZE;
newValue = integrate(lowerBound,upperBound); //perform your integration methods here
}while(Math.abs(newValue-oldValue)>DELTA);
Eventually, if the integral converges, then you will get enough of the important stuff in that widening the bounds further will not produce meaningful information.
A word to the wise though: this kind of thing can be explosively bad if the integral doesn't converge. In that case, one of two situations can occur: Either your termination condition is never satisfied and you fall into an infinite loop, or the value of the integral oscillates indefinitely around a value, which may cause your termination condition to be incorrectly satisfied (giving incorrect results).
To avoid the first, the best way is to put in some maximum number of steps to take before returning--doing this should stop the potentially infinite loop that can result.
To avoid the second, hope it doesn't happen or prove that the integral must converge (three cheers for Calculus 2, anyone? ;-)).
To answer your question formally, no, there are no other such ways to perform your computation in java. In fact, there are no guaranteed ways of doing it in any language, with any algorithm--the mathematics just don't work out the way we want them to. However, in practice, a lot (though by no means all!) of the practical integrals do converge; its been my experience that only about ~20 iterations will give you an approximation of reasonable accuracy, and Apache should be fast enough to handle that without taking absurdly long.
Suppose you are integrating f(x) over -infinity to 1, then substitute x = 2 - 1/(1-t), and evaluate over the range 0 .. 1. Note check a maths text for how to do the substition, I'm a little rusty and its too late here.
The result of a numerical integration where one of the bounds is infinity has a good chance to be infinity as well. And it will take infinite time to prove it ;)
So you either find an equivalent formula (using real math) that can be computed or your replace the lower bound with a reasonable big negative value and look, if you can get a good estimation for the integral.
If Apache Commons Math could do numerical integration for integrals with infinite bounds in finite time, they wouldn't give it away for free ;-)
Maybe it's your algorithm.
If you're doing something naive like Simpson's rule it's likely to take a very long time.
If you're using Gaussian or log quadrature you might have better luck.
What's the function you're trying to integrate, and what's the algorithm you're using?