In praise of Java
In praise of Java
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.
- Waverley Instruments
- Posts: 147
- Joined: Thu May 05, 2022 2:10 pm
Re: In praise of Java
Seconded!
I also think the SDK is a fantastic technical achievement. I'm still amazed that VM is the only (?) development system that allows you to create sample-accurate DSP and MIDI effects and processors, written entirely in Java.
But to stick to the general topic of Java, here's something nerdy / amusing that I recall seeing during a technical presentation / introduction to the Java language. Powerpoint auto-correct, strikes again perhaps...
What Is Java?
- Java is a pointless language
-Rob @ WI
Re: In praise of Java
For anyone not getting the joke, the original text would have been "Java is a pointerless language".
VMD does indeed provide us with a superb development environment, CA did a fabulous job. My only real criticism is the crazy capitalization of method names in the API. Almost every single day I get a compiler error because I've typed something like getValue() rather than GetValue(). It's a total mystery to me why CA broke the long established identifier case convention.
- Waverley Instruments
- Posts: 147
- Joined: Thu May 05, 2022 2:10 pm
Re: In praise of Java
I blame Microsoft / C#ColinP wrote: ↑Wed Aug 16, 2023 8:32 amMy only real criticism is the crazy capitalization of method names in the API. Almost every single day I get a compiler error because I've typed something like getValue() rather than GetValue(). It's a total mystery to me why CA broke the long established identifier case convention.
-Rob
Re: In praise of Java
Ah, I had not thought of that. Maybe Dan G. had the misfortune to work in C# for a while.
For people unaware of case converntions the main ones are I think called PascalCase, camelCase, snake_case and UPPERCASE_SNAKE by Microsoft.
In Java PascalCase is used for class names, UPPERCASE_SNAKE is used for constants but most of the time one uses camelCase as this is the convention for method and variable names. But unfortunately the VM API uses PascalCase for method names.
Moving from C++ to Java the main motor skill problem is retraining your muscle memory to use camelCase rather then snake_case for most identifiers. It took me nearly six months to adapt and get my typing speed back up.
- Waverley Instruments
- Posts: 147
- Joined: Thu May 05, 2022 2:10 pm
Re: In praise of Java
I guess Microsoft's PascalCase inclination goes back to the C function-based Windows API, its thinly-veiled C++ object-based wrapper, MFC and then the C# / .NET Framework, the controversial offspring of Microsoft's short-lived version of Java aka Visual J++.
Microsoft tech stack developers probably would've been up in arms if they suddenly had to type showWindow() instead of ShowWindow() just to be more object-oriented
Speaking of muscle-memory, I still forget sometimes to add a ; at the end of Java statements after working exclusively with Swift for several years.
As someone once said, the great thing about standards, is that there are so many to choose from!
Rob @ WI
Microsoft tech stack developers probably would've been up in arms if they suddenly had to type showWindow() instead of ShowWindow() just to be more object-oriented
Speaking of muscle-memory, I still forget sometimes to add a ; at the end of Java statements after working exclusively with Swift for several years.
As someone once said, the great thing about standards, is that there are so many to choose from!
Rob @ WI
Re: In praise of Java
I think companies like Microsoft could have adopted standards but chose not to in order to lock their customers in. The / \ thing being a ridiculous case in point. Anyone working for Microsoft have only themselves to blame though.
Not sure about semicolonless syntax. I've designed quite a few languages and not having a terminator symbol makes it really difficult to generate meaningful error messages. Even with the help of ; compilers often produce silly amounts of parasitic messages. So I'm happy to type the ; as a tradeoff for more accurate analysis when I accidentally type something like { rather than }.
Not sure about semicolonless syntax. I've designed quite a few languages and not having a terminator symbol makes it really difficult to generate meaningful error messages. Even with the help of ; compilers often produce silly amounts of parasitic messages. So I'm happy to type the ; as a tradeoff for more accurate analysis when I accidentally type something like { rather than }.
- Waverley Instruments
- Posts: 147
- Joined: Thu May 05, 2022 2:10 pm
Re: In praise of Java
As a possible point of interest, the semicolon is actually optional in Swift so the compiler is probably quite smart. Plus there's some other weird indexing voodoo that goes on inside Xcode that provides you with surprisingly meaningful warnings and error messages.
Anyway, you'll probably find in most commercial environments, and Apple's example code, that the standard is to omit the semicolon in Swift. After a short while, I got used to it and actually found standard Swift code a little cleaner and quicker to scan than its Java equivalent, say when comparing Swift on iOS and Java on Android for mobile dev when you're looking at code that's essentially trying to do the same thing.
It's really tragic though that for all the cutting-edge engineering, design and debate that went into Swift, you can't write DSP-level code, like you can in Java / Voltage Modular.
Hey presto, back on topic!
-Rob @ WI
Anyway, you'll probably find in most commercial environments, and Apple's example code, that the standard is to omit the semicolon in Swift. After a short while, I got used to it and actually found standard Swift code a little cleaner and quicker to scan than its Java equivalent, say when comparing Swift on iOS and Java on Android for mobile dev when you're looking at code that's essentially trying to do the same thing.
It's really tragic though that for all the cutting-edge engineering, design and debate that went into Swift, you can't write DSP-level code, like you can in Java / Voltage Modular.
Hey presto, back on topic!
-Rob @ WI
Re: In praise of Java
Swift may well be an excellent language and Swift compiler devs may have discovered some clever error analysis techniques beyond my understanding but I'd still place it and Apple in the same category as C# and Microsoft.
C is probably still the most popular language when you include embedded applications and Python is very popular at a theoretical level. Heck, COBOL is still in use! But for the kind of work I'm interested in Java is really the only show in town. It's far from perfect of course. But I predict that it will become the dominant language over the next two or three decades as it strikes a balance between clarity, expressiveness, ease of use and performance that is hard to beat.
C is probably still the most popular language when you include embedded applications and Python is very popular at a theoretical level. Heck, COBOL is still in use! But for the kind of work I'm interested in Java is really the only show in town. It's far from perfect of course. But I predict that it will become the dominant language over the next two or three decades as it strikes a balance between clarity, expressiveness, ease of use and performance that is hard to beat.
Re: In praise of Java
I’ve used Java, C and C++ professionally for a long time and I still have the shortest path from idea to working code in Java. I was a bit surprised how efficient the VM environment is and it's absolutely fantastic for development and prototyping.
On a related note, I also think Cherry Audio has done an excellent job at abstracting out the host and host OS as it relates to audio, midi, and related processing.
On a related note, I also think Cherry Audio has done an excellent job at abstracting out the host and host OS as it relates to audio, midi, and related processing.