itoa durch Shiftoperatoren - möglich?

Gast #2641904
Hi Leute!

Ist es möglich eine Art von itoa mittels Shiftoperationen zu machen?

Ich merke schon deutlich einen Speicherplatzunterschied wenn ich z.B. 
anstatt durch 32 teilen einen Rechtsshift um 5 mache. Das macht bei mir 
hier gerade einen Unterschied von 268 Byte aus (mein Controller hat 2k).

Ich habe hier meine eigene Funktion
1
void integer_to_single_numbers( int16_t source_number, uint8_t *dest_buffer )
2
{ 
3
  if( source_number < 0 )
4
  {
5
    shift_reg_control.display__sign = SIGN_NEGATIVE;
6
    source_number *= -1;
7
  }
8
  else
9
  {
10
    shift_reg_control.display__sign = NO_SIGN;
11
  }
12
  
13
  dest_buffer[0] = (source_number / 1000);
14
  source_number %= 1000;
15
  dest_buffer[1] = (source_number / 100);
16
  source_number %= 100;
17
  dest_buffer[2] = (source_number / 10);
18
  dest_buffer[3] = (source_number % 10);
19
}
die mir Zahlen in ihre Einzelteile zerlegt. Gibt es denn irgendeine 
Möglichkeit auch zu einer Zehnerpotenz ähnliches anzuwenden?
Gast #2641926
Optimierung ausgeschaltet? Der Compiler benutzt automatisch Shifts, wenn 
Zweierpotenzen im Spiel sind.

Wenn du Platz sparen willst, lass es lieber für solch triviale Sachen 
Structs zu benutzen.

Und nein, Shifts im Binärsystem bringen dir nichts im Dezimalsystem.
#2641944
Divisionen sind teuer. Aber das wirst Du auch schon festgestellt haben. 
In Assembler ließe sich sowas noch mehr optimieren, aber hast du mal 
etwas im Stil von dem da probiert:


1
void itoa (unsigned int input, char* digits)
2
//digits muss ein Array sein das hinreichend groß ist.
3
{
4
unsigned char ucplace=0;
5
unsigned int uidiv;
6

7
while (ucplace<MAXDIGITS)
8
{
9
  switch (ucplace)
10
  {
11
    case 0:
12
    {
13
      uidiv=10000;
14
      break;
15
    }
16
    case 1:
17
    {
18
      uidiv=1000;
19
      break;
20
    }
21
    ...//Die restlichen Zuweisungen, bis runter zu case 4: uidiv=1;
22
  }
23
  
24
  while (input >= uidiv)
25
  {
26
    input -= uidiv;
27
    digits[ucplace]++;
28
  }
29
}
30
return;
31
}

Hab ich jetzt nur mal runtergeschrieben und nicht ausprobiert, aber die 
Idee ist, so lang die höchste 10er Potenz abzuziehen (und die Anzahl zu 
zählen) bis der Rest kleiner ist, dann die nächstniedrigere 10er Potenz 
zählen usw.

Sollt klappen, schneller sein und weniger Platz fressen. In ASM könnte 
man hier noch einiges mehr optimieren... ich guck mal ob ich Zeit finde, 
brauchen kann man sowas bekanntlich immer mal.
#2641967
Zahlenausgabe schnell und codesparend, da ist die Subtraktionsmethode 
optimal:
1
void outint( int val )
2
{
3
  uint code TEST[] = { 10, 100, 1000, 10000 };
4
  uchar d, i;
5
  bit zero;
6
  uint uval = val;
7

8
  if( val < 0 ){
9
    uval = -val;
10
    putchar( '-' );
11
  }
12

13
  zero = 1;
14
  i = 4;
15
  do{
16
    i--;
17
    for( d = '0'; uval >= TEST[i]; uval -= TEST[i] ){
18
      d++;
19
      zero = 0;
20
    }
21
    if( zero == 0 )
22
      putchar( d );
23
  }while( i );
24

25
  putchar( (uchar)uval + '0' );
26
}


Peter
Gast #2642003
Harglbar schrieb:
> Optimierung ausgeschaltet? Der Compiler benutzt automatisch Shifts, wenn
> Zweierpotenzen im Spiel sind.

Hm. Von welcher Firma ist denn das Produkt "Der Compiler"?
Das sollten man schon wissen, bevor man solche Aussagen trifft.

Eine Optimierung, soweit vorhanden, sollte man aber schon einschalten.

Oliver
Gast #2644532
Hi und danke!

Ich habe es jetzt mit der Subtraktion implementiert und wieder 132Byte 
gespart. Super!
1
void integer_to_numbers( int16_t source_number, uint8_t *dest_buffer )
2
{
3
  uint16_t test_val[4] = { 1000, 100, 10, 1 };
4
  uint16_t temp_val;
5
  uint8_t count, number;
6
  
7
  temp_val = source_number;
8
  shift_reg_control.display__sign = NO_SIGN;
9
  
10
  if( source_number < 0 )
11
  {
12
    shift_reg_control.display__sign = SIGN_NEGATIVE;
13
    temp_val = -source_number;
14
  }
15
  
16
  count = 0;
17
  
18
  do
19
  {    
20
    for( number = 0; temp_val >= test_val[count]; temp_val -= test_val[count] )
21
    {
22
      number++;
23
    }
24
    
25
    dest_buffer[count] = number;  
26
    count++;
27
    
28
  } while( count < 4 );
29
}
Gast #2644902
> wieder 132Byte gespart.
1
void integer_to_numbers(int16_t source_number, uint8_t *dest_buffer)
2
{
3
    char *buf[6];
4

5
    if(source_number<0) 
6
    { 
7
        *dest_buffer++='-'; 
8
        source_number=-source_number; 
9
    }
10
    buf[i=5]='\0'; // terminiert nachher den dest_buffer
11
    do // immer mindestens eine 0
12
    {
13
       buf[--i]=source_number%10;
14
       source_number/=10;
15
    }
16
    while(source_number); // kann auch 5 Stellen, führende 0 unterdrückt
17
    while(i<=5)
18
    {
19
        *dest_buffer++=buf[i++];
20
    }
21
}
braucht sicher weniger Speicher.
Gast #2645595
Sven schrieb:
> (mein Controller hat 2k)

So, 2k WAS? Dein Controller hat nur 2 K Byte Befehlsspeicher oder 2 K 
Befehle? Ist also vermutlich ein kleiner 8 Bitter.

Und da willst du solche Hilfsroutinen wie das Ausgabekonvertieren von 
Integers in C schreiben, ohne dich intensiv um die in den mitgelieferten 
Bibliotheken vorhandenen Funktionen zu kümmern. Ah ja.

Such einfach mal nach der Funktion ldiv. Die müßte es bei IAR auch für 
die kleineren uC geben - und wenn du sie gefunden hast, dann schreib 
deine Ziffernroutine neu.

Ich geb dir hier mal einen Ausschnitt aus ner Konvertierung (die 
Formatierungen hab ich weggelassen):

long LtoStr (long aValue, int digits, char* Buffer, int BufLen)
  ...
  i = BufLen - 1;
  Buffer[i--] = 0;    /* Endemarker setzen */
  if (aValue<0)
       { L = -aValue;
         v = 1;
       }
  else { L = aValue;
         v = 0;
       }

  if (!L)  Buffer[i--] = '0';
  while (i)
  { if (!L) goto _lready;
    T = ldiv(L,10);
    L = T.quot;
    Buffer[i--] = '0'+T.rem;
  }

                     /* i = 0 heisst: der Buffer ist voll! */
  i = 0; j = BufLen - 2;
  Buffer[i++] = '>';
  while (i<j) Buffer[i++] = '9';
  return aValue;

_lready:
  ...


Aber bei einem uC mit nur 2 K Programmspeicher würde ich zumindest 
solche Routinen in Assembler schreiben und nicht ldiv, sondern die von 
Peter Dannegger skizzierte Subtraktionsmethode benutzen. Die braucht 
lediglich Subtraktionen und ein paar Zehnerpotenzen als Konstanten.

W.S.

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 14 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.