@dekutree64 You were correct - on an arduino the int cast is way slower than the fmod. Arduino also doesn’t seem to have a double precision option.
This is what I’m getting:
Starting Angle Normalization Benchmark
=== PERFORMANCE BENCHMARK RESULTS ===
Timing results for angle normalization methods:
fmod method: 256 microseconds
Subtraction method: 496 microseconds
Double precision method: 252 microseconds
=== ACCURACY BENCHMARK RESULTS ===
Differences compared to double precision method (baseline):
fmod method error statistics:
Mean Error: 0.000000
Standard Deviation: 0.000000
Maximum Error: 0.000000
Minimum Error: 0.000000
Subtraction method error statistics:
Mean Error: 0.000088
Standard Deviation: 0.000079
Maximum Error: 0.000231
Minimum Error: 0.000003
With this piece of code, running on an Arduino Nano.
#include <Arduino.h>
#include <math.h> // For fmod
#include <stdint.h> // For int32_t
const float TWO_PI_FLOAT = 2.0f * PI; // Single precision 2π
const float ONE_OVER_TWO_PI_FLOAT = 1.0f / TWO_PI_FLOAT;
const double TWO_PI_DOUBLE = 2.0 * M_PI; // Double precision 2π
const double ONE_OVER_TWO_PI_DOUBLE = 1.0 / TWO_PI_DOUBLE;
// Normalizing with float using fmod
float normalizeAngle_fmod(float angle) {
float normalizedAngle = fmod(angle, TWO_PI_FLOAT);
return normalizedAngle >= 0 ? normalizedAngle : (normalizedAngle + TWO_PI_FLOAT);
}
// Normalizing with float using subtraction method
float normalizeAngle_subtraction(float angle) {
float normalizedAngle = angle - TWO_PI_FLOAT * ((int)(angle * ONE_OVER_TWO_PI_FLOAT));
return (normalizedAngle < 0) ? (normalizedAngle + TWO_PI_FLOAT) : normalizedAngle;
}
// Normalizing with double using fmod
double normalizeAngle_double(double angle) {
double normalizedAngle = fmod(angle, TWO_PI_DOUBLE);
return normalizedAngle >= 0 ? normalizedAngle : (normalizedAngle + TWO_PI_DOUBLE);
}
// Function to calculate the mean
double calculateMean(const double* values, int count) {
double sum = 0.0;
for (int i = 0; i < count; ++i) {
sum += values[i];
}
return sum / count;
}
// Function to calculate the standard deviation
double calculateStdDev(const double* values, int count, double mean) {
double sumSquaredDifferences = 0.0;
for (int i = 0; i < count; ++i) {
sumSquaredDifferences += (values[i] - mean) * (values[i] - mean);
}
return sqrt(sumSquaredDifferences / count);
}
// Function to find the max value in an array
double findMaxValue(const double* values, int count) {
double maxVal = values[0];
for (int i = 1; i < count; ++i) {
if (values[i] > maxVal) {
maxVal = values[i];
}
}
return maxVal;
}
// Function to find the min value in an array
double findMinValue(const double* values, int count) {
double minVal = values[0];
for (int i = 1; i < count; ++i) {
if (values[i] < minVal) {
minVal = values[i];
}
}
return minVal;
}
void setup() {
Serial.begin(115200);
Serial.println("Starting Angle Normalization Benchmark");
const int NUM_TESTS = 10;
float testAngles[NUM_TESTS];
double normalizedAngles_fmod[NUM_TESTS];
double normalizedAngles_subtraction[NUM_TESTS];
double normalizedAngles_double[NUM_TESTS];
// Arrays to store the differences
double error_fmod_vs_double[NUM_TESTS];
double error_subtraction_vs_double[NUM_TESTS];
// Generate random angles between -10000 and 10000 radians
for (int i = 0; i < NUM_TESTS; ++i) {
testAngles[i] = random(-10000, 10000);
}
unsigned long startTime, endTime;
// Measure time for fmod method
startTime = micros();
for (int i = 0; i < NUM_TESTS; ++i) {
normalizedAngles_fmod[i] = normalizeAngle_fmod(testAngles[i]);
}
endTime = micros();
unsigned long executionTime_fmod = endTime - startTime;
// Measure time for subtraction method
startTime = micros();
for (int i = 0; i < NUM_TESTS; ++i) {
normalizedAngles_subtraction[i] = normalizeAngle_subtraction(testAngles[i]);
}
endTime = micros();
unsigned long executionTime_subtraction = endTime - startTime;
// Measure time for double method
startTime = micros();
for (int i = 0; i < NUM_TESTS; ++i) {
normalizedAngles_double[i] = normalizeAngle_double(testAngles[i]);
}
endTime = micros();
unsigned long executionTime_double = endTime - startTime;
// Calculate the differences between methods
for (int i = 0; i < NUM_TESTS; ++i) {
error_fmod_vs_double[i] = abs(normalizedAngles_fmod[i] - normalizedAngles_double[i]);
error_subtraction_vs_double[i] = abs(normalizedAngles_subtraction[i] - normalizedAngles_double[i]);
}
// Compute statistics for the errors
double meanError_fmod = calculateMean(error_fmod_vs_double, NUM_TESTS);
double stdDevError_fmod = calculateStdDev(error_fmod_vs_double, NUM_TESTS, meanError_fmod);
double maxError_fmod = findMaxValue(error_fmod_vs_double, NUM_TESTS);
double minError_fmod = findMinValue(error_fmod_vs_double, NUM_TESTS);
double meanError_subtraction = calculateMean(error_subtraction_vs_double, NUM_TESTS);
double stdDevError_subtraction = calculateStdDev(error_subtraction_vs_double, NUM_TESTS, meanError_subtraction);
double maxError_subtraction = findMaxValue(error_subtraction_vs_double, NUM_TESTS);
double minError_subtraction = findMinValue(error_subtraction_vs_double, NUM_TESTS);
// Print benchmark results
Serial.println("=== PERFORMANCE BENCHMARK RESULTS ===");
Serial.println("Timing results for angle normalization methods:");
Serial.print("fmod method: "); Serial.print(executionTime_fmod); Serial.println(" microseconds");
Serial.print("Subtraction method: "); Serial.print(executionTime_subtraction); Serial.println(" microseconds");
Serial.print("Double precision method: "); Serial.print(executionTime_double); Serial.println(" microseconds");
Serial.println("\n=== ACCURACY BENCHMARK RESULTS ===");
Serial.println("Differences compared to double precision method (baseline):");
Serial.println();
Serial.println("fmod method error statistics:");
Serial.print("Mean Error: "); Serial.println(meanError_fmod, 6);
Serial.print("Standard Deviation: "); Serial.println(stdDevError_fmod, 6);
Serial.print("Maximum Error: "); Serial.println(maxError_fmod, 6);
Serial.print("Minimum Error: "); Serial.println(minError_fmod, 6);
Serial.println();
Serial.println("Subtraction method error statistics:");
Serial.print("Mean Error: "); Serial.println(meanError_subtraction, 6);
Serial.print("Standard Deviation: "); Serial.println(stdDevError_subtraction, 6);
Serial.print("Maximum Error: "); Serial.println(maxError_subtraction, 6);
Serial.print("Minimum Error: "); Serial.println(minError_subtraction, 6);
}
void loop() {
// Nothing to do in the loop
}