# Float to int conversion not working right in method using varargs

I am trying to convert a floating point number to an integer, but it seems like the conversion is not working properly.

I am using the math library to calculate the power of 10 based on the number of varargs, where the first argument is the number of arguments followed by the varargs.

``````int charsToIntValue(int argc, ...) {
int multiplier = (int) pow(10, argc - 1);
va_list argp;
va_start(argp, argc);
Serial.println(multiplier);
...
va_end(argp);
}
``````

For some reason, `multiplier` is always stored as 1 less than the actual result. e.g. If `charsToIntValue(4, 1, 2, 3, 4)` is executed, the multiplier is stored as 999 rather than 1000. However, when it is printed as a float, it does show 1000.00. And whenever I change it to a constant (`(int) pow(10, 3)`) it works fine, returning 1000.

I've tired storing the value of argc into a local variable but that didn't seem to change anything.

Full project is here for context: https://github.com/Pyrodron/charger-destination-board

File in question is charger-destination-board.ino; lines 32 to 51

• A minimal test setup around your `charsToIntValue` doesn't show me what you're talking about. I don't think this has anything strictly to do with variadic function support. Nor Arduino. I will just add that `pow(10, argc - 1);` could be better written as a small function that takes an `int`, returns an `int`, and consists of a switch statement with no `float` at all. Feb 2 at 0:03
• I'm currently not sure, if `pow()` returns a float. If yes, that that behavior seems normal. Floats can only save an aproximation of the wanted number. The actual float value, that is calculated might be something like 999.99. When you them convert to an int, the digits behind the decimal point get thrown away, leaving 999. Thats something, that you would need to accept, when you want to work with floats. And thats one reason, why it is often better, to just use an integer representation. Feb 2 at 7:21
• @timemage I took inspiration from your comment and implemented my own power function rather than using the `math.h` library. It works now!
– Pyro
Feb 2 at 14:25

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!

• I'd had some doubt about this actually solved the problem, because I'd tested the handful of exponent values you were using to see if the `pow()` happened to produce the values you wanted. Turns out avr-gcc will use higher precision if the optimizer can determine at compile time irrespective of whether it's doing this is a `constexper` context, meaning so much as having an argument something volatile qualified will cause `pow()` to evaluate at lower precision. Just more reason to not use `float` on an AVR. Feb 2 at 16:55
• When the optimizer kicked in `pow(10, 4)`'s result had significand field `0b00111000011111111111110` and where the optimizer couldn't determine the exponent at compile time a significand field of `0b00111000100000000000000`. The former converts to `int` with the expected (for certain values of "expected") 10000 while the latter produces a value closer to 9984. Something also you should be aware of is that in avr-gcc, there really is no `double`. It's a distinct type for overload purposes, but it's implemented as single precision `float`. Feb 2 at 16:59
• @timemage all helpful information. Thank you
– Pyro
Feb 4 at 0:23

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...

• "works fine with compile-time constants because" as I said in the the comments on the O.P.'s own answer, it's actually more complicated than that. It has nothing to do with whether or not they're technically "constant". You can elicit the difference in this behaviour without crossing between constant expressions and non-constant expressions. The standard makes provisions for differences in precision between the two cases. So that is fin. But, the fact that it will give two completely different results outside the constexpr context is probably a bug. Feb 2 at 17:43