In praise of Java
Posted: Tue Aug 15, 2023 7:57 pm
I am so glad that Cherry Audio chose to use Java for VM development.
<rant>
Today I needed to convert an x,y coordinate into a rotation angle so googled atan2 to remind myself how it works. Arc tangent is a tricky function so I expected a list of how special cases are handled and the official Java documentation told me exactly what I needed to know.
Then just out of interest I looked up the C++ documentation for the same function.
I've copied both below. What this illustrates to me in just how stark the difference is between the worlds of Java and C++.
I used C++ for decades and have been doing a bit of work with it again recently but I really do hate it!
</rant>
JAVA VERSION
atan2
public static double atan2(double y,
double x)
Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta). This method computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi. Special cases:
If either argument is NaN, then the result is NaN.
If the first argument is positive zero and the second argument is positive, or the first argument is positive and finite and the second argument is positive infinity, then the result is positive zero.
If the first argument is negative zero and the second argument is positive, or the first argument is negative and finite and the second argument is positive infinity, then the result is negative zero.
If the first argument is positive zero and the second argument is negative, or the first argument is positive and finite and the second argument is negative infinity, then the result is the double value closest to pi.
If the first argument is negative zero and the second argument is negative, or the first argument is negative and finite and the second argument is negative infinity, then the result is the double value closest to -pi.
If the first argument is positive and the second argument is positive zero or negative zero, or the first argument is positive infinity and the second argument is finite, then the result is the double value closest to pi/2.
If the first argument is negative and the second argument is positive zero or negative zero, or the first argument is negative infinity and the second argument is finite, then the result is the double value closest to -pi/2.
If both arguments are positive infinity, then the result is the double value closest to pi/4.
If the first argument is positive infinity and the second argument is negative infinity, then the result is the double value closest to 3*pi/4.
If the first argument is negative infinity and the second argument is positive infinity, then the result is the double value closest to -pi/4.
If both arguments are negative infinity, then the result is the double value closest to -3*pi/4.
The computed result must be within 2 ulps of the exact result. Results must be semi-monotonic.
Parameters:
y - the ordinate coordinate
x - the abscissa coordinate
Returns:
the theta component of the point (r, theta) in polar coordinates that corresponds to the point (x, y) in Cartesian coordinates.
--------------------------------------------------------------------------------------------------------
C++ VERSION
std::atan2, std::atan2f, std::atan2l
C++ Numerics library Common mathematical functions
Defined in header <cmath>
(1)
float atan2 ( float y, float x );
double atan2 ( double y, double x );
long double atan2 ( long double y, long double x );
(until C++23)
/* floating-point-type */
atan2 ( /* floating-point-type */ y,
/* floating-point-type */ x );
(since C++23)
(constexpr since C++26)
float atan2f( float y, float x );
(2) (since C++11)
(constexpr since C++26)
long double atan2l( long double y, long double x );
(3) (since C++11)
(constexpr since C++26)
Additional overloads (since C++11)
Defined in header <cmath>
template< class Integer >
double atan2 ( Integer y, Integer x );
(A) (constexpr since C++26)
1-3) Computes the arc tangent of y / x using the signs of arguments to determine the correct quadrant. The library provides overloads of std::atan2 for all cv-unqualified floating-point types as the type of the parameters. (since C++23)
A) Additional overloads are provided for all integer types, which are treated as double.
(since C++11)
Parameters
y, x - floating-point or integer values
Return value
If no errors occur, the arc tangent of y / x (arctan(
y
x
)) in the range [-π, +π] radians, is returned.
y argumentReturn value
math-atan2.png
x argument
If a domain error occurs, an implementation-defined value is returned (NaN where supported).
If a range error occurs due to underflow, the correct result (after rounding) is returned.
Error handling
Errors are reported as specified in math_errhandling.
Domain error may occur if x and y are both zero.
If the implementation supports IEEE floating-point arithmetic (IEC 60559),
If x and y are both zero, domain error does not occur
If x and y are both zero, range error does not occur either
If y is zero, pole error does not occur
If y is ±0 and x is negative or -0, ±π is returned
If y is ±0 and x is positive or +0, ±0 is returned
If y is ±∞ and x is finite, ±π/2 is returned
If y is ±∞ and x is -∞, ±3π/4 is returned
If y is ±∞ and x is +∞, ±π/4 is returned
If x is ±0 and y is negative, -π/2 is returned
If x is ±0 and y is positive, +π/2 is returned
If x is -∞ and y is finite and positive, +π is returned
If x is -∞ and y is finite and negative, -π is returned
If x is +∞ and y is finite and positive, +0 is returned
If x is +∞ and y is finite and negative, -0 is returned
If either x is NaN or y is NaN, NaN is returned
Notes
std::atan2(y, x) is equivalent to std::arg(std::complex<std::common_type_t<decltype(x), decltype(y)>>(x, y)).
POSIX specifies that in case of underflow, the value y / x is returned, and if that is not supported, an implementation-defined value no greater than DBL_MIN, FLT_MIN, and LDBL_MIN is returned.
The additional overloads are not required to be provided exactly as (A). They only need to be sufficient to ensure that for their first argument num1 and second argument num2:
If num1 or num2 has type long double, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast<long double>(num1),
static_cast<long double>(num2)).
Otherwise, if num1 and/or num2 has type double or an integer type, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast<double>(num1),
static_cast<double>(num2)).
Otherwise, if num1 or num2 has type float, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast<float>(num1),
static_cast<float>(num2)).
(until C++23)
If num1 and num2 have arithmetic types, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast</* common-floating-point-type */>(num1),
static_cast</* common-floating-point-type */>(num2)), where /* common-floating-point-type */ is the floating-point type with the greatest floating-point conversion rank and greatest floating-point conversion subrank between the types of num1 and num2, arguments of integer type are considered to have the same floating-point conversion rank as double.
If no such floating-point type with the greatest rank and subrank exists, then overload resolution does not result in a usable candidate from the overloads provided.
<rant>
Today I needed to convert an x,y coordinate into a rotation angle so googled atan2 to remind myself how it works. Arc tangent is a tricky function so I expected a list of how special cases are handled and the official Java documentation told me exactly what I needed to know.
Then just out of interest I looked up the C++ documentation for the same function.
I've copied both below. What this illustrates to me in just how stark the difference is between the worlds of Java and C++.
I used C++ for decades and have been doing a bit of work with it again recently but I really do hate it!
</rant>
JAVA VERSION
atan2
public static double atan2(double y,
double x)
Returns the angle theta from the conversion of rectangular coordinates (x, y) to polar coordinates (r, theta). This method computes the phase theta by computing an arc tangent of y/x in the range of -pi to pi. Special cases:
If either argument is NaN, then the result is NaN.
If the first argument is positive zero and the second argument is positive, or the first argument is positive and finite and the second argument is positive infinity, then the result is positive zero.
If the first argument is negative zero and the second argument is positive, or the first argument is negative and finite and the second argument is positive infinity, then the result is negative zero.
If the first argument is positive zero and the second argument is negative, or the first argument is positive and finite and the second argument is negative infinity, then the result is the double value closest to pi.
If the first argument is negative zero and the second argument is negative, or the first argument is negative and finite and the second argument is negative infinity, then the result is the double value closest to -pi.
If the first argument is positive and the second argument is positive zero or negative zero, or the first argument is positive infinity and the second argument is finite, then the result is the double value closest to pi/2.
If the first argument is negative and the second argument is positive zero or negative zero, or the first argument is negative infinity and the second argument is finite, then the result is the double value closest to -pi/2.
If both arguments are positive infinity, then the result is the double value closest to pi/4.
If the first argument is positive infinity and the second argument is negative infinity, then the result is the double value closest to 3*pi/4.
If the first argument is negative infinity and the second argument is positive infinity, then the result is the double value closest to -pi/4.
If both arguments are negative infinity, then the result is the double value closest to -3*pi/4.
The computed result must be within 2 ulps of the exact result. Results must be semi-monotonic.
Parameters:
y - the ordinate coordinate
x - the abscissa coordinate
Returns:
the theta component of the point (r, theta) in polar coordinates that corresponds to the point (x, y) in Cartesian coordinates.
--------------------------------------------------------------------------------------------------------
C++ VERSION
std::atan2, std::atan2f, std::atan2l
C++ Numerics library Common mathematical functions
Defined in header <cmath>
(1)
float atan2 ( float y, float x );
double atan2 ( double y, double x );
long double atan2 ( long double y, long double x );
(until C++23)
/* floating-point-type */
atan2 ( /* floating-point-type */ y,
/* floating-point-type */ x );
(since C++23)
(constexpr since C++26)
float atan2f( float y, float x );
(2) (since C++11)
(constexpr since C++26)
long double atan2l( long double y, long double x );
(3) (since C++11)
(constexpr since C++26)
Additional overloads (since C++11)
Defined in header <cmath>
template< class Integer >
double atan2 ( Integer y, Integer x );
(A) (constexpr since C++26)
1-3) Computes the arc tangent of y / x using the signs of arguments to determine the correct quadrant. The library provides overloads of std::atan2 for all cv-unqualified floating-point types as the type of the parameters. (since C++23)
A) Additional overloads are provided for all integer types, which are treated as double.
(since C++11)
Parameters
y, x - floating-point or integer values
Return value
If no errors occur, the arc tangent of y / x (arctan(
y
x
)) in the range [-π, +π] radians, is returned.
y argumentReturn value
math-atan2.png
x argument
If a domain error occurs, an implementation-defined value is returned (NaN where supported).
If a range error occurs due to underflow, the correct result (after rounding) is returned.
Error handling
Errors are reported as specified in math_errhandling.
Domain error may occur if x and y are both zero.
If the implementation supports IEEE floating-point arithmetic (IEC 60559),
If x and y are both zero, domain error does not occur
If x and y are both zero, range error does not occur either
If y is zero, pole error does not occur
If y is ±0 and x is negative or -0, ±π is returned
If y is ±0 and x is positive or +0, ±0 is returned
If y is ±∞ and x is finite, ±π/2 is returned
If y is ±∞ and x is -∞, ±3π/4 is returned
If y is ±∞ and x is +∞, ±π/4 is returned
If x is ±0 and y is negative, -π/2 is returned
If x is ±0 and y is positive, +π/2 is returned
If x is -∞ and y is finite and positive, +π is returned
If x is -∞ and y is finite and negative, -π is returned
If x is +∞ and y is finite and positive, +0 is returned
If x is +∞ and y is finite and negative, -0 is returned
If either x is NaN or y is NaN, NaN is returned
Notes
std::atan2(y, x) is equivalent to std::arg(std::complex<std::common_type_t<decltype(x), decltype(y)>>(x, y)).
POSIX specifies that in case of underflow, the value y / x is returned, and if that is not supported, an implementation-defined value no greater than DBL_MIN, FLT_MIN, and LDBL_MIN is returned.
The additional overloads are not required to be provided exactly as (A). They only need to be sufficient to ensure that for their first argument num1 and second argument num2:
If num1 or num2 has type long double, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast<long double>(num1),
static_cast<long double>(num2)).
Otherwise, if num1 and/or num2 has type double or an integer type, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast<double>(num1),
static_cast<double>(num2)).
Otherwise, if num1 or num2 has type float, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast<float>(num1),
static_cast<float>(num2)).
(until C++23)
If num1 and num2 have arithmetic types, then std::atan2(num1, num2) has the same effect as std::atan2(static_cast</* common-floating-point-type */>(num1),
static_cast</* common-floating-point-type */>(num2)), where /* common-floating-point-type */ is the floating-point type with the greatest floating-point conversion rank and greatest floating-point conversion subrank between the types of num1 and num2, arguments of integer type are considered to have the same floating-point conversion rank as double.
If no such floating-point type with the greatest rank and subrank exists, then overload resolution does not result in a usable candidate from the overloads provided.