Skip to content

Commit cf61d68

Browse files
committed
Use more native math functions
1 parent 7ae4492 commit cf61d68

File tree

5 files changed

+162
-54
lines changed

5 files changed

+162
-54
lines changed

user/super/com/google/gwt/emul/java/lang/Math.java

Lines changed: 47 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,6 @@
2626
* Math utility methods and constants.
2727
*/
2828
public final class Math {
29-
// The following methods are not implemented because JS doesn't provide the
30-
// necessary pieces:
31-
// public static double ulp (double x)
32-
// public static float ulp (float x)
33-
// public static int getExponent (double d)
34-
// public static int getExponent (float f)
3529

3630
public static final double E = 2.7182818284590452354;
3731
public static final double PI = 3.14159265358979323846;
@@ -87,9 +81,8 @@ public static long addExact(long x, long y) {
8781
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.atan2")
8882
public static native double atan2(double y, double x);
8983

90-
public static double cbrt(double x) {
91-
return x == 0 || !Double.isFinite(x) ? x : pow(x, 1.0 / 3.0);
92-
}
84+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.cbrt")
85+
public static native double cbrt(double x);
9386

9487
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.ceil")
9588
public static native double ceil(double x);
@@ -109,9 +102,8 @@ public static float copySign(float magnitude, float sign) {
109102
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.cos")
110103
public static native double cos(double x);
111104

112-
public static double cosh(double x) {
113-
return (exp(x) + exp(-x)) / 2;
114-
}
105+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.cosh")
106+
public static native double cosh(double x);
115107

116108
public static int decrementExact(int x) {
117109
checkCriticalArithmetic(x != Integer.MIN_VALUE);
@@ -175,6 +167,25 @@ public static int getExponent(float v) {
175167
return ((JsUtils.floatToRawIntBits(v) >> 23) & 255) - Float.MAX_EXPONENT;
176168
}
177169

170+
public static double ulp(double v) {
171+
if (!Double.isFinite(v)) {
172+
return Math.abs(v);
173+
}
174+
int exponent = Math.getExponent(v);
175+
if (exponent == -1023) {
176+
return Double.MIN_VALUE;
177+
}
178+
return Math.pow(2, exponent - 52);
179+
}
180+
181+
public static float ulp(float v) {
182+
int exponent = Math.getExponent(v);
183+
if (exponent == -Float.MAX_EXPONENT) {
184+
return Float.MIN_VALUE;
185+
}
186+
return (float) Math.pow(2, exponent - 23);
187+
}
188+
178189
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.hypot")
179190
public static native double hypot(double x, double y);
180191

@@ -191,9 +202,8 @@ public static long incrementExact(long x) {
191202
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.log")
192203
public static native double log(double x);
193204

194-
public static double log10(double x) {
195-
return log(x) * NativeMath.LOG10E;
196-
}
205+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.log10")
206+
public static native double log10(double x);
197207

198208
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.log1p")
199209
public static native double log1p(double x);
@@ -224,12 +234,28 @@ public static long min(long x, long y) {
224234
return x < y ? x : y;
225235
}
226236

237+
public static long multiplyFull(int x, int y) {
238+
return (long) x * (long) y;
239+
}
240+
227241
public static int multiplyExact(int x, int y) {
228242
double r = (double) x * (double) y;
229243
checkCriticalArithmetic(isSafeIntegerRange(r));
230244
return (int) r;
231245
}
232246

247+
public static long multiplyExact(long x, int y) {
248+
if (y == -1) {
249+
return negateExact(x);
250+
}
251+
if (y == 0) {
252+
return 0;
253+
}
254+
long r = x * y;
255+
checkCriticalArithmetic(r / y == x);
256+
return r;
257+
}
258+
233259
public static long multiplyExact(long x, long y) {
234260
if (y == -1) {
235261
return negateExact(x);
@@ -311,13 +337,8 @@ public static float scalb(float f, int scaleFactor) {
311337
return (float) scalb((double) f, scaleFactor);
312338
}
313339

314-
public static double signum(double d) {
315-
if (d == 0 || Double.isNaN(d)) {
316-
return d;
317-
} else {
318-
return d < 0 ? -1 : 1;
319-
}
320-
}
340+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.sign")
341+
public static native double signum(double d);
321342

322343
public static float signum(float f) {
323344
return (float) signum((double) f);
@@ -326,28 +347,17 @@ public static float signum(float f) {
326347
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.sin")
327348
public static native double sin(double x);
328349

329-
public static double sinh(double x) {
330-
return x == 0.0 ? x : (exp(x) - exp(-x)) / 2;
331-
}
350+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.sinh")
351+
public static native double sinh(double x);
332352

333353
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.sqrt")
334354
public static native double sqrt(double x);
335355

336356
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.tan")
337357
public static native double tan(double x);
338358

339-
public static double tanh(double x) {
340-
if (x == 0.0) {
341-
// -0.0 should return -0.0.
342-
return x;
343-
}
344-
345-
double e2x = exp(2 * x);
346-
if (Double.isInfinite(e2x)) {
347-
return 1;
348-
}
349-
return (e2x - 1) / (e2x + 1);
350-
}
359+
@JsMethod(namespace = JsPackage.GLOBAL, name = "Math.tanh")
360+
public static native double tanh(double x);
351361

352362
public static double toDegrees(double x) {
353363
return x * PI_UNDER_180;
@@ -438,7 +448,6 @@ private static boolean isSafeIntegerRange(double value) {
438448

439449
@JsType(isNative = true, name = "Math", namespace = JsPackage.GLOBAL)
440450
private static class NativeMath {
441-
public static double LOG10E;
442451
public static native double round(double x);
443452
}
444453
}

user/test-super/com/google/gwt/emultest/super/com/google/gwt/emultest/java17/lang/MathTest.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,6 @@ public void testAbsExactWithFolding() {
4444
assertThrowsArithmetic(() -> Math.absExact(Long.MIN_VALUE));
4545
}
4646

47-
private <T> T hideFromCompiler(T value) {
48-
if (Math.random() < -1) {
49-
// Can never happen, but fools the compiler enough not to optimize this call.
50-
fail();
51-
}
52-
return value;
53-
}
54-
5547
private void assertThrowsArithmetic(Runnable check) {
5648
try {
5749
check.run();

user/test/com/google/gwt/emultest/java/lang/DoubleTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,8 @@ public void testDoubleConstants() {
161161
assertTrue(Double.MIN_VALUE < Double.MAX_VALUE);
162162
assertFalse(Double.NaN == Double.NaN);
163163
assertEquals(64, Double.SIZE);
164-
// jdk1.6 assertEquals(Math.getExponent(Double.MAX_VALUE),
165-
// Double.MAX_EXPONENT);
166-
// jdk1.6 assertEquals(Math.getExponent(Double.MIN_NORMAL),
167-
// Double.MIN_EXPONENT);
164+
assertEquals(Math.getExponent(Double.MAX_VALUE), Double.MAX_EXPONENT);
165+
assertEquals(Math.getExponent(Double.MIN_NORMAL), Double.MIN_EXPONENT);
168166
// issue 8073 - used to fail in prod mode
169167
assertFalse(Double.isInfinite(Double.NaN));
170168
}

user/test/com/google/gwt/emultest/java8/lang/MathTest.java

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,45 @@ public void testMultiplyExactLongs() {
225225
}
226226
}
227227

228+
public void testMultiplyExactLongInt() {
229+
for (long a : ALL_LONG_CANDIDATES) {
230+
for (int b : ALL_INTEGER_CANDIDATES) {
231+
BigInteger expectedResult = BigInteger.valueOf(a).multiply(BigInteger.valueOf(b));
232+
boolean expectedSuccess = fitsInLong(expectedResult);
233+
try {
234+
assertEquals(a * b, Math.multiplyExact(a, b));
235+
assertTrue(expectedSuccess);
236+
} catch (ArithmeticException e) {
237+
assertFalse(expectedSuccess);
238+
}
239+
}
240+
}
241+
}
242+
243+
public void testMultiplyFull() {
244+
assertEquals(1L, Long.MAX_VALUE
245+
- Math.multiplyFull(Integer.MAX_VALUE, Integer.MAX_VALUE) * 2 - 4L * Integer.MAX_VALUE);
246+
assertEquals(-1L, Long.MAX_VALUE
247+
- 2 * Math.multiplyFull(Integer.MIN_VALUE, Integer.MIN_VALUE));
248+
249+
assertEquals(1L, hideFromCompiler(Long.MAX_VALUE)
250+
- Math.multiplyFull(Integer.MAX_VALUE, Integer.MAX_VALUE) * 2 - 4L * Integer.MAX_VALUE);
251+
assertEquals(-1L, hideFromCompiler(Long.MAX_VALUE)
252+
- 2 * Math.multiplyFull(Integer.MIN_VALUE, Integer.MIN_VALUE));
253+
}
254+
255+
public void testCbrt() {
256+
assertEquals(-2, Math.cbrt(-8), EPS);
257+
assertEquals(Double.POSITIVE_INFINITY, Math.cbrt(Double.POSITIVE_INFINITY), EPS);
258+
assertEquals(Double.NEGATIVE_INFINITY, Math.cbrt(Double.NEGATIVE_INFINITY), EPS);
259+
260+
assertEquals(-2, Math.cbrt(hideFromCompiler(-8)), EPS);
261+
assertEquals(Double.POSITIVE_INFINITY,
262+
Math.cbrt(hideFromCompiler(Double.POSITIVE_INFINITY)), EPS);
263+
assertEquals(Double.NEGATIVE_INFINITY,
264+
Math.cbrt(hideFromCompiler(Double.NEGATIVE_INFINITY)), EPS);
265+
}
266+
228267
public void testNegateExact() {
229268
for (int a : ALL_INTEGER_CANDIDATES) {
230269
BigInteger expectedResult = BigInteger.valueOf(a).negate();
@@ -381,6 +420,78 @@ public void testIEEEremainder() {
381420
assertTrue(Double.isNaN(Math.IEEEremainder(hideFromCompiler(Double.NaN), Double.NaN)));
382421
}
383422

423+
public void testUlpWithFolding() {
424+
double ulpEps = 1e-50;
425+
assertEquals(2.220446049250313E-16, Math.ulp(1.0), ulpEps);
426+
assertEquals(2.220446049250313E-16, Math.ulp(-1.0), ulpEps);
427+
assertEquals(4.440892098500626E-16, Math.ulp(2.0), ulpEps);
428+
assertEquals(4.440892098500626E-16, Math.ulp(-2.0), ulpEps);
429+
assertEquals(4.440892098500626E-16, Math.ulp(3.0), ulpEps);
430+
assertEquals(4.440892098500626E-16, Math.ulp(-3.0), ulpEps);
431+
assertEquals(1.99584030953472E292, Math.ulp(Double.MAX_VALUE), EPS);
432+
assertEquals(1.99584030953472E292, Math.ulp(-Double.MAX_VALUE), EPS);
433+
assertEquals(Double.POSITIVE_INFINITY, Math.ulp(Double.NEGATIVE_INFINITY), EPS);
434+
assertEquals(Double.POSITIVE_INFINITY, Math.ulp(Double.POSITIVE_INFINITY), EPS);
435+
assertTrue(Double.isNaN(Math.ulp(Double.NaN)));
436+
assertEquals(Double.MIN_VALUE, Math.ulp(Double.MIN_NORMAL), 0);
437+
assertEquals(Double.MIN_VALUE, Math.ulp(-Double.MIN_NORMAL), 0);
438+
assertEquals(Double.MIN_VALUE, Math.ulp(0.0), 0);
439+
assertEquals(Double.MIN_VALUE, Math.ulp(-0.0), 0);
440+
assertEquals(Double.MIN_VALUE, Math.ulp(8.289046E-317), 0);
441+
assertEquals(Double.MIN_VALUE, Math.ulp(-8.289046E-317), 0);
442+
443+
assertEquals(1.1920929E-7f, Math.ulp(1.0f), EPS);
444+
assertEquals(1.1920929E-7f, Math.ulp(-1.0f), EPS);
445+
assertEquals(2.3841858E-7f, Math.ulp(2.0f), EPS);
446+
assertEquals(2.3841858E-7f, Math.ulp(-2.0f), EPS);
447+
assertEquals(2.3841858E-7f, Math.ulp(3.0f), EPS);
448+
assertEquals(2.3841858E-7f, Math.ulp(-3.0f), EPS);
449+
assertEquals(2.028241E31f, Math.ulp(Float.MAX_VALUE), EPS);
450+
assertEquals(2.028241E31f, Math.ulp(-Float.MAX_VALUE), EPS);
451+
assertEquals(Float.MIN_VALUE, Math.ulp(Float.MIN_VALUE), 0);
452+
assertEquals(Float.MIN_VALUE, Math.ulp(-Float.MIN_VALUE), 0);
453+
assertEquals(Float.MIN_VALUE, Math.ulp(Float.MIN_NORMAL), 0);
454+
assertEquals(Float.MIN_VALUE, Math.ulp(-Float.MIN_NORMAL), 0);
455+
assertEquals(Float.MIN_VALUE, Math.ulp(0.0f), 0);
456+
assertEquals(Float.MIN_VALUE, Math.ulp(-0.0f), 0);
457+
}
458+
459+
public void testUlp() {
460+
double ulpEps = 1e-50;
461+
assertEquals(2.220446049250313E-16, Math.ulp(hideFromCompiler(1.0)), ulpEps);
462+
assertEquals(2.220446049250313E-16, Math.ulp(hideFromCompiler(-1.0)), ulpEps);
463+
assertEquals(4.440892098500626E-16, Math.ulp(hideFromCompiler(2.0)), ulpEps);
464+
assertEquals(4.440892098500626E-16, Math.ulp(hideFromCompiler(-2.0)), ulpEps);
465+
assertEquals(4.440892098500626E-16, Math.ulp(hideFromCompiler(3.0)), ulpEps);
466+
assertEquals(4.440892098500626E-16, Math.ulp(hideFromCompiler(-3.0)), ulpEps);
467+
assertEquals(1.99584030953472E292, Math.ulp(hideFromCompiler(Double.MAX_VALUE)), EPS);
468+
assertEquals(1.99584030953472E292, Math.ulp(hideFromCompiler(-Double.MAX_VALUE)), EPS);
469+
assertEquals(Double.POSITIVE_INFINITY, Math.ulp(hideFromCompiler(Double.NEGATIVE_INFINITY)), EPS);
470+
assertEquals(Double.POSITIVE_INFINITY, Math.ulp(hideFromCompiler(Double.POSITIVE_INFINITY)), EPS);
471+
assertTrue(Double.isNaN(Math.ulp(hideFromCompiler(Double.NaN))));
472+
assertEquals(Double.MIN_VALUE, Math.ulp(hideFromCompiler(Double.MIN_NORMAL)), 0);
473+
assertEquals(Double.MIN_VALUE, Math.ulp(hideFromCompiler(-Double.MIN_NORMAL)), 0);
474+
assertEquals(Double.MIN_VALUE, Math.ulp(hideFromCompiler(0.0)), 0);
475+
assertEquals(Double.MIN_VALUE, Math.ulp(hideFromCompiler(-0.0)), 0);
476+
assertEquals(Double.MIN_VALUE, Math.ulp(hideFromCompiler(8.289046E-317)), 0);
477+
assertEquals(Double.MIN_VALUE, Math.ulp(hideFromCompiler(-8.289046E-317)), 0);
478+
479+
assertEquals(1.1920929E-7f, Math.ulp(hideFromCompiler(1.0f)), EPS);
480+
assertEquals(1.1920929E-7f, Math.ulp(hideFromCompiler(-1.0f)), EPS);
481+
assertEquals(2.3841858E-7f, Math.ulp(hideFromCompiler(2.0f)), EPS);
482+
assertEquals(2.3841858E-7f, Math.ulp(hideFromCompiler(-2.0f)), EPS);
483+
assertEquals(2.3841858E-7f, Math.ulp(hideFromCompiler(3.0f)), EPS);
484+
assertEquals(2.3841858E-7f, Math.ulp(hideFromCompiler(-3.0f)), EPS);
485+
assertEquals(2.028241E31f, Math.ulp(hideFromCompiler(Float.MAX_VALUE)), EPS);
486+
assertEquals(2.028241E31f, Math.ulp(hideFromCompiler(-Float.MAX_VALUE)), EPS);
487+
assertEquals(Float.MIN_VALUE, Math.ulp(hideFromCompiler(Float.MIN_VALUE)), 0);
488+
assertEquals(Float.MIN_VALUE, Math.ulp(hideFromCompiler(-Float.MIN_VALUE)), 0);
489+
assertEquals(Float.MIN_VALUE, Math.ulp(hideFromCompiler(Float.MIN_NORMAL)), 0);
490+
assertEquals(Float.MIN_VALUE, Math.ulp(hideFromCompiler(-Float.MIN_NORMAL)), 0);
491+
assertEquals(Float.MIN_VALUE, Math.ulp(hideFromCompiler(0.0f)), 0);
492+
assertEquals(Float.MIN_VALUE, Math.ulp(hideFromCompiler(-0.0f)), 0);
493+
}
494+
384495
public void testHypot() {
385496
assertEquals(5.0, Math.hypot(3.0, 4.0), EPS);
386497
assertEquals(Double.POSITIVE_INFINITY, Math.hypot(1, Double.NEGATIVE_INFINITY));

user/test/com/google/gwt/emultest/java8/util/DoubleSummaryStatisticsTest.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,13 @@ public void testAverageAndSumWithCompensation() throws Exception {
4848
double initial = 1.0d;
4949
long count = 100000;
5050

51-
// 'precision' is the hardcoded result of Math.ulp(initial) in JVM,
52-
// since GWT does not emulate Math.ulp().
53-
// This value represents the distance from 'initial' (1.0d) to the
51+
// The value of 'precision' represents the distance from 'initial' (1.0d) to the
5452
// previous/next double. If we add half or less of that distance/precision
5553
// to 'initial' then the result will be truncated to 'initial' again due to
5654
// floating point arithmetic rounding.
5755
// With Kahan summation such rounding errors are detected and compensated
5856
// so the summation result should (nearly) equal the expected sum.
59-
double precision = 2.220446049250313E-16;
57+
double precision = Math.ulp(initial);
6058
double value = precision / 2;
6159
double expectedSum = initial + (count * value);
6260
long expectedCount = count + 1;

0 commit comments

Comments
 (0)