Stellen eines (signed) integer ermitteln

Also, ich will eigentlich die Stellen eines signed integers ermitteln, inkl. einer Stelle für ein negatives Vorzeichen wenn notwendig.

Jetzt habe ich 3 1/2 Varianten, alle sehen etwas anders aus und alle kompilieren für den Uno mit gleich viel Flash/RAM

// returns the needed digits for a signed number

// Variante 1
uint8_t neededDigits(int32_t value)  // 1750/204
{
  if (value == 0) return 1;  // alternative ohne  einen expliziten if für 0?
  uint8_t digits = 0;
  if (value < 0)
  {
    digits++;
    value *= -1;
  }
  while (value > 0)
  {
    value /= 10;
    digits++;
  }
  return digits;
}


// Variante 2                     // Quelle: https://www.mikrocontroller.net/topic/79744
uint8_t neededDigits2(int32_t n)  //1750/204
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}


//Variante 3
uint8_t neededDigits3(int32_t value)  //1750/204
{
  uint8_t digits = 1;
  if (value < 0)
  {
    digits++;
    value *= -1;
  }
  while (value >= 10)
  {
    value /= 10;
    digits++;
  }
  return digits;
}



/*
//Variante 4
//ruft Variante 2 auf die in diesem Fall wirklich neededDigits2 heißen muss
uint8_t neededDigits2(int32_t value)   //1750 / 204
{
  if (value < 0)
    return neededDigits2(value * -1) + 1;
  else
    return neededDigits2(value);
}
*/


void setup()
{
  Serial.begin(115200);
  Serial.println("Stellen zaehlen");

  Serial.println(neededDigits(0));
  Serial.println(neededDigits(1));
  Serial.println(neededDigits(10));
  Serial.println(neededDigits(333));
  Serial.println(neededDigits(-4444));
}

void loop()
{

}

Der Sketch verwendet 1750 Bytes (5%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 204 Bytes (9%) des dynamischen Speichers, 1844 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Egal welche Variante ich aktiviere. Immer das gleiche. Zum aktivieren benne ich die jeweils gewünsche Funktion um auf neededDigits (aber auch wenn ich es mit /* auskommentiere,…)

Begonnen habe ich mit Variante 1. Da habe ich für den Wert 0 extra ein if benötigt, was mir nicht so gefallen hat und ergo weitergesucht habe.

Variante 2 finde ich supersexy (nicht von mir - Quelle steht dabei), nur kann die so halt kein signed (ergo habe ich nur 3 1/2 Lösungen).

Variante 3 wäre eine Derivat von 1 ohne einen extra if für 0 und von der Lesbarkeit eigentlich mein Favorit.

Variante 4: ist eigentlich nur eine Helper Funktion die dann Variante 2 aufruft und somit für negative Zahlen das richtige Ergebnis bringt.

Was ich nicht verstehe und ich somit euch Frage:
Warum kompilieren alle 4 Varianten mit scheinbar (?) dem gleichen Programm-Speicherbedarf?

noiasca:
Warum kompilieren alle 4 Varianten mit scheinbar (?) dem gleichen Programm-Speicherbedarf?

Wie stellst Du das fest? Zumindest Variante 4 sollte länger werden als Variante 2.

Vermutlich schleppt Serial.println() den ganzen Overhead mit ein, der Rest macht nur noch ein paar Bytes.

Variante 4 (und 2) könnte kürzer und marginal schneller werden mit
if (value < 0)
return neededDigits2(-value) + 1;
Kommt drauf an, wie clever der Compiler ist.

DrDiettrich:
Wie stellst Du das fest? Zumindest Variante 4 sollte länger werden als Variante 2.

die Angaben nach dem Kompilieren sind bei allen 4 Varianten gleich.

Der Sketch verwendet 1750 Bytes (5%) des Programmspeicherplatzes. Das Maximum sind 32256 Bytes.
Globale Variablen verwenden 204 Bytes (9%) des dynamischen Speichers, 1844 Bytes für lokale Variablen verbleiben. Das Maximum sind 2048 Bytes.

Wie machst Du die Varianten?

Zum aktivieren benenne ich die jeweils gewünschte Funktion um auf neededDigits , die anderen erhalten einfach eine Zahl hinten dran damit sie nicht benötigt werden (und somit auch nicht mit kompiliert werden).

aber auch wenn ich die anderen Funktionen mit /* */ auskommentiere, bleibt die Compiler Angabe gleich.

Hallo,
das liegt am Optimieralgorithmus des Compilers. Der ist manchmal erstaunlich schlau und/oder bringt überraschende Ergebnisse.

Wenn Du am Anfang die Zeile

#pragma GCC optimize "O0"

einfügst schaltest Du das Optimieren aus. Dann bekommst Du auch bei jeder Variante unterschiedliche Ergebnisse ( musst die nicht genutzten dann aber auch komplett auskommentieren oder per Precompiler unterdrücken )

Kannst du die Zahl nicht mit itoa wandeln und auf strlen prüfen? Nur so als Idee

ElEspanol:
… mit itoa wandeln …

itoa() ist nicht standardkonform und wird nicht von jedem Compiler übersetzt. Wenn einem das wurscht ist, ist’s ok.

Gruß

Gregor

noiasca:
aber auch wenn ich die anderen Funktionen mit /* */ auskommentiere, bleibt die Compiler Angabe gleich.

Das klingt verdächtig danach, daß der Compiler aus irgendwelchen Gründen alles wegoptimiert. Schalte testhalber mal alle Optimierungen ab.

Hallo,
da er long-Werte berechnenwill, muss er ‘ltoa’ nehmen.

Interessant ist, dass bei einer Erweiterung und Umstellung des Codes jetzt auch mit Optimierung unterschiedlche Längen heruaskommen. Di Variante von EIEspanol habe ich auch aufgenommen ( Variante 5 ).
Die aktive Variante kann man in der 1. Zeile einstellen.

#define VARIANT 5

//#pragma GCC optimize "O0"

// returns the needed digits for a signed number

#if (VARIANT == 1)
// Variante 1
uint8_t neededDigits(int32_t value)  // 1750/204
{
  if (value == 0) return 1;  // alternative ohne  einen expliziten if für 0?
  uint8_t digits = 0;
  if (value < 0)
  {
    digits++;
    value *= -1;
  }
  while (value > 0)
  {
    value /= 10;
    digits++;
  }
  return digits;
}
#endif

#if (VARIANT == 2)

// Variante 2                     // Quelle: https://www.mikrocontroller.net/topic/79744
uint8_t neededDigits(int32_t n)  //1750/204
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}
#endif

#if (VARIANT == 3)

//Variante 3
uint8_t neededDigits(int32_t value)  //1750/204
{
  uint8_t digits = 1;
  if (value < 0)
  {
    digits++;
    value *= -1;
  }
  while (value >= 10)
  {
    value /= 10;
    digits++;
  }
  return digits;
}
#endif


#if (VARIANT == 4)

//Variante 4
// Variante 2                     // Quelle: https://www.mikrocontroller.net/topic/79744
uint8_t neededDigits2(int32_t n)  //1750/204
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}
//ruft Variante 2 auf die in diesem Fall wirklich neededDigits2 heißen muss
uint8_t neededDigits(int32_t value)   //1750 / 204
{
  if (value < 0)
    return neededDigits2(value * -1) + 1;
  else
    return neededDigits2(value);
}
#endif

#if (VARIANT == 5)

//Variante 3
uint8_t neededDigits(int32_t value)  //1750/204
{ char tempStr[10];
  
  return strlen( ltoa( value, tempStr, 10 ) );
}
#endif


int32_t werte[] = { 0,1,10,333,-444, 12345, -100234 };
void setup()
{
  Serial.begin(115200);
  Serial.println("Stellen zaehlen");
  for ( auto val : werte ) {
    Serial.print( val ); Serial.print(" needs ");
    Serial.println(neededDigits(val));
  }
}

void loop()
{

}

Was hindert euch, den generierten Assemblercode anzusehen?

Die Bequemlichkeit :grin: :wink:

Bei der ITOA Variante must du den buffer auf 12 vergrößern. 10 Stellen, Vorzeichen, Nullterminierung
https://de.wikibooks.org/wiki/C-Programmierung_mit_AVR-GCC/_Datentypen

Stimmt, da hatte ich mich wohl bei den Stellen etwas verzählt ...

zunächst danke für die Rege Beteiligung. Mein Broterwerb lässt keine Versuche unter tags zu, geht nur Abends.

mal ein Tests durchgeführt

// Stellen einer Zahl ermitteln
// https://forum.arduino.cc/index.php?topic=634400.0


/*
     | Compiler - Standard     |  optimize "00"
  Var   Sketch   Global   micros   Sketch    Global   micros
  1   2122     254      3788     2830      254      3820
  2   2158     254      3716     2828      254      3716
  3   2120     254      3752     2810      254      3772
  4   2164     254      3716     2924      254      3720
  5   2176     254      3760     2842      254      3760
*/


#define VARIANT 2
//#pragma GCC optimize "O0"

#if (VARIANT == 1)
uint8_t neededDigits(int32_t value)
{
  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 n)    // Quelle: https://www.mikrocontroller.net/topic/79744
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}
#endif

#if (VARIANT == 3)

//Variante 3
uint8_t neededDigits(int32_t value)
{
  uint8_t digits = 1;
  if (value < 0)
  {
    digits++;
    value *= -1;
  }
  while (value >= 10)
  {
    value /= 10;
    digits++;
  }
  return digits;
}
#endif


#if (VARIANT == 4)

uint8_t neededDigits2(int32_t n)  // Quelle: https://www.mikrocontroller.net/topic/79744
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}
//ruft Variante 2 auf die in diesem Fall wirklich neededDigits2 heißen muss
uint8_t neededDigits(int32_t value)
{
  if (value < 0)
    return neededDigits2(value * -1) + 1;
  else
    return neededDigits2(value);
}
#endif

#if (VARIANT == 5)
uint8_t neededDigits(int32_t value)
{
  char tempStr[10];
  return strlen( ltoa( value, tempStr, 12 ) );
}
#endif


int32_t werte[] = { 0, 1, 10, 333, -444, 12345, -100234 };
void setup()
{
  Serial.begin(115200);
  Serial.println("Stellen zaehlen");
  uint32_t start = micros();
  for ( auto val : werte ) {
    Serial.print( val );
    Serial.print(" needs ");
    Serial.println(neededDigits(val));
  }
  uint32_t total = micros() - start;
  Serial.print("Durchlaufzeit "); Serial.println(total);
}

void loop()
{

}

Warum es nun doch unterschiedliche Werte gibt, ist eher das nächste Mysterium.
Warum der noiasca nicht in Assembler nachschaut ist einfach erklärt, weil ihm das nichts sagt.

aha, irgendwas ist an mircobahn’s code anders…

// Stellen einer Zahl ermitteln
// https://forum.arduino.cc/index.php?topic=634400.0


/*
  Bei Ausgabe = 1 noiasca
       | Compiler - Standard     |  optimize "00"
  Var   Sketch   Global   micros   Sketch    Global   micros
  1     1922     218      408      2630      218      892
  2     1922     218      408      2628      218      452
  3     1922     218      408      2610      218      712
  4     1922     218      408      2724      218      468
  5     2166     218      604      2642      218      632
  
  
 
  Bei Ausgabe = 2 microbahn
       | Compiler - Standard     |  optimize "00"
  Var   Sketch   Global   micros   Sketch    Global   micros
  1     2166     254      3796     2848      254      3820
  2     2206     254      3716     2846      254      3720
  3     2174     254      3752     2828      254      3768
  4     2214     254      3716     2942      254      3720
  5     2192     254      3716     2860      254      3720
*/


#define VARIANT 5
//#pragma GCC optimize "O0"

#define AUSGABE 1             // 1 noiasca        2 microbahn

#if (VARIANT == 1)
uint8_t neededDigits(int32_t value)
{
  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 n)    // Quelle: https://www.mikrocontroller.net/topic/79744
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}
#endif

#if (VARIANT == 3)

//Variante 3
uint8_t neededDigits(int32_t value)
{
  uint8_t digits = 1;
  if (value < 0)
  {
    digits++;
    value *= -1;
  }
  while (value >= 10)
  {
    value /= 10;
    digits++;
  }
  return digits;
}
#endif


#if (VARIANT == 4)

uint8_t neededDigits2(int32_t n)  // Quelle: https://www.mikrocontroller.net/topic/79744
{
  if (n < 100000) {
    if (n < 1000) {
      if (n < 100) {
        if (n < 10)
          return 1;
        return 2;
      }
      return 3;
    }
    if (n < 10000)
      return 4;
    else
      return 5;
  }
  if (n < 100000000) {
    if (n < 10000000) {
      if (n < 1000000)
        return 6;
      return 7;
    }
    return 8;
  }
  if (n < 1000000000)
    return 9;
  else
    return 10;
}
//ruft Variante 2 auf die in diesem Fall wirklich neededDigits2 heißen muss
uint8_t neededDigits(int32_t value)
{
  if (value < 0)
    return neededDigits2(value * -1) + 1;
  else
    return neededDigits2(value);
}
#endif

#if (VARIANT == 5)
uint8_t neededDigits(int32_t value)
{
  char tempStr[12];
  return strlen( ltoa( value, tempStr, 10 ) );
}
#endif


void setup()
{
  Serial.begin(115200);
  Serial.println("Stellen zaehlen");
  uint32_t start = micros();
#if (AUSGABE == 1)
  Serial.println(neededDigits(0));
  Serial.println(neededDigits(1));
  Serial.println(neededDigits(10));
  Serial.println(neededDigits(333));
  Serial.println(neededDigits(-4444));
#endif


#if (AUSGABE == 2)
  int32_t werte[] = { 0, 1, 10, 333, -444, 12345, -100234 };
  for ( auto val : werte ) {
    Serial.print( val );
    Serial.print(" needs ");
    Serial.println(neededDigits(val));
  }
#endif
  uint32_t total = micros() - start;
  Serial.print("Durchlaufzeit "); Serial.println(total);
}


void loop()
{

}

Einzige Erkenntnis: Variante 5 kommt mir nicht ins Haus.
Ich glaube ich suche mir jetzt ein neues Hobby…
edit - kleinen Fehler noch ausgebessert.
edit2: noch mal input von microbahner eingearbeitet

noiasca:
aha, irgendwas ist an mircobahn's code anders...

Den Unterschied machen nur die anderen Aufrufe im Setup aus. Wenn Du da wieder deinen Code reinsetzt sind alle Varianten wieder gleich groß :o . Vielleicht sollte man doch mal in den Assembler code reinschauen ...

noiasca:
edit - kleinen Fehler noch ausgebessert.

Die 12 gehört in die Grössendefinition des char arrays, nicht in den ltoa-Befehl :wink:

MicroBahner:
Die 12 gehört in die Grössendefinition des char arrays, nicht in den ltoa-Befehl :wink:

oki, verbessert etwas die Performance, hab die vier betroffenen Versuche aktualisiert.

übrigens, nicht alle sind gleich groß: nur 1-4.

So, hab' mir mal den Assemblercode angesehen.

Die Ursache für die gleichlangen Sketche liegt darin, dass Du deine 'neededDigits' immer nur mit Konstanten aufrufst. Der Compiler geht nun einfach daher, und berechnet das Ergebnis anhand deiner Funktionen bereits zur Compilezeit. D.h. im Assemblercode gibt es die gar nicht, und deshalb sind alle Varianten gleich lang.
Man kann das im Assembler schön sehen, da die println-Funktion direkt mit dem richtigen Ergebnis aufgerufen wird. Einen Funktionsaufruf von 'neededDigits' gibt's da gar nicht mehr.

Nur bei der Variante mit ltoi get das wegen dem Aufruf von ltoi nicht.

Super Erklärung! Danke Microbahner.
Karma!