itoa durch Shiftoperatoren - möglich?

Gast #2641904
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

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
Lesenswert?

> 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
Lesenswert?

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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren