Forum: Compiler & IDEs Negative Ganzzahlen von 32bit in 16bit konvertieren


von Ingo B. (tueddler)


Lesenswert?

Hallo,

ich beschaeftige mich seit einiger Zeit mit AVRs, wobei ich bisher 
hauptsaechlich in Assembler programmiert habe. Seit ueber einem Jahr bin 
ich regelmaessig im Microkotroller Froum unterwegs, und alle meine 
Fragen konnten mit Hilfe der SuFu beantwortet werden (Danke an dieser 
Stelle fuer die zahlreichen kompetenten Beitraege). Jetzt stehe ich aber 
irgendwie bei meinen ersten gcc-Versuchen auf dem Schlauch. Und zwar 
moechte ich den Wert einer ADC-Wandlung in den Typ int16_t konvertieren, 
der einer Spannung im Wertebereich von -maxVoltage bis +maxVoltage 
(minus 1LSB) entspricht (die Null-Referenz der zu messenden Spannung 
liegt ca. bei der Haelfte von Vref, die Zahl entspricht der Spannung in 
hundertstel Volt). Um das Ganze ohne float zu realisieren, brauche ich 
fuer die Zwischenrechnung den Typ int32_t, der nach der Division durch 
512 (bei positiven Zahlen waere das 1 byte und 1 bit rechtsschieben) 
wieder in int16_t umgewandelt werden kann. Hier der (fehlerhafte) Code:

static int16_t getVoltage()
{
  // ADC output auf Wertebereich von -maxVoltage bis +maxVoltage (-1LSB) 
mappen:
  return (int16_t)((((int32_t)(ADC_Read(0)-512+ADCzero))*maxVoltage) / 
512 );
}

maxVoltage liegt etwa bei 500 (Versorgungsspannung). Bei positiven 
Zahlen funktioniert das wunderbar, bei negativen wird aber das MSB 
(Minuszeichen) nicht richtig verarbeitet, so dass Datenmuell dabei 
rauskommt. Einzige Loesung, die mir einfaellt, ist eine if/else 
Verzweigung, um negative Zahlen gesondert zu behandeln. Gibt es da in C 
nicht eine elegantere (und trotzdem effiziente) Vorgehensweise?

Vielen Dank!
Ingo.

von Ralf G. (ralg)


Lesenswert?

Müsste es nicht reichen, wenn ADC_Read() ein int32_t zurückgibt?

von (prx) A. K. (prx)


Lesenswert?

Wenn ADC_Read() ein vorzeichenloses Resultat liefert, dann bleibt die 
Zwischenrechnung innerhalb der Klammer vorzeichenlos und folglich die 
ganze Rechnung. Also: (int16_t)ADC_Read(0). ADCzero sollte dann freilich 
auch nicht vorzeichenlos sein, sonst hast du schon wieder verloren.

von Ingo B. (tueddler)


Lesenswert?

Ich muss mich wohl erstmal wieder an die Eigenarten der C-Typen 
gewoehnen (ist schon eine Weile her). So funktioniert es dann:

uint16_t ADCVal;
ADCVal=ADC_Read(0);
return ((((int32_t)ADCVal-512+ADCzero)*maxVoltage) / 512 );

Geht es aber nicht ohne die Extra-Variable ADCVal?

Ingo.

von Ingo B. (tueddler)


Lesenswert?

Ingo B. schrieb:
> return ((((int32_t)ADCVal-512+ADCzero)*maxVoltage) / 512 );

Besser ist:
return ((((int32_t)ADCVal-512+ADCzero)*maxVoltage) >> 9 )

Aber warum funktioniert das ueberhaupt? Wird das MSB (neg. Zahl) nicht 
mitgeschoben?

von Ralf G. (ralg)


Lesenswert?

Ingo B. schrieb:
> Geht es aber nicht ohne die Extra-Variable ADCVal?

ich denke mal so:
return ((((int32_t)ADC_Read(0)-512+ADCzero)*maxVoltage) / 512 );

oder eben:

Ralf G. schrieb:
> Müsste es nicht reichen, wenn ADC_Read() ein int32_t zurückgibt?

Ingo B. schrieb:
> Ich muss mich wohl erstmal wieder an die Eigenarten der C-Typen
> gewoehnen

Hat damit nichts zu tun! Was ist denn das Ergebnis von ADC_Read(), was 
ist der Datentyp von maxVoltage?
Du hast wahrscheinlich alles in 16 Bit gerechnet und erst 'als es zu 
spät war' in 32 Bit gecastet.

von (prx) A. K. (prx)


Lesenswert?

Ingo B. schrieb:
> Geht es aber nicht ohne die Extra-Variable ADCVal?

Doch. Aber es muss ein Vorzeichen in die Rechnung rein, sonst bleibt es 
draussen. Ob du das mit int32_t oder int16_t machst ist aber egal.

Denk dran, dass bei 8/16-Bittern innerhalb einer 16-Bit Rechnung bereits 
ein einziger vorzeichenloser Typ die ganze Rechnung vorzeichenlos macht. 
Wenn du dann erst mit int32_t einen vorzeichenbehafteten Typ draus 
machst, dann hast du den Stall erst abgesperrt, nachdem das Pferd schon 
ausgebüxt ist.

von Ingo B. (tueddler)


Lesenswert?

Naja, ADC_Read gibt den Wert des ADC raus, der ist per se positiv (0 bis 
1023). Es macht ja keinen Sinn, den in der Funktion ADC_Read in eine 
signed zu verwandeln. Das ist erst notwendig, wenn die Zahl 512 
subtrahiert werden soll.

Ralf G. schrieb:
> return ((((int32_t)ADC_Read(0)-512+ADCzero)*maxVoltage) / 512 );

Hatte ich versucht, funktionierte nicht (Fehlermeldung). Jetzt aber 
schon. Muss mich wohl vertippt haben. Vielen Dank!

Wenn jetzt noch die Frage wegen ">>9" beantwortet wird, loesen sich 
meine Fragezeichen vollends auf!

von (prx) A. K. (prx)


Lesenswert?

Ingo B. schrieb:
> Das ist erst notwendig, wenn die Zahl 512
> subtrahiert werden soll.

Eben: Beitrag "Re: Negative Ganzzahlen von 32bit in 16bit konvertieren"

von Ralf G. (ralg)


Lesenswert?

Ingo B. schrieb:
> Wenn jetzt noch die Frage wegen ">>9" beantwortet wird, loesen sich
> meine Fragezeichen vollends auf!

Schreib mal lieber 512. Das ">>9" schiebt dir die Bits. Aber mit 
Vorzeichen!
Arithmetisches Schieben gibt's beim AVR nicht (ist's ein AVR?). Falls du 
einen µC verwendest, der das beherrscht (ich weiß nicht, ob es so einen 
gibt), dann findet der Compiler solche 'Tricks' in der Regel selber 
raus.

von Ingo B. (tueddler)


Lesenswert?

Ralf G. schrieb:
> Schreib mal lieber 512. Das ">>9" schiebt dir die Bits. Aber mit
> Vorzeichen!

Dachte ich auch, tut es aber nicht!

Bei MSDN heisst es:
For leftward shifts, the vacated right bits are set to 0. For rightward 
shifts, the vacated left bits are filled based on the type of the first 
operand after conversion. If the type is unsigned, they are set to 0. 
Otherwise, they are filled with copies of the sign bit.

Also wird schon beruecksichtigt, dass die Zahl signed ist.

Ralf G. schrieb:
> Arithmetisches Schieben gibt's beim AVR nicht (ist's ein AVR?). Falls du
> einen µC verwendest, der das beherrscht (ich weiß nicht, ob es so einen
> gibt), dann findet der Compiler solche 'Tricks' in der Regel selber
> raus.

Ich verstehe nicht ganz, was arithmetishes Schieben heisst. Und wenn es 
der Compiler kann, sollte es doch reichen, oder? Jedenfalls wird der 
Code mit ">>9" um 70 Bytes kuerzer als mit "/512". Kann natuerlich sein, 
dass die Optimierung nicht richtig eingestellt ist (steht auf -Os).

von (prx) A. K. (prx)


Lesenswert?

Ralf G. schrieb:
> Arithmetisches Schieben gibt's beim AVR nicht

Doch.

von (prx) A. K. (prx)


Lesenswert?

Ingo B. schrieb:
> Bei MSDN heisst es:

Seit wann beschreibt MSDN einen Compiler für AVRs? Als universelle 
C-Referenz ist diese Quelle ungeeignet, denn im Standard heisst es: "If 
E1 has a signed type and a negative value, the resulting value is 
implementation-defined."

von (prx) A. K. (prx)


Lesenswert?

NB: "/ 512" und ">> 9" liefern bei negativen Werten unterschiedliche 
Ergebnisse, was die Rundung angeht.

von Ingo B. (tueddler)


Lesenswert?

A. K. schrieb:
> Seit wann beschreibt MSDN einen Compiler für AVRs? Als universelle
> C-Referenz ist diese Quelle ungeeignet, denn im Standard heisst es: "If
> E1 has a signed type and a negative value, the resulting value is
> implementation-defined."

OK, aber fuer den Hobbybereich ziehe ich die dann doch die deutlich 
effizientere Variante vor. Wie gesagt, ich komme eigentlich aus dem 
Assembler-Bereich, und da ist ohnehin nichts so richtig portierbar. Und 
ich werde wohl bei AVR Studio bleiben, da sollte das MSDN-Zitat 
anwendbar sein. Ein Hinweis als Kommentar im Programmkopf kann aber 
nicht schaden.

A. K. schrieb:
> NB: "/ 512" und ">> 9" liefern bei negativen Werten unterschiedliche
> Ergebnisse, was die Rundung angeht.

Daran haette ich nicht gedacht, ist aber logisch. Danke fuer den 
Hinweis!

von Karl H. (kbuchegg)


Lesenswert?

Ingo B. schrieb:
> A. K. schrieb:
>> Seit wann beschreibt MSDN einen Compiler für AVRs? Als universelle
>> C-Referenz ist diese Quelle ungeeignet, denn im Standard heisst es: "If
>> E1 has a signed type and a negative value, the resulting value is
>> implementation-defined."
>
> OK, aber fuer den Hobbybereich

Es geht aber nicht um 'Hobbybereich' oder nicht.

Es geht darum, dass >> ein falsches Ergebnis liefert.


Tu dir selbst einen Gefallen und vergiss diese ganzen Assembler-Tricks. 
Wenn es möglich ist eine Division oder eine Multiplikation durch 
Schieben zu ersetzen UND das dann auch noch effizienter ist, dann macht 
der Compiler diese Optimierung für dich. So was haben Compiler seit 50 
Jahren drauf. Sowas und noch viel mehr. Für dich lautet die Devise: Ich 
schreib meinen Code so, dass ich in 2 Jahren den Code immer noch lesen 
kann! Low-Level Optimierungen überlass ich dem Compiler.

von Ralf G. (ralg)


Lesenswert?

A. K. schrieb:
> Ralf G. schrieb:
>> Arithmetisches Schieben gibt's beim AVR nicht
>
> Doch.

Stimmt. Den Befehl 'ASR' hab' ich tatsächlich immer überlesen :-(
Die Verwendung von '>>' wird, glaube ich, immer mit 'LSR' übersetzt. (?)

von (prx) A. K. (prx)


Lesenswert?

Ralf G. schrieb:
> Die Verwendung von '>>' wird, glaube ich, immer mit 'LSR' übersetzt.

Ist genauso falsch. Weshalb sollte er das tun? Gleicher Aufwand.

von xfr (Gast)


Lesenswert?

Eine andere Idee: Wenn ADCzero positiv ist, kannst Du die Division auch 
vorzeichenlos machen und die Subtraktion erst ganz am Schluss 
vorzeichenbehaftet. Das sollte der Compiler dann zu einem Shift machen:
1
(int16_t) ((((uint32_t) ADC_Read(0) + ADCzero) * maxVoltage) / 512) - maxVoltage

Falls ADCzero und maxVoltage konstant sind (oder nur einmal zu Beginn 
des Programms ermittelt werden), wäre das hier die schnellste Variante:
1
int16_t offset = ((int32_t) ADCzero * maxVoltage / 512 - maxVoltage);
2
return ((int16_t) ((uint32_t) ADC_Read(0) * maxVoltage / 512)) + offset

von (prx) A. K. (prx)


Lesenswert?

Nochmal >>: Interessant ist eine solche Frage effektiv nur bei 
Prozessoren, die keinen Befehl dafür haben, wie etwa 8051 und die 8-Bit 
PICs. Ich vermute freilich, dass viele oder alle Compiler auch da mit 
Vorzeichen schieben, umständlicher eben. Einfach deshalb, weil das zwar 
nicht vom Standard vorgeschrieben ist, sich aber eingebürgert hat.

von Ralf G. (ralg)


Lesenswert?

A. K. schrieb:
> Ralf G. schrieb:
>> Die Verwendung von '>>' wird, glaube ich, immer mit 'LSR' übersetzt.
>
> Ist genauso falsch. Weshalb sollte er das tun? Gleicher Aufwand.

So'n Mist. Stimmt! (Wo hatte ich das bloß wieder her?)
Vielleicht kann man sich mal rantasten:
1
#include <avr/io.h>
2
#include <inttypes.h>
3
4
5
volatile uint8_t i;
6
7
8
int main()
9
{
10
 i=(uint8_t)PINB;
11
 i=i/2;
12
 return 0;
13
}

Bei uintXX_t für 'i' wird geschoben. Bei intXX_t wird dividiert .
(Daher meine Fehlinterpretation, dass ASR fehlt. Hatte allerdings bis 
jetzt immer nur unsigned zum Testen.)
Ersetzt man 'i=i/2' durch 'i=i >> 1' wird immer mathematisch korrekt 
geschoben.

von Ingo B. (tueddler)


Lesenswert?

@xfr:
Guter Punkt! Dass 512/512=1 ist, haette ich auch sehen sollen ;-)

Zwar ist ADCzero u.U. auch negativ (und keine Konstante, sondern vom Typ 
int8_t), aber man koennte es ja umdefinieren und einfach noch mal 512 
abziehen:
1
int16_t ADCzero;
2
...
3
ADCzero = 0 + 512; // bleibt immer positiv, statt "0" der eigentliche Nullwert
4
...
5
return (int16_t) (((uint32_t)( ADC_Read(0) + ADCzero) * maxVoltage) / 512 - 2*maxVoltage);

Ausserdem habe ich den typecast weiter nach vorne gezogen, da die Summe 
ja erstmal als 16bit-Zahl ausgerechnet werden kann.

Ist sogar noch kuerzer als die >>9 Variante. So kann ich auch guten 
Gewissens auf die "Assembler-Tricks" verzichten. 70 Bytes (und etliche 
Takte) extra fuer "/512" sind hingegen schwer zu akzeptieren...

Danke nochmal an alle, ich glaube jetzt bin ich am Ziel! Erstmal.

von (prx) A. K. (prx)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Low-Level Optimierungen überlass ich dem Compiler.

Blöd ist freilich, dass die Division bisweilen kürzer ist als der Shift. 
In Bytes vom Aufruf der Laufzeitfunktion, nicht in Takten. Beim üblichen 
-Os kann sich das martialisch bemerkbar machen.

von xfr (Gast)


Lesenswert?

Ingo B. schrieb:
> Ausserdem habe ich den typecast weiter nach vorne gezogen, da die Summe
> ja erstmal als 16bit-Zahl ausgerechnet werden kann.

Wenn die Subtraktion auch in 16 Bit reicht, kannst Du mit Verschieben 
einer Klammer vielleicht noch ein paar Befehle sparen. :D
1
return (int16_t) (((uint32_t)( ADC_Read(0) + ADCzero) * maxVoltage) / 512) - 2*maxVoltage;

von xfr (Gast)


Lesenswert?

Edit: Du musst das sogar machen (oder in int32_t casten), denn sonst 
rechnest Du die Subtraktion unsigned und castest erst ganz am Ende (was 
sowieso implizit passieren würde, weil der Rückgabewert der Funktion ja 
int16_t ist).

von Falk B. (falk)


Lesenswert?

@Karl Heinz Buchegger (kbuchegg) (Moderator)

>Tu dir selbst einen Gefallen und vergiss diese ganzen Assembler-Tricks.

Jo, aber . . .

>Schieben zu ersetzen UND das dann auch noch effizienter ist, dann macht
>der Compiler diese Optimierung für dich. So was haben Compiler seit 50
>Jahren drauf.

Beim letzten Mal waren es erst 30. ;-) Vor 50 Jahren war von C noch 
keine Rede, und Compiler waren bestenfalls Assembler. Alte Männer 
erzählen vom Krieg.

>kann! Low-Level Optimierungen überlass ich dem Compiler.

Jain. Man muss wie immer wissen was man tut und ob es sich lohnt.

AVR-GCC-Codeoptimierung

von Ingo B. (tueddler)


Lesenswert?

xfr schrieb:
> Edit: Du musst das sogar machen (oder in int32_t casten), denn sonst
> rechnest Du die Subtraktion unsigned und castest erst ganz am Ende (was
> sowieso implizit passieren würde, weil der Rückgabewert der Funktion ja
> int16_t ist).

Hmm, sehe ich ein. Seltsamerweise funktioniert es, und es gibt keinen 
Unterschied zwischen den beiden Klammer-Versionen in der Codegroesse. 
Hat vielleicht damit zu tun, dass die uint32_t-Zahl ja quasi negativ 
ist, da das MSB gesetzt wird (overflow bei der Subtraktion). Bei der 
Konversion geht das dann halt trotz des Typkonflikts gut. Schoen ist es 
aber nicht.

von xfr (Gast)


Lesenswert?

Dann hat der Compiler anscheinend auch bei der ersten Variante schon 
erkannt, dass nur die unteren 16 Bit weiterverwendet werden und 
entsprechend in 16 Bit gerechnet.

Dass bei der unsigned-Subtraktion und danach casten das gleiche 
rauskommt, ist kein Wunder. Schließlich repräsentiert man negative 
Zahlen in der Praxis ja genau deswegen im Zweiterkomplement, damit die 
Rechenoperationen bei signed und unsigned die gleichen sind.

Nur garantiert C einem nicht, dass die Konvertierung von unsigned in 
signed auf diese Weise abläuft, wenn der unsigned-Wert nicht in den 
signed-Wert passt. Auf irgendeiner exotischen Maschine könnten negative 
Zahlen ja anders repräsentiert sein. Daher ist es auf jeden Fall 
sauberer, mit den richtigen Typen zu rechnen. Es kostet ja nichts.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ralf G. schrieb:
> {
>  i=(uint8_t)PINB;
>  i=i/2;
>  return 0;
> }

> Bei uintXX_t für 'i' wird geschoben. Bei intXX_t wird dividiert .

Nein, bei beiden wird dividiert, denn es steht eine Division da!

Ob die Division durch Schieben oder wie auch immer erledigt wird, ist 
Sache des Compilers:
 
1
#include <stdint.h>
2
3
uint8_t udiv2 (uint8_t i)
4
{
5
  return i / 2;
6
}
7
8
int8_t div2 (int8_t i)
9
{
10
  return i / 2;
11
}
 
Dieses Beispiel von dir wird vom aktuellen avr-gcc zB so übersetzt:
 
1
udiv2:
2
  lsr r24
3
  ret
4
5
div2:
6
  sbrc r24,7
7
  subi r24,lo8(-(1))
8
.L3:
9
  asr r24
10
  ret

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Und hier noch der Code für 16-Bit Division durch 512:
 
1
#include <stdint.h>
2
3
uint16_t udiv2 (uint16_t i)
4
{
5
  return i / 512;
6
}
7
8
int16_t div2 (int16_t i)
9
{
10
  return i / 512;
11
}
 
Das wird mit -Os und dem aktuellen Compiler zu:
 
1
udiv2:
2
  mov r24,r25
3
  clr r25
4
  lsr r24
5
  ret
6
7
div2:
8
  sbrs r25,7
9
  rjmp .L3
10
  subi r24,1
11
  sbci r25,-2
12
.L3:
13
  mov r24,r25
14
  lsl r25
15
  sbc r25,r25
16
  asr r24
17
  ret
 
Soweit ich sehen kann ist der Code optimal, oder weiß jemand eine 
effizientere Variante?

von Ralf G. (ralg)


Lesenswert?

Johann L. schrieb:
> Nein, bei beiden wird dividiert, denn es steht eine Division da!
Spaßvogel.

Also mit 'uint8_t' bei mir :
1
 i=i/2;
2
  4e:  80 91 60 00   lds  r24, 0x0060
3
  52:  86 95         lsr  r24
4
  54:  80 93 60 00   sts  0x0060, r24
5
6
 i=i >> 1;
7
  4e:  80 91 60 00   lds  r24, 0x0060
8
  52:  86 95         lsr  r24
9
  54:  80 93 60 00   sts  0x0060, r24

mit 'int8_t':
1
 i=i/2;
2
  4e:  80 91 60 00   lds  r24, 0x0060
3
  52:  62 e0         ldi  r22, 0x02  ; 2
4
  54:  05 d0         rcall  .+10       ; 0x60 <__divmodqi4>
5
  56:  80 93 60 00   sts  0x0060, r24
6
7
 i=i >> 1;
8
  4e:  80 91 60 00   lds  r24, 0x0060
9
  52:  85 95         asr  r24
10
  54:  80 93 60 00   sts  0x0060, r24
Da bastelt meine alte GCC-Version sozusagen was anderes draus.

von Karl H. (kbuchegg)


Lesenswert?

Ralf G. schrieb:

> mit 'int8_t':
>
1
>  i=i/2;
2
>   4e:  80 91 60 00   lds  r24, 0x0060
3
>   52:  62 e0         ldi  r22, 0x02  ; 2
4
>   54:  05 d0         rcall  .+10       ; 0x60 <__divmodqi4>
5
>   56:  80 93 60 00   sts  0x0060, r24
6
> 
7
>  i=i >> 1;
8
>   4e:  80 91 60 00   lds  r24, 0x0060
9
>   52:  85 95         asr  r24
10
>   54:  80 93 60 00   sts  0x0060, r24
11
>
> Da bastelt meine alte GCC-Version sozusagen was anderes draus.

Das das daran liegt, dass die neuere GCC Version mitlerweile auch für 
Divisionen entsprechende Spezialbehandlungen mithat, dürftest du ja 
schon gemerkt haben.
Das aber für int8_t eine Division durch 2 und einmal rechts schieben 
nicht das gleiche Ergebnis bringt, kannst du ganz einfach feststellen, 
wenn du i einfach mal den Wert -3 gibst. Im einen Fall kommt korrekt -1 
raus, im anderen Fall kommt -2 raus.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ralf G. schrieb:
> [...]
>> Da bastelt meine alte GCC-Version sozusagen was anderes draus.

Dann hast du nicht auf Geschwindigkeit optimiert, was du offenbar 
willst.

Keine der von mir getesteten Versionsn (3.4, 4.3, 4.5, 4.6, 4.7, 4.8, 
mehr hab ich net da) ruft bei Speed-Optimierung zur Division eine 
Bibliotheksfunktion auf.

> Das das daran liegt, dass die neuere GCC Version mitlerweile auch für
> Divisionen entsprechende Spezialbehandlungen mithat, dürftest du ja
> schon gemerkt haben.

Wie gesagt, das macht schon avr-gcc 3.4 (ca. 7 Jahre alt), vorausgesetzt 
man bedient ihn richtig ;-)

von Karl H. (kbuchegg)


Lesenswert?

Johann L. schrieb:

> Wie gesagt, das macht schon avr-gcc 3.4 (ca. 7 Jahre alt), vorausgesetzt
> man bedient ihn richtig ;-)

Gut zu wissen, wenn der nächste meint, er muss da wieder selbst 
'Handoptimieren'.
Ich hab da ehrlich gesagt noch nie im Assembler nachgeschaut. Denn wenn 
ich 'durch 512' meine, dann schreib ich auch 'durch 512'. :-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Johann L. schrieb:
>
>> Wie gesagt, das macht schon avr-gcc 3.4 (ca. 7 Jahre alt), vorausgesetzt
>> man bedient ihn richtig ;-)
>
> Gut zu wissen, wenn der nächste meint, er muss da wieder selbst
> 'Handoptimieren'.
> Ich hab da ehrlich gesagt noch nie im Assembler nachgeschaut. Denn wenn
> ich 'durch 512' meine, dann schreib ich auch 'durch 512'. :-)

Der übliche Denkfehler dabei ist, daß auf Größe optimiert wird, aber 
Code erwartet wird, wie er bei Geschwindigkeits-Optimierung erzeugt 
wird.

Erst ab avr-gcc 4.7 verhält sich der Compiler nicht ganz wie erwartet, 
d.h. bei Optimierung auf Größe mittels -Os erzeugt er Code, der mehrere 
Instruktionen größer sein kann als der optimale Code, dafür aber um 
Größenodnungen schneller ist.

Die Frage ist, wo hier die Grenze zu ziehen ist; immerhin will der 
Anwender bei -Os den kleinstmöglichen Code.

Andererseits sind Shifts so elementar und so weit verbreitet, daß mit 
dieses Verhalten sinnvoll erschien. Konkret wird das erreicht, indem 
Division teurer gemacht wird als sie gemäß der erzeugten Instruktionen 
tatsächlich ist.

In den 4.7 Release Notes [1] ist das der Punkt

  "Integer division by a constant"

obwohl der Code aus Sicht von -Os schlechter wird.

[1] http://gcc.gnu.org/gcc-4.7/changes.html#avr

von xfr (Gast)


Lesenswert?

Johann L. schrieb:
> Die Frage ist, wo hier die Grenze zu ziehen ist; immerhin will der
> Anwender bei -Os den kleinstmöglichen Code.

Für Mikrocontroller wäre imo ein Modus optimal, der den vorhandenen 
Speicher bestmöglich ausnutzt. Also zuerst O3 und wenns zu voll wird 
nach und nach Geschwindigkeitsoptimierungen gegen Größenoptimierungen 
tauschen ... Und vielleicht noch anzeigen, was die kleinstmögliche Größe 
gewesen wäre. Naja, man wird ja noch träumen dürfen ... ;-)

von Ralf G. (ralg)


Lesenswert?

Johann L. schrieb:
> Die Frage ist, wo hier die Grenze zu ziehen ist; immerhin will der
> Anwender bei -Os den kleinstmöglichen Code.
Okay, ich habe das (int8_t) mit Version 4.3 und Optimierung -Os 
getestet:
Code: 150Byte
mit -O3:
Code: 116Byte, und siehe da: jetzt wird auch 'asr' verwendet! -> 
schneller und kürzer.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

xfr schrieb:
> Johann L. schrieb:
>> Die Frage ist, wo hier die Grenze zu ziehen ist; immerhin will der
>> Anwender bei -Os den kleinstmöglichen Code.
>
> Für Mikrocontroller wäre imo ein Modus optimal, der den vorhandenen
> Speicher bestmöglich ausnutzt. Also zuerst O3 und wenns zu voll wird
> nach und nach Geschwindigkeitsoptimierungen gegen Größenoptimierungen
> tauschen ... Und vielleicht noch anzeigen, was die kleinstmögliche Größe
> gewesen wäre. Naja, man wird ja noch träumen dürfen ... ;-)

Was glaubst du, wie die Leute rumweinern würden, wenn man das in den 
Compiler einbaut? Nämlich spekulative Optimierung, die zu Compilezeiten 
führen würden, wie du sie für ein Schachprogramm einplanen würdest, von 
dem du erwartest, daß es auf GM-Niveau spielt: 1/4 Stündchen oder mehr 
für die 100-Zeilen Quelle gefällig?

Sowas ist doch nur eine Fingerübung für Makefile-Autoren, wenn du sowas 
willst  brauchst  haben musst, schreib dir entsprechende Makefiles 
oder Skripte.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.