Skip to content

Commit fcaf871

Browse files
committed
8302028: Port fdlibm atan2 to Java
Reviewed-by: bpb
1 parent 07e976a commit fcaf871

File tree

6 files changed

+607
-20
lines changed

6 files changed

+607
-20
lines changed

src/java.base/share/classes/java/lang/FdLibm.java

+110
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,116 @@ static double compute(double x) {
394394
}
395395
}
396396

397+
/**
398+
* Returns the angle theta from the conversion of rectangular
399+
* coordinates (x, y) to polar coordinates (r, theta).
400+
*
401+
* Method :
402+
* 1. Reduce y to positive by atan2(y,x)=-atan2(-y,x).
403+
* 2. Reduce x to positive by (if x and y are unexceptional):
404+
* ARG (x+iy) = arctan(y/x) ... if x > 0,
405+
* ARG (x+iy) = pi - arctan[y/(-x)] ... if x < 0,
406+
*
407+
* Special cases:
408+
*
409+
* ATAN2((anything), NaN ) is NaN;
410+
* ATAN2(NAN , (anything) ) is NaN;
411+
* ATAN2(+-0, +(anything but NaN)) is +-0 ;
412+
* ATAN2(+-0, -(anything but NaN)) is +-pi ;
413+
* ATAN2(+-(anything but 0 and NaN), 0) is +-pi/2;
414+
* ATAN2(+-(anything but INF and NaN), +INF) is +-0 ;
415+
* ATAN2(+-(anything but INF and NaN), -INF) is +-pi;
416+
* ATAN2(+-INF,+INF ) is +-pi/4 ;
417+
* ATAN2(+-INF,-INF ) is +-3pi/4;
418+
* ATAN2(+-INF, (anything but,0,NaN, and INF)) is +-pi/2;
419+
*
420+
* Constants:
421+
* The hexadecimal values are the intended ones for the following
422+
* constants. The decimal values may be used, provided that the
423+
* compiler will convert from decimal to binary accurately enough
424+
* to produce the hexadecimal values shown.
425+
*/
426+
static class Atan2 {
427+
private Atan2() {throw new UnsupportedOperationException();}
428+
429+
private static final double
430+
tiny = 1.0e-300,
431+
pi_o_4 = 0x1.921fb54442d18p-1, // 7.8539816339744827900E-01
432+
pi_o_2 = 0x1.921fb54442d18p0, // 1.5707963267948965580E+00
433+
pi_lo = 0x1.1a62633145c07p-53; // 1.2246467991473531772E-16
434+
435+
static double compute(double y, double x) {
436+
double z;
437+
int k, m, hx, hy, ix, iy;
438+
/*unsigned*/ int lx, ly;
439+
440+
hx = __HI(x);
441+
ix = hx & 0x7fff_ffff;
442+
lx = __LO(x);
443+
hy = __HI(y);
444+
iy = hy&0x7fff_ffff;
445+
ly = __LO(y);
446+
if (Double.isNaN(x) || Double.isNaN(y))
447+
return x + y;
448+
if (((hx - 0x3ff0_0000) | lx) == 0) // x = 1.0
449+
return StrictMath.atan(y);
450+
m = ((hy >> 31) & 1)|((hx >> 30) & 2); // 2*sign(x) + sign(y)
451+
452+
// when y = 0
453+
if ((iy | ly) == 0) {
454+
switch(m) {
455+
case 0:
456+
case 1: return y; // atan(+/-0, +anything) = +/-0
457+
case 2: return Math.PI + tiny; // atan(+0, -anything) = pi
458+
case 3: return -Math.PI - tiny; // atan(-0, -anything) = -pi
459+
}
460+
}
461+
// when x = 0
462+
if ((ix | lx) == 0) {
463+
return (hy < 0)? -pi_o_2 - tiny : pi_o_2 + tiny;
464+
}
465+
466+
// when x is INF
467+
if (ix == 0x7ff0_0000) {
468+
if (iy == 0x7ff0_0000) {
469+
switch(m) {
470+
case 0: return pi_o_4 + tiny; // atan(+INF, +INF)
471+
case 1: return -pi_o_4 - tiny; // atan(-INF, +INF)
472+
case 2: return 3.0*pi_o_4 + tiny; // atan(+INF, -INF)
473+
case 3: return -3.0*pi_o_4 - tiny; // atan(-INF, -INF)
474+
}
475+
} else {
476+
switch(m) {
477+
case 0: return 0.0; // atan(+..., +INF)
478+
case 1: return -0.0; // atan(-..., +INF)
479+
case 2: return Math.PI + tiny; // atan(+..., -INF)
480+
case 3: return -Math.PI - tiny; // atan(-..., -INF)
481+
}
482+
}
483+
}
484+
// when y is INF
485+
if (iy == 0x7ff0_0000) {
486+
return (hy < 0)? -pi_o_2 - tiny : pi_o_2 + tiny;
487+
}
488+
489+
// compute y/x
490+
k = (iy - ix) >> 20;
491+
if (k > 60) { // |y/x| > 2**60
492+
z = pi_o_2+0.5*pi_lo;
493+
} else if (hx < 0 && k < -60) { // |y|/x < -2**60
494+
z = 0.0;
495+
} else { // safe to do y/x
496+
z = StrictMath.atan(Math.abs(y/x));
497+
}
498+
switch (m) {
499+
case 0: return z; // atan(+, +)
500+
case 1: return -z; // atan(-, +)
501+
case 2: return Math.PI - (z - pi_lo); // atan(+, -)
502+
default: return (z - pi_lo) - Math.PI; // atan(-, -), case 3
503+
}
504+
}
505+
}
506+
397507
/**
398508
* cbrt(x)
399509
* Return cube root of x

src/java.base/share/classes/java/lang/StrictMath.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,9 @@ public static double rint(double a) {
547547
* in polar coordinates that corresponds to the point
548548
* (<i>x</i>,&nbsp;<i>y</i>) in Cartesian coordinates.
549549
*/
550-
public static native double atan2(double y, double x);
550+
public static double atan2(double y, double x) {
551+
return FdLibm.Atan2.compute(y, x);
552+
}
551553

552554
/**
553555
* Returns the value of the first argument raised to the power of the
+193-17
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2004, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2004, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -23,44 +23,220 @@
2323

2424
/*
2525
* @test
26-
* @bug 4984407
26+
* @bug 4984407 8302028
2727
* @summary Tests for {Math, StrictMath}.atan2
2828
*/
2929

3030
public class Atan2Tests {
3131
private Atan2Tests(){}
3232

33-
static int testAtan2Case(double input1, double input2, double expected) {
33+
public static void main(String... args) {
3434
int failures = 0;
35-
failures += Tests.test("StrictMath.atan2", input1, input2, StrictMath::atan2, expected);
36-
failures += Tests.test("Math.atan2", input1, input2, Math::atan2, expected);
3735

38-
return failures;
36+
failures += testAtan2();
37+
38+
if (failures > 0) {
39+
System.err.println("Testing atan2 incurred "
40+
+ failures + " failures.");
41+
throw new RuntimeException();
42+
}
3943
}
4044

41-
static int testAtan2() {
45+
/**
46+
* Special cases from the spec interspersed with test cases.
47+
*/
48+
private static int testAtan2() {
4249
int failures = 0;
50+
double NaNd = Double.NaN;
51+
double MIN_VALUE = Double.MIN_VALUE;
52+
double MIN_NORM = Double.MIN_NORMAL;
53+
double MAX_VALUE = Double.MAX_VALUE;
54+
double InfinityD = Double.POSITIVE_INFINITY;
55+
double PI = Math.PI;
56+
57+
/*
58+
* If either argument is NaN, then the result is NaN.
59+
*/
60+
for(double nan : Tests.NaNs) {
61+
failures += testAtan2Case(nan, 0.0, NaNd);
62+
failures += testAtan2Case(0.0, nan, NaNd);
63+
}
4364

4465
double [][] testCases = {
45-
{-3.0, Double.POSITIVE_INFINITY, -0.0},
66+
/*
67+
* If the first argument is positive zero and the second
68+
* argument is positive, or the first argument is positive
69+
* and finite and the second argument is positive
70+
* infinity, then the result is positive zero.
71+
*/
72+
{+0.0, MIN_VALUE, +0.0},
73+
{+0.0, MIN_NORM, +0.0},
74+
{+0.0, 1.0, +0.0},
75+
{+0.0, MAX_VALUE, +0.0},
76+
{+0.0, InfinityD, +0.0},
77+
78+
{MIN_VALUE, InfinityD, +0.0},
79+
{MIN_NORM, InfinityD, +0.0},
80+
{1.0, InfinityD, +0.0},
81+
{MAX_VALUE, InfinityD, +0.0},
82+
{MIN_VALUE, InfinityD, +0.0},
83+
84+
/*
85+
* If the first argument is negative zero and the second
86+
* argument is positive, or the first argument is negative
87+
* and finite and the second argument is positive
88+
* infinity, then the result is negative zero.
89+
*/
90+
{-0.0, MIN_VALUE, -0.0},
91+
{-0.0, MIN_NORM, -0.0},
92+
{-0.0, 1.0, -0.0},
93+
{-0.0, MAX_VALUE, -0.0},
94+
{-0.0, InfinityD, -0.0},
95+
96+
{-MIN_VALUE, InfinityD, -0.0},
97+
{-MIN_NORM, InfinityD, -0.0},
98+
{-1.0, InfinityD, -0.0},
99+
{-MAX_VALUE, InfinityD, -0.0},
100+
101+
/*
102+
* If the first argument is positive zero and the second
103+
* argument is negative, or the first argument is positive
104+
* and finite and the second argument is negative
105+
* infinity, then the result is the double value closest
106+
* to pi.
107+
*/
108+
{+0.0, -MIN_VALUE, PI},
109+
{+0.0, -MIN_NORM, PI},
110+
{+0.0, -1.0, PI},
111+
{+0.0, -MAX_VALUE, PI},
112+
{+0.0, -InfinityD, PI},
113+
114+
{MIN_VALUE, -InfinityD, PI},
115+
{MIN_NORM, -InfinityD, PI},
116+
{1.0, -InfinityD, PI},
117+
{MAX_VALUE, -InfinityD, PI},
118+
119+
/*
120+
* If the first argument is negative zero and the second
121+
* argument is negative, or the first argument is negative
122+
* and finite and the second argument is negative
123+
* infinity, then the result is the double value closest
124+
* to -pi.
125+
*/
126+
{-0.0, -MIN_VALUE, -PI},
127+
{-0.0, -MIN_NORM, -PI},
128+
{-0.0, -1.0, -PI},
129+
{-0.0, -MAX_VALUE, -PI},
130+
{-0.0, -InfinityD, -PI},
131+
132+
{-MIN_VALUE, -InfinityD, -PI},
133+
{-MIN_NORM, -InfinityD, -PI},
134+
{-1.0, -InfinityD, -PI},
135+
{-MAX_VALUE, -InfinityD, -PI},
136+
137+
/*
138+
* If the first argument is positive and the second
139+
* argument is positive zero or negative zero, or the
140+
* first argument is positive infinity and the second
141+
* argument is finite, then the result is the double value
142+
* closest to pi/2.
143+
*/
144+
{MIN_VALUE, +0.0, PI/2.0},
145+
{MIN_NORM, +0.0, PI/2.0},
146+
{1.0, +0.0, PI/2.0},
147+
{MAX_VALUE, +0.0, PI/2.0},
148+
149+
{MIN_VALUE, -0.0, PI/2.0},
150+
{MIN_VALUE, -0.0, PI/2.0},
151+
{MIN_NORM, -0.0, PI/2.0},
152+
{1.0, -0.0, PI/2.0},
153+
{MAX_VALUE, -0.0, PI/2.0},
154+
155+
{InfinityD, -MIN_VALUE, PI/2.0},
156+
{InfinityD, -MIN_NORM, PI/2.0},
157+
{InfinityD, -1.0, PI/2.0},
158+
{InfinityD, -MAX_VALUE, PI/2.0},
159+
160+
{InfinityD, MIN_VALUE, PI/2.0},
161+
{InfinityD, MIN_NORM, PI/2.0},
162+
{InfinityD, 1.0, PI/2.0},
163+
{InfinityD, MAX_VALUE, PI/2.0},
164+
165+
/*
166+
* If the first argument is negative and the second argument is
167+
* positive zero or negative zero, or the first argument is
168+
* negative infinity and the second argument is finite, then the
169+
* result is the double value closest to -pi/2.
170+
*/
171+
{-MIN_VALUE, +0.0, -PI/2.0},
172+
{-MIN_NORM, +0.0, -PI/2.0},
173+
{-1.0, +0.0, -PI/2.0},
174+
{-MAX_VALUE, +0.0, -PI/2.0},
175+
176+
{-MIN_VALUE, -0.0, -PI/2.0},
177+
{-MIN_VALUE, -0.0, -PI/2.0},
178+
{-MIN_NORM, -0.0, -PI/2.0},
179+
{-1.0, -0.0, -PI/2.0},
180+
{-MAX_VALUE, -0.0, -PI/2.0},
181+
182+
{-InfinityD, -MIN_VALUE, -PI/2.0},
183+
{-InfinityD, -MIN_NORM, -PI/2.0},
184+
{-InfinityD, -1.0, -PI/2.0},
185+
{-InfinityD, -MAX_VALUE, -PI/2.0},
186+
187+
{-InfinityD, MIN_VALUE, -PI/2.0},
188+
{-InfinityD, MIN_NORM, -PI/2.0},
189+
{-InfinityD, 1.0, -PI/2.0},
190+
{-InfinityD, MAX_VALUE, -PI/2.0},
191+
192+
/*
193+
* If both arguments are positive infinity, then the result is the
194+
* double value closest to pi/4.
195+
*/
196+
{InfinityD, InfinityD, PI/4.0},
197+
198+
/*
199+
* If the first argument is positive infinity and the
200+
* second argument is negative infinity, then the result
201+
* is the double value closest to 3*pi/4.
202+
*/
203+
// Note: in terms of computation, the result of the double
204+
// expression
205+
// 3*PI/4.0
206+
// is the same as a high-precision decimal value of pi
207+
// scaled accordingly and rounded to double:
208+
// BigDecimal bdPi = new BigDecimal("3.14159265358979323846264338327950288419716939937510");
209+
// bdPi.multiply(BigDecimal.valueOf(3)).divide(BigDecimal.valueOf(4)).doubleValue();
210+
{InfinityD, -InfinityD, 3*PI/4.0},
211+
212+
/*
213+
* If the first argument is negative infinity and the second
214+
* argument is positive infinity, then the result is the double
215+
* value closest to -pi/4.
216+
*/
217+
{-InfinityD, InfinityD, -PI/4.0},
218+
219+
/*
220+
* If both arguments are negative infinity, then the result is the
221+
* double value closest to -3*pi/4.
222+
*/
223+
{-InfinityD, -InfinityD, -3*PI/4.0},
224+
225+
{-3.0, InfinityD, -0.0},
46226
};
47227

48228
for (double[] testCase : testCases) {
49-
failures+=testAtan2Case(testCase[0], testCase[1], testCase[2]);
229+
failures += testAtan2Case(testCase[0], testCase[1], testCase[2]);
50230
}
51231

52232
return failures;
53233
}
54234

55-
public static void main(String... argv) {
235+
private static int testAtan2Case(double input1, double input2, double expected) {
56236
int failures = 0;
237+
failures += Tests.test("StrictMath.atan2", input1, input2, StrictMath::atan2, expected);
238+
failures += Tests.test("Math.atan2", input1, input2, Math::atan2, expected);
57239

58-
failures += testAtan2();
59-
60-
if (failures > 0) {
61-
System.err.println("Testing atan2 incurred "
62-
+ failures + " failures.");
63-
throw new RuntimeException();
64-
}
240+
return failures;
65241
}
66242
}

0 commit comments

Comments
 (0)