1.06 is not a float. The closest float is 1.059999942779541015625. If
you multiply that by 100 you get a number that, again, is not a float.
Rounding to the nearest float yields 105.99999237060546875, which is the
result of the expression ver * 100, and just one ULP short of 106.
Yes, floating point computations do quite usually involve rounding
steps.
Casting to an int implicitly rounds towards zero, and you get 105,
which is the correct result. Correct according to the rules of IEEE 754
arithmetics.
You may want to round() to the nearest integer before the cast.
Variable ver can be any decimal in this format x.xx
No, a float cannot be “any decimal” in this format. Floats are stored
in binary, and most decimal numbers (numbers that can be written with a
finite number of decimal digits) cannot be written in binary with a
finite number of bits.
Except for a few values (namely the multiples of 0.25), ver can only
store an approximation of the decimal number.
Converting float to int
Converting float to int by removeing decimal point
Convert float to int error - Arduino Stack Exchange
arduino - C embedded convert float to int - Stack Overflow
Videos
LED_CYCLE_TIME is zero because you don't use floating point division ((1 / F_LEDS) / 2 is (1 / 5) / 2, but 1 / 5 == 0, so it's 0).
Just use doubles: #define LED_CYCLE_TIME (0.5 / F_LEDS)
And you also need some extra brackets around your defines, since currently OVERFLOWS_PER_CYCLE is defined as (unsigned int)((1 / F_LEDS) / 2 / 1 / F_CPU / 256.0), which does the division left-to-right instead of in the correct order.
So your corrected code:
#define OF_FREQUENCY (F_CPU / 256.0)
#define SECONDS_PER_OF (1 / OF_FREQUENCY)
#define F_LEDS 5
#define LED_CYCLE_TIME (0.5 / F_LEDS)
#define OVERFLOWS_PER_CYCLE (unsigned int)(LED_CYCLE_TIME / SECONDS_PER_OF)
The arithmetic can be considerably simpler and can be performed without the need for floating point.
Consider that LED_CYCLE_TIME is:
1 / (2 * F_LEDS)
and that you are dividing that by SECONDS_PER_OF. But that is the same as multiplying by the reciprocal of SECONDS_PER_OF, and you already calculated that as OF_FREQUENCY. So now you have:
1 / (2 * F_LEDS) * OF_FREQUENCY
which is the same as:
OF_FREQUENCY / (2 * F_LEDS)
So what you end up with in code is:
#define OF_FREQUENCY (F_CPU / 256)
#define F_LEDS 5
#define OVERFLOWS_PER_CYCLE (OF_FREQUENCY / (2 * F_LEDS))
Or better:
#define LED_PRESCALER 256
#define F_LEDS 5
#define OVERFLOWS_PER_CYCLE (F_CPU / (LED_PRESCALER * 2 * F_LEDS))
Which given F_CPU == 16000000 is 6250 and requires no floating point.
With some inspiration from timemage's comment, I decided in the long run to implement my own power function that takes two int arguments and returns an int. Rather than use the pow function in the <math.h> library which returns a double when I only need an int.
int power(int x, int y) {
return y == 0 ? 1 : x * power(x, y - 1);
}
Now the program works as intended!
As explained by @chrisl in a comment, the issue is that pow() does not
return an exact result, only an approximation. It works fine with
compile-time constants because in this case it is evaluated at compile
time, by the compiler, on you PC.
Simple solution: replace the cast to int with round():
int multiplier = round(pow(10, argc - 1));
This works, but is awfully inefficient, as pow() involves the
computation of both an exponential and a logarithm, both very expensive
on an AVR-based Arduino. The int-only function in your own answer is
certainly better. However, it is generally advised to avoid recursion on
low-memory Arduinos like the Uno. Prefer an iterative algorithm if you
can.
Also, the complexity of you int-only function is O(y). There is a well-known algorithm for integer powers that goes like O(log(y)):
int power(int x, int y)
{
int z = 1; // Invariant: result = x^y * z
while (y) {
if (y & 1) z *= x;
y >>= 1;
x *= x;
}
return z;
}
Maybe I am nitpicking, as your y cannot go beyond 5 without
overflowing the result...