Skip to content

Commit cdc2093

Browse files
committed
[GR-24066] Fix ComplexBuiltin.__abs__ performance
PullRequest: graalpython/1019
2 parents 5c3a945 + 343ca2f commit cdc2093

File tree

1 file changed

+130
-15
lines changed
  • graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex

1 file changed

+130
-15
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/complex/ComplexBuiltins.java

Lines changed: 130 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
import static com.oracle.graal.python.nodes.SpecialMethodNames.__STR__;
6464
import static com.oracle.graal.python.nodes.SpecialMethodNames.__SUB__;
6565
import static com.oracle.graal.python.nodes.SpecialMethodNames.__TRUEDIV__;
66-
import static com.oracle.graal.python.runtime.exception.PythonErrorType.OverflowError;
6766

6867
import java.util.List;
6968

@@ -104,27 +103,143 @@ public abstract static class AbsNode extends PythonUnaryBuiltinNode {
104103

105104
public abstract double executeDouble(Object arg);
106105

107-
public static AbsNode create() {
108-
return ComplexBuiltinsFactory.AbsNodeFactory.create();
109-
}
110-
111106
@Specialization
112107
double abs(PComplex c) {
113-
if (!Double.isFinite(c.getReal()) || !Double.isFinite(c.getImag())) {
114-
if (Double.isInfinite(c.getReal())) {
115-
return Math.abs(c.getReal());
116-
} else if (Double.isInfinite(c.getImag())) {
117-
return Math.abs(c.getImag());
108+
double x = c.getReal();
109+
double y = c.getImag();
110+
if (Double.isInfinite(x) || Double.isInfinite(y)) {
111+
return Double.POSITIVE_INFINITY;
112+
} else if (Double.isNaN(x) || Double.isNaN(y)) {
113+
return Double.NaN;
114+
} else {
115+
116+
final int expX = getExponent(x);
117+
final int expY = getExponent(y);
118+
if (expX > expY + 27) {
119+
// y is neglectible with respect to x
120+
return abs(x);
121+
} else if (expY > expX + 27) {
122+
// x is neglectible with respect to y
123+
return abs(y);
118124
} else {
119-
return Double.NaN;
125+
126+
// find an intermediate scale to avoid both overflow and
127+
// underflow
128+
final int middleExp = (expX + expY) / 2;
129+
130+
// scale parameters without losing precision
131+
final double scaledX = scalb(x, -middleExp);
132+
final double scaledY = scalb(y, -middleExp);
133+
134+
// compute scaled hypotenuse
135+
final double scaledH = Math.sqrt(scaledX * scaledX + scaledY * scaledY);
136+
137+
// remove scaling
138+
double r = scalb(scaledH, middleExp);
139+
if (Double.isInfinite(r)) {
140+
throw raise(PythonErrorType.OverflowError, ErrorMessages.ABSOLUTE_VALUE_TOO_LARGE);
141+
}
142+
return r;
120143
}
121144
}
122-
double r = Math.hypot(c.getReal(), c.getImag());
123-
if (Double.isInfinite(r)) {
124-
throw raise(OverflowError, ErrorMessages.ABSOLUTE_VALUE_TOO_LARGE);
145+
}
146+
147+
private static final long MASK_NON_SIGN_LONG = 0x7fffffffffffffffL;
148+
149+
static double abs(double x) {
150+
return Double.longBitsToDouble(MASK_NON_SIGN_LONG & Double.doubleToRawLongBits(x));
151+
}
152+
153+
static double scalb(final double d, final int n) {
154+
155+
// first simple and fast handling when 2^n can be represented using
156+
// normal numbers
157+
if ((n > -1023) && (n < 1024)) {
158+
return d * Double.longBitsToDouble(((long) (n + 1023)) << 52);
159+
}
160+
161+
// handle special cases
162+
if (Double.isNaN(d) || Double.isInfinite(d) || (d == 0)) {
163+
return d;
125164
}
126-
return r;
165+
if (n < -2098) {
166+
return (d > 0) ? 0.0 : -0.0;
167+
}
168+
if (n > 2097) {
169+
return (d > 0) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
170+
}
171+
172+
// decompose d
173+
final long bits = Double.doubleToRawLongBits(d);
174+
final long sign = bits & 0x8000000000000000L;
175+
int exponent = ((int) (bits >>> 52)) & 0x7ff;
176+
long mantissa = bits & 0x000fffffffffffffL;
177+
178+
// compute scaled exponent
179+
int scaledExponent = exponent + n;
180+
181+
if (n < 0) {
182+
// we are really in the case n <= -1023
183+
if (scaledExponent > 0) {
184+
// both the input and the result are normal numbers, we only
185+
// adjust the exponent
186+
return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52) | mantissa);
187+
} else if (scaledExponent > -53) {
188+
// the input is a normal number and the result is a subnormal
189+
// number
190+
191+
// recover the hidden mantissa bit
192+
mantissa = mantissa | (1L << 52);
193+
194+
// scales down complete mantissa, hence losing least significant
195+
// bits
196+
final long mostSignificantLostBit = mantissa & (1L << (-scaledExponent));
197+
mantissa = mantissa >>> (1 - scaledExponent);
198+
if (mostSignificantLostBit != 0) {
199+
// we need to add 1 bit to round up the result
200+
mantissa++;
201+
}
202+
return Double.longBitsToDouble(sign | mantissa);
203+
204+
} else {
205+
// no need to compute the mantissa, the number scales down to 0
206+
return (sign == 0L) ? 0.0 : -0.0;
207+
}
208+
} else {
209+
// we are really in the case n >= 1024
210+
if (exponent == 0) {
211+
212+
// the input number is subnormal, normalize it
213+
while ((mantissa >>> 52) != 1) {
214+
mantissa = mantissa << 1;
215+
--scaledExponent;
216+
}
217+
++scaledExponent;
218+
mantissa = mantissa & 0x000fffffffffffffL;
219+
220+
if (scaledExponent < 2047) {
221+
return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52) | mantissa);
222+
} else {
223+
return (sign == 0L) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
224+
}
225+
226+
} else if (scaledExponent < 2047) {
227+
return Double.longBitsToDouble(sign | (((long) scaledExponent) << 52) | mantissa);
228+
} else {
229+
return (sign == 0L) ? Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
230+
}
231+
}
232+
}
233+
234+
static int getExponent(final double d) {
235+
// NaN and Infinite will return 1024 anywho so can use raw bits
236+
return (int) ((Double.doubleToRawLongBits(d) >>> 52) & 0x7ff) - 1023;
237+
}
238+
239+
public static AbsNode create() {
240+
return ComplexBuiltinsFactory.AbsNodeFactory.create();
127241
}
242+
128243
}
129244

130245
@Builtin(name = __ADD__, minNumOfPositionalArgs = 2)

0 commit comments

Comments
 (0)