I don’t understand why there are individual keywords for different multiples of π. Why use TWO_PI when you can just use 2*PI? Doesn’t it allow for more compact and readable code? I would love it if someone could explain this to me.
Welcome to the forum.
The discussion title doesn’t have anything to do with the question asked so I will answer that ![]()
There are 2π radians in a circle so usingTWO_PI would suggest there is some connection with the geometry of a circle for instance ang = ang % TWO_PI where ang is any positive angle in radians would normalise the angle to the range ≥0 and <2π radians.
You could do the same with ang = ang % (2 * TWO_PI) but I think the first is clearer in this instance. Using 2 * PI does not have the same semantic feel also TWO_PI saves a multiplication operation every time it is used.
Ultimately the programmer has a choice between TWO_PI or 2 * PI depending on their own personal preference.
Finally you might ask yourself why have the constant PI rather than using 3.14159265359 the answer is obvious.
Java and Processing have many useful constants to prevent errors and make the code more readable & understandable.
I fixed the title ![]()
We could add that there are even more constants for multiples and fractions of PI too.
As mentioned in the reference:
HALF_PI: a mathematical constant with the value 1.57079632679489661923PI: a mathematical constant with the value 3.14159265358979323846QUARTER_PI: a mathematical constant with the value 0.7853982TAU: An alias forTWO_PITWO_PI: a mathematical constant with the value 6.28318530717958647693
We can also use its alias TAU instead: ![]()
There are others, like HALF_PI, QUARTER_PI and even THIRD_PI:
When they’re clustered together, like 5 - THIRD_PI * RAD_TO_DEG, Java can replace literals and final variables operations as 1 “compile-time constant” value:
https://www.Baeldung.com/java-compile-time-constants
Today I learned Processing has an undocumented THIRD_PI constant ![]()
I was curious:
// The Processing constants are evaluated as doubles and then cast to floats.
// I am comparing the constants to the mathematical expression on the right for binary equality.
// float PI = (float) Math.PI;
println(PI == PI); // :)
// float HALF_PI = (float) (Math.PI / 2.0);
println(HALF_PI == PI / 2);
// float THIRD_PI = (float)(Math.PI / 3.0);
println(THIRD_PI == PI / 3);
// float QUARTER_PI = (float)(Math.PI / 4.0);
println(QUARTER_PI == PI / 4);
// float TWO_PI = (float) (2.0 * Math.PI);
println(TWO_PI == 2 * PI);
// float TAU = (float) (2.0 * Math.PI);
println(TAU == 2 * PI);
They were all true!
Processing generally works with float values for its sketch-level math.
In some cases, I have used double for higher precision during the intermediate calculation, then cast the final result to float.
Further exploration:
Processing float values: decimal output and IEEE 754 bit patterns
:)
Java math uses the double data type by default but the Processing creators decided to use float as the default in Processing. AFAIK all Processing math constants are created from Java constants and then cast to float you can see that here
println((float)Math.PI == PI); // true
Because of this the user must be careful mixing Java and Processing constants in the same code for instance
println(Math.PI == PI); // false
In this case the PI is promoted to a double before the comparison. In general Java promotes all numerical types to the higher precision before performing the calculation.
This answer is only covers floating point number variables and constants.
Although I understand the rationale for using the float type as Processing’s default data type, personally i believe it would have been better to leave it as double.
For floating literals inside a “.pde” file, suffix them w/ a “d” to force them to be treated as a double type: 2.0654d
And for “.java” files, suffix them w/ an “f” to force them to be treated as a float type: 2.0654f
With the risk of derailing the topic, you are aware that you actually never should use == to compare floats (and doubles)?
Below code demonstrates
float f = 0;
for(int cnt=0; cnt < 20; cnt++)
{
f += 0.1;
println(f);
if(f == 0.7)
println("match");
}
The output
0.1
0.2
0.3
0.4
0.5
0.6
0.70000005
0.8000001
0.9000001
1.0000001
1.1000001
1.2000002
1.3000002
1.4000002
1.5000002
1.6000003
1.7000003
1.8000003
1.9000003
2.0000002
One would expect a match on 0.7 but as can be seen 0.7 does not exist in the output, the value is slightly higher.
This is a separate issue from my example, but since it was raised I will clarify it.
The context is important.
== is a valid operator for floats. It is appropriate when exact equality of the stored floating-point values is the intended comparison.
It was used correctly and as intended in my example comparing the constant to a mathematical expression.
Your example uses == in a different context, where repeated addition accumulates floating-point approximation.
Experienced users would recognize the observed output as the expected result.
Expanding your code example:
The float is stored in binary as an approximation, then converted to decimal for printing.
Processing’s println(f) uses Java’s default float-to-text conversion.
That conversion prints a short rounded decimal form, so the stored float for 0.1 appears as “0.1”.
The second println uses nf(f, 1, 20) to print 20 decimal places.
Printing 20 decimal places expands the decimal display of the stored binary float.
These extra digits show the decimal representation of the stored value, NOT extra precision.
Each += adds a binary floating-point approximation of 0.1 and stores the rounded result back in f.
// The float is stored in binary as an approximation, then converted to decimal for printing.
// Processing's println(f) uses Java's default float-to-text conversion.
// That conversion prints a short rounded decimal form, so the stored float for 0.1 appears as "0.1".
// The second println uses nf(f, 1, 20) to print 20 decimal places.
// Printing 20 decimal places expands the decimal display of the stored binary float.
// These extra digits show the decimal representation of the stored value, NOT extra precision.
// Each += adds a binary floating-point approximation of 0.1 and stores the rounded result back in f.
float f = 0;
for(int cnt=0; cnt < 20; cnt++)
{
f += 0.1;
println(f);
println(nf(f, 1, 20));
println();
if(f == 0.7)
println("match");
}
Output:
0.1
0.10000000149011612000
0.2
0.20000000298023224000
0.3
0.30000001192092896000
0.4
0.40000000596046450000
0.5
0.50000000000000000000
0.6
0.60000002384185790000
0.70000005
0.70000004768371580000
0.8000001
0.80000007152557370000
0.9000001
0.90000009536743160000
1.0000001
1.00000011920928960000
1.1000001
1.10000014305114750000
1.2000002
1.20000016689300540000
1.3000002
1.30000019073486330000
1.4000002
1.40000021457672120000
1.5000002
1.50000023841857900000
1.6000003
1.60000026226043700000
1.7000003
1.70000028610229500000
1.8000003
1.80000030994415280000
1.9000003
1.90000033378601070000
2.0000002
2.00000023841857900000
Additional references here:
Processing float values: decimal output and IEEE 754 bit patterns
Caveat: The expanded decimal output helps display the stored float, but typing that decimal back into code still creates a new float by converting the decimal literal to the nearest representable binary value.
Bonus code
float f=0;
for(int cnt=0; cnt < 20; cnt++)
{
int i = 3; // Try with 1, 2. 3, ...
f += 1.0/(1<<i);
println(f);
println(nf(f, 1, 20));
if(f == 1.75)
println("match");
println();
}
:)