Skip to content

Commit df464bc

Browse files
committed
Remove checks from math where java.lang.Math suffices.
java.lang.Math often does the right thing for Python, and never raises. In some cases we need to convert a nan or inf returned, into a Python domain or range error.
1 parent f1fa33f commit df464bc

File tree

1 file changed

+98
-116
lines changed

1 file changed

+98
-116
lines changed

src/org/python/modules/math.java

Lines changed: 98 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,7 @@ public static double expm1(double v) {
6767
}
6868

6969
public static double acos(double v) {
70-
if (isinf(v)) {
71-
throwMathDomainValueError();
72-
}
73-
if (isnan(v)) {
74-
return v;
75-
}
76-
return Math.acos(v);
70+
return exceptNaN(Math.acos(v), v);
7771
}
7872

7973
/**
@@ -108,13 +102,7 @@ public static double acosh(double y) {
108102
}
109103

110104
public static double asin(double v) {
111-
if (isinf(v)) {
112-
throwMathDomainValueError();
113-
}
114-
if (isnan(v)) {
115-
return v;
116-
}
117-
return Math.asin(v);
105+
return exceptNaN(Math.asin(v), v);
118106
}
119107

120108
public static double asinh(double v) {
@@ -147,17 +135,14 @@ public static double asinh(double v) {
147135
}
148136

149137
public static double atan(double v) {
150-
if (isnan(v)) {
151-
return v;
152-
}
153-
return Math.atan(v);
138+
return exceptNaN(Math.atan(v), v);
154139
}
155140

156141
/**
157142
* Compute <i>tanh<sup>-1</sup>y</i>.
158143
*
159144
* @param y
160-
* @return x such that <i>tanh x = y</i>
145+
* @return <i>x</i> such that <i>tanh x = y</i>
161146
*/
162147
public static double atanh(double y) {
163148
double absy = Math.abs(y);
@@ -180,44 +165,26 @@ public static double ceil(PyObject v) {
180165
}
181166

182167
public static double ceil(double v) {
183-
if (isnan(v) || isinf(v)) {
184-
return v;
185-
}
186168
return Math.ceil(v);
187169
}
188170

189171
public static double cos(double v) {
190-
if (isinf(v)) {
191-
throwMathDomainValueError();
192-
}
193-
if (isnan(v)) {
194-
return NAN;
195-
}
196-
return Math.cos(v);
172+
return exceptNaN(Math.cos(v), v);
197173
}
198174

199175
public static double cosh(double v) {
200-
return Math.cosh(v);
176+
return exceptInf(Math.cosh(v), v);
201177
}
202178

203179
public static double exp(double v) {
204-
if (isninf(v)) {
205-
return ZERO;
206-
}
207-
if (isnan(v) || isinf(v)) {
208-
return v;
209-
}
210-
return check(Math.exp(v));
180+
return exceptInf(Math.exp(v), v);
211181
}
212182

213183
public static double floor(PyObject v) {
214184
return floor(v.asDouble());
215185
}
216186

217187
public static double floor(double v) {
218-
if (isnan(v) || isinf(v)) {
219-
return v;
220-
}
221188
return Math.floor(v);
222189
}
223190

@@ -232,10 +199,7 @@ public static double log(PyObject v, PyObject base) {
232199
} else {
233200
doubleValue = log(v.asDouble());
234201
}
235-
if (base != null) {
236-
return check(applyLoggedBase(doubleValue, base));
237-
}
238-
return doubleValue;
202+
return (base == null) ? doubleValue : applyLoggedBase(doubleValue, base);
239203
}
240204

241205
public static double pow(double v, double w) {
@@ -254,7 +218,7 @@ public static double pow(double v, double w) {
254218
} else if (w > ZERO || ispinf(w)) {
255219
return ZERO;
256220
} else {
257-
throwMathDomainValueError();
221+
throw mathDomainError();
258222
}
259223
}
260224
if (isninf(v)) {
@@ -301,7 +265,7 @@ public static double pow(double v, double w) {
301265
}
302266
}
303267
if (v < ZERO && !isIntegral(w)) {
304-
throwMathDomainValueError();
268+
throw mathDomainError();
305269
}
306270
return Math.pow(v, w);
307271
}
@@ -311,60 +275,39 @@ public static double sin(PyObject v) {
311275
}
312276

313277
public static double sin(double v) {
314-
if (isinf(v)) {
315-
throwMathDomainValueError();
316-
}
317-
if (isnan(v)) {
318-
return v;
319-
}
320-
return Math.sin(v);
278+
return exceptNaN(Math.sin(v), v);
321279
}
322280

323281
public static double sqrt(PyObject v) {
324282
return sqrt(v.asDouble());
325283
}
326284

327285
public static double sqrt(double v) {
328-
if (isnan(v)) {
329-
return v;
330-
}
331-
if (ispinf(v)) {
332-
return v;
333-
}
334-
if (isninf(v) || v < MINUS_ZERO) {
335-
throwMathDomainValueError();
336-
}
337-
return Math.sqrt(v);
286+
return exceptNaN(Math.sqrt(v), v);
338287
}
339288

340289
public static double tan(double v) {
341-
if (isnan(v)) {
342-
return NAN;
343-
}
344-
if (isinf(v)) {
345-
throw Py.ValueError("math domain error");
346-
}
347-
return Math.tan(v);
290+
return exceptNaN(Math.tan(v), v);
348291
}
349292

350293
public static double log10(PyObject v) {
351294
if (v instanceof PyLong) {
352295
int exp[] = new int[1];
353296
double x = ((PyLong)v).scaledDoubleValue(exp);
354297
if (x <= ZERO) {
355-
throwMathDomainValueError();
298+
throw mathDomainError();
356299
}
357300
return log10(x) + (exp[0] * EIGHT) * log10(TWO);
358301
}
359302
return log10(v.asDouble());
360303
}
361304

362305
public static double sinh(double v) {
363-
return Math.sinh(v);
306+
return exceptInf(Math.sinh(v), v);
364307
}
365308

366309
public static double tanh(double v) {
367-
return Math.tanh(v);
310+
return exceptInf(Math.tanh(v), v);
368311
}
369312

370313
public static double fabs(double v) {
@@ -379,10 +322,10 @@ public static double fmod(double v, double w) {
379322
return v;
380323
}
381324
if (w == ZERO) {
382-
throwMathDomainValueError();
325+
throw mathDomainError();
383326
}
384327
if (isinf(v) && w == ONE) {
385-
throwMathDomainValueError();
328+
throw mathDomainError();
386329
}
387330
return v % w;
388331
}
@@ -468,11 +411,12 @@ public static double hypot(double x, double y) {
468411
}
469412

470413
public static double radians(double v) {
471-
return check(Math.toRadians(v));
414+
return Math.toRadians(v);
472415
}
473416

474417
public static double degrees(double v) {
475-
return check(Math.toDegrees(v));
418+
// Note that this does not raise overflow in Python: 1e307 -> inf as in Java.
419+
return Math.toDegrees(v);
476420
}
477421

478422
public static boolean isnan(double v) {
@@ -501,24 +445,27 @@ public static double copysign(double v, double w) {
501445
public static PyLong factorial(double v) {
502446
if (v == ZERO || v == ONE) {
503447
return new PyLong(1);
448+
} else if (v < ZERO || isnan(v) || isinf(v)) {
449+
throw mathDomainError();
450+
} else if (!isIntegral(v)) {
451+
throw mathDomainError();
452+
} else {
453+
// long input should be big enough :-)
454+
long value = (long)v;
455+
BigInteger bi = new BigInteger(Long.toString(value));
456+
for (long l = value - 1; l > 1; l--) {
457+
bi = bi.multiply(new BigInteger(Long.toString(l)));
458+
}
459+
return new PyLong(bi);
504460
}
505-
if (v < ZERO || isnan(v) || isinf(v)) {
506-
throwMathDomainValueError();
507-
}
508-
if (!isIntegral(v)) {
509-
throwMathDomainValueError();
510-
}
511-
// long input should be big enough :-)
512-
long value = (long)v;
513-
BigInteger bi = new BigInteger(Long.toString(value));
514-
for (long l = value - 1; l > 1; l--) {
515-
bi = bi.multiply(new BigInteger(Long.toString(l)));
516-
}
517-
return new PyLong(bi);
518461
}
519462

520463
public static double log1p(double v) {
521-
return log(ONE + v);
464+
if (v <= -1.) {
465+
throw mathDomainError();
466+
} else {
467+
return Math.log1p(v);
468+
}
522469
}
523470

524471
public static double fsum(final PyObject iterable) {
@@ -530,7 +477,7 @@ private static double calculateLongLog(PyLong v) {
530477
int exp[] = new int[1];
531478
double x = v.scaledDoubleValue(exp);
532479
if (x <= ZERO) {
533-
throwMathDomainValueError();
480+
throw mathDomainError();
534481
}
535482
return log(x) + (exp[0] * EIGHT) * log(TWO);
536483
}
@@ -542,27 +489,23 @@ private static double applyLoggedBase(double loggedValue, PyObject base) {
542489
} else {
543490
loggedBase = log(base.asDouble());
544491
}
545-
return check(loggedValue / loggedBase);
492+
return loggedValue / loggedBase;
546493
}
547494

548495
private static double log(double v) {
549-
if (isninf(v) || v <= ZERO) {
550-
throwMathDomainValueError();
551-
}
552-
if (isinf(v) || isnan(v)) {
553-
return v;
496+
if (v <= 0.) {
497+
throw mathDomainError();
498+
} else {
499+
return Math.log(v);
554500
}
555-
return Math.log(v);
556501
}
557502

558503
private static double log10(double v) {
559-
if (isninf(v)) {
560-
throwMathDomainValueError();
561-
}
562-
if (isinf(v) || isnan(v)) {
563-
return v;
504+
if (v <= 0.) {
505+
throw mathDomainError();
506+
} else {
507+
return Math.log10(v);
564508
}
565-
return Math.log10(v);
566509
}
567510

568511
private static boolean isninf(double v) {
@@ -606,25 +549,64 @@ private static PyException mathRangeError() {
606549
return Py.OverflowError("math range error");
607550
}
608551

609-
private static void throwMathDomainValueError() {
610-
throw Py.ValueError("math domain error");
611-
}
612-
613-
private static double check(double v) {
614-
if (isnan(v)) {
615-
throwMathDomainValueError();
616-
}
552+
private static double checkOverflow(double v) {
617553
if (isinf(v)) {
618554
throw Py.OverflowError("math range error");
619555
}
620556
return v;
621557
}
622558

623-
private static double checkOverflow(double v) {
624-
if (isinf(v)) {
559+
/**
560+
* Turn a <code>NaN</code> result into a thrown <code>ValueError</code>, a math domain error, if
561+
* the original argument was not itself <code>NaN</code>. Use as:
562+
*
563+
* <pre>
564+
* public static double asin(double v) { return exceptNaN(Math.asin(v), v); }
565+
* </pre>
566+
*
567+
* Note that the original function argument is also supplied to this method. Most Java math
568+
* library methods do exactly what we need for Python, but some return {@value Double#NaN} when
569+
* Python should raise <code>ValueError</code>. This is a brief way to change that.
570+
*
571+
* @param result to return (if we return)
572+
* @param arg to include in check
573+
* @return result if <code>arg</code> was <code>NaN</code> or <code>result</code> was not
574+
* <code>NaN</code>
575+
* @throws PyException (ValueError) if <code>result</code> was <code>NaN</code> and
576+
* <code>arg</code> was not <code>NaN</code>
577+
*/
578+
private static double exceptNaN(double result, double arg) throws PyException {
579+
if (Double.isNaN(result) && !Double.isNaN(arg)) {
580+
throw mathDomainError();
581+
} else {
582+
return result;
583+
}
584+
}
585+
586+
/**
587+
* Turn an infinite result into a thrown <code>OverflowError</code>, a math range error, if the
588+
* original argument was not itself infinite. Use as:
589+
*
590+
* <pre>
591+
* public static double cosh(double v) { return exceptInf( Math.cosh(v), v); }
592+
* </pre>
593+
*
594+
* Note that the original function argument is also supplied to this method. Most Java math
595+
* library methods do exactly what we need for Python, but some return an infinity when Python
596+
* should raise <code>OverflowError</code>. This is a brief way to change that.
597+
*
598+
* @param result to return (if we return)
599+
* @param arg to include in check
600+
* @return result if <code>arg</code> was infinite or <code>result</code> was not infinite
601+
* @throws PyException (ValueError) if <code>result</code> was infinite and <code>arg</code> was
602+
* not infinite
603+
*/
604+
private static double exceptInf(double result, double arg) {
605+
if (Double.isInfinite(result) && !Double.isInfinite(arg)) {
625606
throw Py.OverflowError("math range error");
607+
} else {
608+
return result;
626609
}
627-
return v;
628610
}
629611

630612
/**

0 commit comments

Comments
 (0)