In short:

- Math.hypot is required in Complex to compute sqrt(x^2 + y^2) without 
over/underflow to 1 ULP precision.
- Complex also requires the same computation without the sqrt or over/underflow 
protection
- I found the reference for Math.hypot and reimplemented the function
- My port is 4x faster than Math.hypot and the same accuracy (or better)
- I will add this as a private internal method in Complex
- The source for the port requires the original licence is maintained

The question is where to put the notice of the original license? Copying from 
commons RNG it would be in the source method javadoc and also in all 
LICENSE.txt files through the multi-module project. This seems excessive. I 
thought perhaps to include it only in numbers parent and then the complex 
module where it applies.


Background

The complex class uses the Math.hypot(double, double) function to determine the 
absolute value of a complex number x + iy as sqrt(x^2 + y^2) without 
over/underflow to 1 ULP precision. This is used directly (in sqrt() and abs()) 
but also without the square root to compute x^2 + y^2 in the log() function. 
These functions also perform over/underflow protection and so ideally just 
require access to the same formula for high precision x^2 + y^2. This would 
enable consistency across the different methods that use the absolute of the 
complex number. Currently the hypot function is very slow in the Complex JMH 
benchmark so I looked into hypot.

This function is known to be slow [1] pre-Java 9 which I was using for 
benchmarking. I found that in Java 9 the code was changed from calling a native 
function to an implementation in Java of the "Freely Distributable Maths 
Library" [2]. The JMH benchmark for complex shows an improvement between Java 8 
and 9 of about 7-fold speed increase. However this does not allow access to the 
same computation without the square root. The source code for fdlibm has a 
permission license [3] so I have implemented a port that allows separation of 
the x^2 + y^2 computation from the sqrt and the overflow protection.

In testing my ported version I found cases where it was more accurate than the 
Java reference, but none where it was less accurate. I attribute this to the 
different implementation of splitting a number into parts for high precision 
that is different in my port from the original. I used the split that is 
already present in Complex. I tested side-by-side an alternative that was 
closer to the method from fdlibm and it was a bit slower (in Java) and the same 
accuracy as the JDK reference. So I assume that the JDK reference has stuck 
exactly to the fdlibm code. I also found my port to be 4x faster than the Java 
reference. This may require more investigation but for now I would like to put 
my port into Complex as an internal method. Note the method is different from 
the commons FastMath.hypot implementation which does not compute the result to 
1 ULP. I will add this to the JMH benchmark for reference so we have 
Math.hypot, FastMath.hypot and the hypot method within Complex.

The licence for fdlibm is shown in [3]. This states that code can be 
copied/modified as long as the original notice is maintained. In commons RNG 
the licence for the port of the MersenneTwister is placed in the Java source 
file and in all LICENSE.txt files through the multi-module project. So should I 
do the same for numbers or just put the license into the complex module?


[1] https://stackoverflow.com/questions/3764978/why-hypot-function-is-so-slow 
<https://stackoverflow.com/questions/3764978/why-hypot-function-is-so-slow>

[2] https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7130085 
<https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7130085>

[3] https://www.netlib.org/fdlibm/e_hypot.c 
<https://www.netlib.org/fdlibm/e_hypot.c>

Reply via email to