in commons-numbers-complex/src/main/java/org/apache/commons/numbers/complex/Complex.java [1597:1688]
private static Complex sqrt(double real, double imaginary) {
// Handle NaN
if (Double.isNaN(real) || Double.isNaN(imaginary)) {
// Check for infinite
if (Double.isInfinite(imaginary)) {
return new Complex(Double.POSITIVE_INFINITY, imaginary);
}
if (Double.isInfinite(real)) {
if (real == Double.NEGATIVE_INFINITY) {
return new Complex(Double.NaN, Math.copySign(Double.POSITIVE_INFINITY, imaginary));
}
return new Complex(Double.POSITIVE_INFINITY, Double.NaN);
}
return NAN;
}
// Compute with positive values and determine sign at the end
final double x = Math.abs(real);
final double y = Math.abs(imaginary);
// Compute
final double t;
// This alters the implementation of Hull et al (1994) which used a standard
// precision representation of |z|: sqrt(x*x + y*y).
// This formula should use the same definition of the magnitude returned
// by Complex.abs() which is a high precision computation with scaling.
// Worry about overflow if 2 * (|z| + |x|) will overflow.
// Worry about underflow if |z| or |x| are sub-normal components.
if (inRegion(x, y, Double.MIN_NORMAL, SQRT_SAFE_UPPER)) {
// No over/underflow
t = Math.sqrt(2 * (abs(x, y) + x));
} else {
// Potential over/underflow. First check infinites and real/imaginary only.
// Check for infinite
if (isPosInfinite(y)) {
return new Complex(Double.POSITIVE_INFINITY, imaginary);
} else if (isPosInfinite(x)) {
if (real == Double.NEGATIVE_INFINITY) {
return new Complex(0, Math.copySign(Double.POSITIVE_INFINITY, imaginary));
}
return new Complex(Double.POSITIVE_INFINITY, Math.copySign(0, imaginary));
} else if (y == 0) {
// Real only
final double sqrtAbs = Math.sqrt(x);
if (real < 0) {
return new Complex(0, Math.copySign(sqrtAbs, imaginary));
}
return new Complex(sqrtAbs, imaginary);
} else if (x == 0) {
// Imaginary only. This sets the two components to the same magnitude.
// Note: In polar coordinates this does not happen:
// real = sqrt(abs()) * Math.cos(arg() / 2)
// imag = sqrt(abs()) * Math.sin(arg() / 2)
// arg() / 2 = pi/4 and cos and sin should both return sqrt(2)/2 but
// are different by 1 ULP.
final double sqrtAbs = Math.sqrt(y) * ONE_OVER_ROOT2;
return new Complex(sqrtAbs, Math.copySign(sqrtAbs, imaginary));
} else {
// Over/underflow.
// Full scaling is not required as this is done in the hypotenuse function.
// Keep the number as big as possible for maximum precision in the second sqrt.
// Note if we scale by an even power of 2, we can re-scale by sqrt of the number.
// a = sqrt(b)
// a = sqrt(b/4) * sqrt(4)
final double rescale;
final double sx;
final double sy;
if (Math.max(x, y) > SQRT_SAFE_UPPER) {
// Overflow. Scale down by 16 and rescale by sqrt(16).
sx = x / 16;
sy = y / 16;
rescale = 4;
} else {
// Sub-normal numbers. Make them normal by scaling by 2^54,
// i.e. more than the mantissa digits, and rescale by sqrt(2^54) = 2^27.
sx = x * 0x1.0p54;
sy = y * 0x1.0p54;
rescale = 0x1.0p-27;
}
t = rescale * Math.sqrt(2 * (abs(sx, sy) + sx));
}
}
if (real >= 0) {
return new Complex(t / 2, imaginary / t);
}
return new Complex(y / t, Math.copySign(t / 2, imaginary));
}