if you are looking for speed, see this trials we did some years ago:
// Stellen einer Zahl ermitteln
// https://forum.arduino.cc/index.php?topic=634400.0
/*
Bei Ausgabe = 2 microbahn (Zeitberechnung ohne Ausgaben) - offizielles Testset per 4.9.2019
| Compiler - Standard | random | optimize "00"
Var Sketch Global micros | millis | Sketch Global micros
1 2214 262 1296 640 2934 262 1412 OK // Division mit extra ifs
2 2254 262 40 199 3004 262 108 OK // geschachteltes If
3 2208 262 1016 561 2914 262 1124 OK // Division (ein If weniger als v1)
4 2244 262 40 198 2986 262 108 OK // geschachteltes If, invers
5 2228 262 940 438 2926 262 1000 OK // ltoa
6 3308 262 1204 - 4016 262 1260 NOK // log 10
7 2240 262 112 235 2942 262 196 OK // Multiplikation invers mit ifs
8 2242 262 44 203 2978 262 108 OK // switch Variante
9 2224 262 44 202 2976 262 120 OK // switch Variante, invers
10 2196 262 1460 583 2894 262 1576 OK // Division, invers (aber langsamer als v3)
11 2204 414 252 254 2922 400 400 OK // Suche in Array/Potenzen
12 2572 262 904 496 3268 262 772 OK // Multiplikation 64bit
13 2242 262 40 200
14 2246 262 40 200
*/
#define VARIANT 14
//#pragma GCC optimize "O0"
#define AUSGABE 9 // 1 noiasca 2 microbahn 9 random_checker
#if (VARIANT == 1)
uint8_t neededDigits(int32_t value) // V1 Division mit extra ifs
{
if (value == -2147483648) return 11; // notwendig, da sonst bei GCC optimize "O0" für diesen Wert ein falscher errechnet würde
if (value == 0) return 1;
uint8_t digits = 0;
if (value < 0)
{
digits++;
value *= -1;
}
while (value > 0)
{
value /= 10;
digits++;
}
return digits;
}
#endif
#if (VARIANT == 2)
uint8_t neededDigits(int32_t value) // V2 geschachteltes If, Ausgangsbasis: https://www.mikrocontroller.net/topic/79744 inkl. signed
{
if (value == -2147483648) return 11; // hack für -2147483648
uint8_t sign = 0;
if (value < 0) {
value = -value;
sign = 1;
}
if (value < 100000) {
if (value < 1000) {
if (value < 100) {
if (value < 10)
return 1 + sign;
return 2 + sign;
}
return 3 + sign;
}
if (value < 10000)
return 4 + sign;
else
return 5 + sign;
}
if (value < 100000000) {
if (value < 10000000) {
if (value < 1000000)
return 6 + sign;
return 7 + sign;
}
return 8 + sign;
}
if (value < 1000000000)
return 9 + sign;
else
return 10 + sign;
}
#endif
#if (VARIANT == 3)
uint8_t neededDigits(int32_t value) // V3 Division - optimiert wegen 0
{
if (value == -2147483648) return 11; // hack
uint8_t digits = 1;
if (value < 0)
{
digits++;
value *= -1;
}
while (value >= 10)
{
value /= 10;
digits++;
}
return digits;
}
#endif
#if (VARIANT == 4) // Geschachteltes If, umgebaut auf negative Logik
uint8_t neededDigits(int32_t value) // V4 Derivat von : https://www.mikrocontroller.net/topic/79744
{
uint8_t sign = 1;
if (value >= 0) {
value = -value;
sign = 0;
}
if (value > -100000) {
if (value > -1000) {
if (value > -100) {
if (value > -10)
return 1 + sign;
return 2 + sign;
}
return 3 + sign;
}
if (value > -10000)
return 4 + sign;
else
return 5 + sign;
}
if (value > -100000000) {
if (value > -10000000) {
if (value > -1000000)
return 6 + sign;
return 7 + sign;
}
return 8 + sign;
}
if (value > -1000000000)
return 9 + sign;
else
return 10 + sign;
}
#endif
#if (VARIANT == 5)
uint8_t neededDigits(int32_t value) // V5 ltoa
{
char tempStr[12];
return strlen( ltoa( value, tempStr, 10 ) );
}
#endif
#if (VARIANT == 6)
uint8_t neededDigits(int32_t value) // V6 falsch -2147483648 needs 2 und bei Stellenüberläufen 999999 und größer
{
if ( value < 0 )
return static_cast<unsigned>(log10(-value)) + 2;
else
return static_cast<unsigned>(log10(value)) + 1;
}
#endif
#if (VARIANT == 7)
uint8_t neededDigits(int32_t value) // V7
{
if ( value > 999999999) return 10; // keine Überschreitung der Multiplikation bei 32bit
if ( value < -999999999) return 11; // keine Überschreitung der Multiplikation bei 32bit
//if ( value == 0) return 1;
uint8_t digits = 1;
int32_t temp = -1;
int32_t absolut = value;
if (value > 0)
{
digits = 0;
absolut = -value;
}
while (absolut <= temp)
{
temp *= 10;
digits++;
}
return digits;
}
#endif
#if (VARIANT == 8)
uint8_t neededDigits(int32_t value) { // V8:
if (value == -2147483648) return 11; // hack
byte digit = 0;
if (value < 0) {
value = -value;
digit++;
}
switch (value) {
case 1000000000L ... 2147483647L: digit++;
case 100000000 ... 999999999: digit++;
case 10000000 ... 99999999: digit++;
case 1000000 ... 9999999: digit++;
case 100000 ... 999999: digit++;
case 10000 ... 99999: digit++;
case 1000 ... 9999: digit++;
case 100 ... 999: digit++;
case 10 ... 99: digit++;
case 0 ... 9: digit++;
default: break;
}
return digit;
}
#endif
#if (VARIANT == 9) // OK
uint8_t neededDigits(int32_t var) {
byte digit = 1;
if (var >= 0) {
var = -var;
digit--;
}
switch (var) {
case -2147483648 ... -1000000000: digit++;
case -999999999 ... -100000000: digit++;
case -99999999 ... -10000000: digit++;
case -9999999 ... -1000000: digit++;
case -999999 ... -100000: digit++;
case -99999 ... -10000: digit++;
case -9999 ... -1000: digit++;
case -999 ... -100: digit++;
case -99 ... -10: digit++;
case -9 ... 0: digit++;
default: break;
}
return digit;
}
#endif
#if (VARIANT == 10)
uint8_t neededDigits(int32_t value) // OK 10 Division mit inverse Logik (korrigiert 3)
{
uint8_t digits = 2;
if (value >= 0)
{
digits--;
value *= -1;
}
while (value <= -10)
{
value /= 10;
digits++;
}
return digits;
}
#endif
#if (VARIANT == 11)
uint8_t neededDigits(int32_t value) // Array lesen
{
const int32_t threshold[] = { -10, -100, -1000, -10000, -100000, -1000000, -10000000, -100000000, -1000000000, -1000000000};
const uint8_t indexes = sizeof(threshold) / sizeof(threshold[0]);
uint8_t sign = 1;
if (value >= 0)
{
value = value * -1;
sign = 0;
}
for (int8_t i = 0; i < indexes; i++)
{
if (value > threshold[i]) return i + 1 + sign;
}
return 10 + sign;
}
#endif
#if (VARIANT == 12)
uint8_t neededDigits(int32_t value) { // ok 64bit aber langsam (korrigierte Multiplikation)
if (value == -2147483648) return 11;
uint8_t count = 0;
uint64_t temp = 1;
uint64_t absolut = (uint64_t)abs(value);
if ( value == 0 ) return 1;
while (absolut >= temp )
{
temp *= 10;
count++;
}
return value > 0 ? count : count + 1;
}
#endif
#if (VARIANT == 13) // ziemlich gut verschachtelter IF, aufsteigend
uint8_t neededDigits(int32_t value) // Derivat von : https://www.mikrocontroller.net/topic/79744
{
uint8_t sign = 1;
if (value >= 0) {
value = -value;
sign = 0;
}
if (value > -1000)
{
if (value > -10) return 1 + sign;
if (value > -100) return 2 + sign;
return 3 + sign;
}
if (value > -1000000)
{
if (value > -10000) return 4 + sign;
if (value > -100000) return 5 + sign;
return 6 + sign;
}
if (value > -100000000)
{
if (value > -10000000) return 7 + sign;
if (value > -100000000) return 8 + sign;
}
if (value > -1000000000) return 9 + sign;
return 10 + sign;
}
#endif
#if (VARIANT == 14) // Spielwiese
uint8_t neededDigits(int32_t value) // Derivat von : https://www.mikrocontroller.net/topic/79744
{
uint8_t sign = 1;
if (value >= 0) {
value = -value;
sign = 0;
}
if (value > -10000)
{
if (value > -10) return 1 + sign;
if (value > -100) return 2 + sign;
if (value > -1000) return 3 + sign;
return 4 + sign;
}
if (value > -10000000)
{
if (value > -100000) return 5 + sign;
if (value > -1000000) return 6 + sign;
return 7 + sign;
}
if (value > -100000000) return 8 + sign;
if (value > -1000000000) return 9 + sign;
return 10 + sign;
}
#endif
#if (AUSGABE == 9)
uint8_t checker(int32_t var) { // Reference Function: Annahme - das ist eine korrekte (und schnelle) Variante)
byte digit = 1;
if (var >= 0) {
var = -var;
digit--;
}
switch (var) {
case -2147483648 ... -1000000000: digit++;
case -999999999 ... -100000000: digit++;
case -99999999 ... -10000000: digit++;
case -9999999 ... -1000000: digit++;
case -999999 ... -100000: digit++;
case -99999 ... -10000: digit++;
case -9999 ... -1000: digit++;
case -999 ... -100: digit++;
case -99 ... -10: digit++;
case -9 ... 0: digit++;
default: break;
}
return digit;
}
byte doCheck(int32_t i) //Die eigentliche Vergleichsfunktion
{
//Serial.println(i);
byte evaluate = neededDigits(i);
byte verified = checker(i);
if (verified != evaluate)
{
Serial.print(i);
Serial.print(" needs ");
Serial.print(verified);
Serial.print(" wrong ");
Serial.println(evaluate);
return 1;
}
else
return 0;
}
#endif
void setup()
{
Serial.begin(115200);
Serial.println("Stellen zaehlen ");
#if (AUSGABE == 1)
uint32_t start = micros();
Serial.println(neededDigits(0));
Serial.println(neededDigits(1));
Serial.println(neededDigits(10));
Serial.println(neededDigits(333));
Serial.println(neededDigits(-4444));
uint32_t total = micros() - start;
Serial.print("Durchlaufzeit "); Serial.println(total);
#endif
#if (AUSGABE >= 8) || (AUSGABE == 2)
//int32_t werte[] = { 0, 1, 10, 333, -444, -2147483648, -100234 };
//int32_t werte[] = { 0, 1, 10, 333, -444, 12345, -100234 }; // alte Prüfwerte für Übersichtstabelle
int32_t werte[] = { 0, 1, 10, 333, -444, 12345, -100234, 2147483647, -2147483648 }; // offizielles Testset per 4.9.2019
const byte WERTEZAHL = sizeof(werte) / sizeof(werte[0]);
#endif
#if (AUSGABE == 2)
uint8_t stellen[WERTEZAHL];
uint32_t start = micros();
for (uint8_t i = 0; i < WERTEZAHL; i++ ) {
stellen[i] = neededDigits( werte[i] );
}
uint32_t total = micros() - start;
Serial.print("Durchlaufzeit "); Serial.println(total);
for (uint8_t i = 0; i < WERTEZAHL; i++ ) {
Serial.print( werte[i] );
Serial.print(" needs ");
Serial.println(stellen[i]);
}
#endif
#if (AUSGABE == 8)
Serial.println("Masstest");
uint16_t testsPerDigit = 10000;
uint32_t errorcounter = 0;
uint32_t testcounter = 0;
int32_t from = 10;
int32_t to = 101;
uint32_t startMs = millis();
for (uint16_t y = 0; y < testsPerDigit; y++)
{
int32_t i = random(from, to); //(min incl, max excl);
errorcounter = neededDigits(i);
testcounter++;
}
uint32_t totalMs = millis() - startMs;
Serial.print(F("last result: ")); Serial.println(errorcounter);
Serial.print(F("Runtime in ms: ")); Serial.println(totalMs);
#endif
#if (AUSGABE == 9) // vergleicht die Ergebnisse der ausgewälten Variante gegen den checker
Serial.println("random checker ...");
uint8_t testsPerDigit = 100;
uint32_t errorcounter = 0;
uint32_t testcounter = 0;
randomSeed(analogRead(A0));
// replacement for pow()
int32_t fuckPow [] = { -2147483648,
-1000000000, -100000000, -10000000, -1000000, -100000, -10000, -1000, -100, -10,
-1,
0,
9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999,
2147483647
};
const uint8_t values = sizeof(fuckPow) / sizeof(fuckPow[0]);
uint32_t startMs = millis();
for (uint8_t z = 1; z < values; z++) // Check random values (n Random numbers for each digit)
{
int32_t from = fuckPow[z - 1] + 1;
int32_t to = fuckPow[z];
//Serial.print(from); Serial.print(" to "); Serial.println(to); // debug only
for (uint8_t y = 0; y < testsPerDigit; y++)
{
int32_t i = random(from, to); //(min incl, max excl);
errorcounter += doCheck(i);
testcounter++;
}
}
for (uint8_t z = 1; z < values; z++) //
{
errorcounter += doCheck(fuckPow[z] - 1);
errorcounter += doCheck(fuckPow[z]);
errorcounter += doCheck(fuckPow[z] + 1);
testcounter += 3;
}
for (uint8_t i = 0; i < WERTEZAHL; i++ ) // The testset itself
{
errorcounter += doCheck(werte[i]);
testcounter++;
}
uint32_t totalMs = millis() - startMs;
Serial.print(F("Tests performed: ")); Serial.println(testcounter);
Serial.print(F("Total erros: ")); Serial.print(errorcounter); if (!errorcounter) Serial.print(F(" - Test passed!")); Serial.println();
Serial.print(F("Runtime in ms: ")); Serial.println(totalMs);
#endif
}
void loop()
{
}
learning:
"switch case" variants are fast.
signed numbers can be tricky, as we have 2147483647 but -2147483648 in a int32_t
if you only need uint8_t you can speed up it even more.
#define VARIANT 14 // will set one of the given examples
#define AUSGABE 9 // translates to "output" ... use 1, 2 or 9