24 / 07 / 31
It's key to fixed-point math: you might already know that picking the right scaling factor is. Did you know you can change the scaling factor as you go? Yep, dynamic scaling is a handy trick when your project needs different levels of precision at different times.
Think of it: minutes handling sensor readings, and then large values being passed seconds afterwards. You can just dial the scaling factor in such that everything is accurized without issue.
Here's a quick example:
int scale; float value = 0.00123; if (value < 1) { scale = 10000; } else { scale = 100; } int fixedValue = value * scale; float result = fixedValue / (float)scale; Serial.println(result);
This allows you to keep your math precise and your Arduino happy.
Ever have your math blow up because of overflow? Really, it's not funny. That is just one reason why saturation arithmetic exists: to act like a safety net for your calculations. If your numbers get too big or too small, they just stop at the limits rather than looping around.
You can set this up as follows:
Define the maximum and minimum values that the largest and smallest numbers can have given a chosen scaling factor.
Before doing any big math, check if the result will go out of bounds. If it will, just set it to the limit.
const int16_t MAX_VAL = 32767; const int16_t MIN_VAL = -32768; int16_t a = 20000; int16_t b = 15000; int32_t result = (int32_t)a + b; if (result > MAX_VAL) { result = MAX_VAL; } else if (result < MIN_VAL) { result = MIN_VAL; }
This prevents it from all going haywire and makes your project far more robust.
If you find that fixed-point math shows up all over your project, it's probably time to make a custom fixed-point data type. This way you can keep all the nitty-gritty math hidden inside a nice easy-to-use class.
Here's the point:
Define a class that holds your number and scaling factor.
Make it so you can add, subtract, multiply, and divide these numbers just like regular variables.
The class will be in charge of all the scaling and other background stuff—keeping you far away from the complications.
class FixedPoint { private: int32_t value; int32_t scale; public: FixedPoint(float number, int32_t scale) : scale(scale) { value = number * scale; } FixedPoint operator+(const FixedPoint& other) { return FixedPoint((this->value + other.value) / (float)this->scale, this->scale); } float toFloat() { return value / (float)scale; } };
In that way, your code remains clean and can work with fixed-point numbers just like any other type.