Forum: Mikrocontroller und Digitale Elektronik Art der if-Auswahl nur Geschmackssache?


von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Der Compiler-Explorer meldet keinen Fehler, der bringt nur eine Warnung.

Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.

> Und witzigerweise bringt der die Warnung nur bei char, uint8_t oder
> int8_t. Und nur bei der Zuweisung, nicht beim Vergleich.

Der GCC meckert mit -Wextra auch den Vergleich an:

1
 warning: comparison is always false due to limited range of data type [-Wtype-limits]

> int16_t c2; c2 = ~0x8000; wird klaglos akzeptiert, obwohl es genauso
> einen Overflow erzeugen würde.

Auf dem PC liefert der GCC dabei die gleichen Warnungen wie oben. Auf
dem AVR natürlich nicht, weil dort int16_t = int ist und somit keine
Konvertierung erfolgt, die einen Überlauf auslösen könnte.

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Auf dem PC liefert der GCC dabei die gleichen Warnungen wie oben. Auf
> dem AVR natürlich nicht, weil dort int16_t = int ist und somit keine
> Konvertierung erfolgt, die einen Überlauf auslösen könnte.

Ja, der gleiche Schmonz funktioniert aber auch auf 32bit CPUs mit int32 
vs. int16, oder auf 64bit CPUs. Das ist kein AVR-spezifisches Problem.

Yalu X. schrieb:
> Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.

Warum ist es unsinnig, um beim AVR-Beispiel zu bleiben, Bits auf diese 
Weise zu selektieren, oder auf ein Bitmuster zu prüfen?

Der Code ist hier vereinfacht, aber c1 könnte seine Daten aus einem 
low-aktiven Tastenfeld haben und dann mit ~auf gedrückte Tasten prüfen. 
Die Werte könnten auch per #define irgendwo anders festgelegt werden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.
>
> Warum ist es unsinnig, um beim AVR-Beispiel zu bleiben, Bits auf diese
> Weise zu selektieren, oder auf ein Bitmuster zu prüfen?

Ich bezog mich auf das da:

Karl schrieb:
> char c1;
> c1 = ~0x80;
> if (c1 == ~0x80)
> // True oder False?

> Der Code ist hier vereinfacht, aber c1 könnte seine Daten aus einem
> low-aktiven Tastenfeld haben und dann mit ~auf gedrückte Tasten prüfen.

Ok, aber was erwartest du bei einem Vergleich zwischen zwei verschieden
breiten Integertypen?

Es gibt im Wesentlichen drei Möglichkeiten, mit diesem Fall umzugehen:

1. Einer oder beide der beiden Typen werden zu einem gemeinsamen Typ
   erweitert, so dass sie vergleichbar werden. Im konkreten Fall wird
   das char unter Beibehaltung des numerischen Werts zu einem int
   erweitert. So wird das in C bei der impliziten Typkonvertierung
   gehandhabt.

2. Einer oder beide der beiden Typen werden zu einem gemeinsamen Typ
   beschnitten, so dass sie vergleichbar werden. So hättest du es in
   deinem Beispiel wohl gerne. Im konkreten Fall c1==~0x80 würde ~0x80
   in ein char konvertiert, wobei sich der Wert von -129 in +127 ändert
   und der Vergleich deswegen wahr wird, da c1 ebenfalls gleich +127
   ist. Implizites Beschneiden und damit u.U. verbundene Änderungen des
   Werts ist aber in den allermeisten Fällen unerwünscht, da es eine
   Fehlerquelle bei numerischen Berechnungen darstellt.

3. Der Vergleich wird wegen der verschiedenen Datentypen gar nicht erst
   zugelassen. Im konkreten Fall würde der Compiler mit einem Fehler
   abbrechen, weil char und int nicht vergleichbar sind. Das wäre die
   sauberste Alternative, es gibt aber nur wenige Programmiersprachen,
   deren Typprüfung so streng ist (z.B. Haskell).

Man muss sich in C (wie auch in neueren Pascals und vielen anderen
Sprachen) bewusst sein, dass implizite Typkonvertierungen manchmal zu
Problemen führen. Solche Probleme lassen sich im Zweifelsfall meist
durch explizite Typkonvertierungen umgehen. Schreibt man im konkreten
Fall c1==(char)~0x80 statt c1==~0x80, ist das Ergebnis das von dir
erwartete.

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> 3. Der Vergleich wird wegen der verschiedenen Datentypen gar nicht erst
>    zugelassen. Im konkreten Fall würde der Compiler mit einem Fehler
>    abbrechen, weil char und int nicht vergleichbar sind. Das wäre die
>    sauberste Alternative, es gibt aber nur wenige Programmiersprachen,
>    deren Typprüfung so streng ist (z.B. Haskell).

Und C++ seit der 2011er Version, wenn man die Initialisierung mit {} 
nutzt.
Da sagt z.B. der gcc bei diesem Programm
1
int main()
2
{
3
    char c { ~0x80 };
4
}
folgendes:
1
0x80.cpp: In function ‘int main()’:
2
0x80.cpp:3:20: error: narrowing conversion of ‘-129’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Ok, aber was erwartest du bei einem Vergleich zwischen zwei verschieden
> breiten Integertypen?

Sowas?
1
# [47] c1 := not($80);
2
  ldi  r18,127
3
# [48] if c1 = not($80) then begin
4
  cpi  r18,127
5
  brne  .Lj16

Die Typen sind nicht verschieden breit. Die sind genau ein Byte. Und ein 
Not eines Bytes ist wiederum genau ein Byte. Ein Byte wird durch ein Not 
nicht zu zwei Byte.

Das ist nur eine Eigenheit von C, welches erstmal versucht alles in 16 
Bit zu quetschen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Rolf M. schrieb:
> Und C++ seit der 2011er Version, wenn man die Initialisierung mit {}
> nutzt.
> Da sagt z.B. der gcc bei diesem Programmint main()
> {
>     char c { ~0x80 };
> }
> folgendes:0x80.cpp: In function ‘int main()’:
> 0x80.cpp:3:20: error: narrowing conversion of ‘-129’ from ‘int’ to
> ‘char’ inside { } [-Wnarrowing]

Ähnliches sagt ja auch der C-Compiler bei der Initialisierung, nur dass
der lediglich eine Warnung ausspricht.

Karl schrieb:
> Yalu X. schrieb:
>> Ok, aber was erwartest du bei einem Vergleich zwischen zwei verschieden
>> breiten Integertypen?
>
> Sowas?# [47] c1 := not($80);
>   ldi  r18,127
> # [48] if c1 = not($80) then begin
>   cpi  r18,127
>   brne  .Lj16

Das sieht nach Pascal aus, aber welcher Dialekt bzw.Compiler? (W.S. hat
diese Frage weiter oben auch schon gestellt)

Da gibt es nämlich große Unterschiede. Mit deinem Compiler wird der
Ausdruck not($80) offensichtlich zu 127, mit Free Pascal (PC-Version)
aber zu -129 ausgewertet. Dort würde der Vergleich false liefern.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Die Typen sind nicht verschieden breit. Die sind genau ein Byte. Und ein
> Not eines Bytes ist wiederum genau ein Byte. Ein Byte wird durch ein Not
> nicht zu zwei Byte.

wo steht das 0x80 ein Byte ist? Ist es nämlich nicht, es ist ein Integer 
(und ein integer heißt nicht 16 bit)

aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder 
ein Integer. Blöd, eh?

> Das ist nur eine Eigenheit von C, welches erstmal versucht alles in 16
> Bit zu quetschen.

16 Bit ist falsch, Integer wäre richtig.

Für dich mag es eine Eigenheit sein, C-Programmierer haben das Kapitel 
"Integral Promotion" in ihrem C-Buch gelesen ;-)

von Bär Luskoni (Gast)


Lesenswert?

Michael R. schrieb:

> Für dich mag es eine Eigenheit sein, C-Programmierer haben das Kapitel
> "Integral Promotion" in ihrem C-Buch gelesen ;-)

Das nützt aber Nichts, wenn in dem "guten C-Buch" des Anderen wieder 
etwas Anderes steht. Eine Sprache, bei der eine 50:50 Chance besteht, 
daß das richtige Ergebnis einer Operation erzielt wird...
Ich weiß nicht so recht...

von Einer K. (Gast)


Lesenswert?

Bär Luskoni schrieb:
> Ich weiß nicht so recht...

Aber ich!

Denn:
Den individuellen Irrtum eines Buchautors, einer Sprache anzulasten, ist 
mindestens ungerecht.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Der Code ist ja auch nicht illegal, sondern höchstens unsinnig.
>
> Warum ist es unsinnig, um beim AVR-Beispiel zu bleiben, Bits auf diese
> Weise zu selektieren, oder auf ein Bitmuster zu prüfen?

Zahlen vergleicht man mit == ,Bitmuster prüft man mit &.

if (! (c & 0x80))...

gibt immer das richtige Ergebnis,unabhängig von der Bitbreite der 
Variablen und der Implementierung.

von ADA_ULTIMA (Gast)


Lesenswert?

Ja,
die Geschmäcker unterscheiden sich.

Ich mag diese Geschmacksrichtung (auf PC, rPI, STM32, ..):
1
package Energie is
2
   type Restzeit_Type is new Integer range 0 .. 1234;
3
   Energie_Schwelle_Oben  : Restzeit_Type := 100;
4
   Energie_Schwelle_Unten : Restzeit_Type :=  10;
5
6
   procedure Led_Energie_Gruen_On;
7
   procedure Led_Energie_Rot_On;
8
   procedure Led_Energie_Gelb_On;
9
10
   procedure Energie_Rest_Ampel (Restzeit : Restzeit_Type);
11
end Energie;
12
13
package body Energie is
14
   procedure Led_Energie_Gruen_On is
15
   begin
16
      null;
17
   end;
18
19
20
   procedure Energie_Rest_Ampel (Restzeit : Restzeit_Type) is
21
   begin
22
      case Restzeit is
23
         when Energie_Schwelle_Oben .. Restzeit_Type'Last =>
24
            Led_Energie_Gruen_On;
25
         when Restzeit_Type'First .. Energie_Schwelle_Oben =>
26
            Led_Energie_Rot_On;
27
         when others =>
28
            Led_Energie_Gelb_On;
29
      end case;
30
   end Energie_Rest_Ampel;
31
end Energie;


oder so ähnlich..

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder
> ein Integer. Blöd, eh?

Ach, wirklich? https://en.wikipedia.org/wiki/Bitwise_operation#NOT

Jobst Q. schrieb:
> Zahlen vergleicht man mit == ,Bitmuster prüft man mit &.
> if (! (c & 0x80))...

Autsch!
1
  c1 = 0x81;
2
  if (c1 & 0x80) {
3
    is_true();
4
  } else {
5
    is_false(); 
6
  }

Das Bitmuster 0x81 ist also gleich dem Bitmuster 0x80?

Da muss man sich nicht wundern, dass Software in C so schlecht ist, wenn 
C-Programmierer nicht mal die einfachsten Vergleiche hinbekommen.

Leute, ist es denn echt so schwer einfach zuzugeben: Ja, der C-Compiler 
baut hier Scheisse, das ist leider so, weil das irgendwann mal so 
angefangen wurde und wir das jetzt wegen der Abwärtskombatibilität so 
beibehalten müssen.

Stattdessen Verrenkungen um Verrenkungen, um zu erklären, warum die 
Scheisse in C jetzt doch die Sachertorte ist.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
>> aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder
>> ein Integer. Blöd, eh?
>
> Ach, wirklich? https://en.wikipedia.org/wiki/Bitwise_operation#NOT

Schön, dass du dir die Mühe gemacht hast, 'Integral Promotion' zu 
recherchieren, und versucht hast, das auch nur ansatzweise zu verstehen 
;-)

Karl schrieb:
> Ja, der C-Compiler
> baut hier Scheisse, das ist leider so, weil das irgendwann mal so
> angefangen wurde und wir das jetzt wegen der Abwärtskombatibilität so
> beibehalten müssen.

Wenn du die Promotion verstanden hättest, wüsstest du auch wo deren 
Vorteile liegen, dass das weder Sch**sse noch Abwärtskompatibilität ist.

So aber wirst du wohl bei deiner $FavoriteProgrammingLanguage (welche 
eigentlich?) bleiben müssen... das ist aber ok, damit hat niemand ein 
Problem, eher im Gegenteil ^^

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> 'Integral Promotion'

Scheisse wird nicht dadurch schmackhafter, dass man ihr einen tollen 
Namen gibt.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Autsch!
>   c1 = 0x81;
>   if (c1 & 0x80) {
>     is_true();
>   } else {
>     is_false();
>   }
> Das Bitmuster 0x81 ist also gleich dem Bitmuster 0x80?

if (c1 & 0x80) fragt ab, ob Bit7 gesetzt ist. Das ist bei c = 0x81 der 
Fall. Also richtig.

Wenn du abfragen willst ob Bit7 und Bit0 gesetzt ist, geht das so:

 if ((c1 & 0x81)==0x81)...

Deinen Mangel an Kenntnissen von Bitoperationen kannst du nicht C 
anlasten.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Scheisse wird nicht dadurch schmackhafter, dass man ihr einen tollen
> Namen gibt.

Hmmm... ich finde Gabelstapler total Scheisse. Ich kann mit denen 
überhaupt nicht umgehen...

Jobst Q. schrieb:
> Deinen Mangel an Kenntnissen von Bitoperationen kannst du nicht C
> anlasten.

Das gilt aber definitiv nicht für Gabelstapler ;-)

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> if (c1 & 0x80) fragt ab, ob Bit7 gesetzt ist. Das ist bei c = 0x81 der
> Fall. Also richtig.

Das ist nicht das, was Du oben behauptet hast.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> if (c1 & 0x80) fragt ab, ob Bit7 gesetzt ist. Das ist bei c = 0x81 der
>> Fall. Also richtig.
>
> Das ist nicht das, was Du oben behauptet hast.

"Bitmuster prüft man mit &." hatte ich gesagt. Und 0x80 ist nunmal als 
Bitmuster in 0x81 enthalten.

von Rolf M. (rmagnus)


Lesenswert?

Karl schrieb:
> Michael R. schrieb:
>> aber selbst wenn du aus 0x80 ein Byte machst, ist das "not" davon wieder
>> ein Integer. Blöd, eh?
>
> Ach, wirklich?

Ja, wirklich.

> https://en.wikipedia.org/wiki/Bitwise_operation#NOT

Schön. Du hast rausgefunden, wie eine boolesche Invertierung 
funtkioniert. Was hat das jetzt damit zu tun, von welchem Datentyp die 
Konstante 0x80 und damit auch ~0x80 in C ist?

> Da muss man sich nicht wundern, dass Software in C so schlecht ist, wenn
> C-Programmierer nicht mal die einfachsten Vergleiche hinbekommen.

Schließe doch bitte nicht von dir auf andere.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> if (c1 == ~0x80)
> // True oder False?

Weder noch, sondern einfach Unsinn. Die einzig sinnvolle Verwendung des 
bitweisen NOT '~' ist das Zurücksetzen von Bits zusammen mit dem 
bitweisen AND '&'.

flags &= (~0x80);

Dabei ist es nicht von Nachteil, wenn die vom Compiler gewählte Größe 
der '~'-Konstruktion größer ist als die betroffene Variable. Alle 
zusätzlichen Bits sind gesetzt, haben also mit dem '&' keine Wirkung.

von Karl (Gast)


Lesenswert?

Rolf M. schrieb:
> Was hat das jetzt damit zu tun, von welchem Datentyp die
> Konstante 0x80 und damit auch ~0x80 in C ist?

Die Konstante hat sinnvoller Weise die Länge, die angegeben ist. 0x80 
sind ein Byte, 0x0080 sind 2 Byte. Gerade bei Hex ist das doch total 
simple und seit Jahrhunderten bewährt.

Ich sag doch: Die Crux bei C ist, dass es erstmal alles auf seine 
mindestens 16 Bit presst, auch wenn das für 8-Bit-Systeme nicht sinnvoll 
ist.

Jobst Q. schrieb:
> Die einzig sinnvolle Verwendung des
> bitweisen NOT '~'...

... die Du Dir vorstellen kannst. Für Deinen beschränkten Horizont kann 
ja sonst keiner was.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Die Konstante hat sinnvoller Weise die Länge, die angegeben ist. 0x80
> sind ein Byte, 0x0080 sind 2 Byte. Gerade bei Hex ist das doch total
> simple und seit Jahrhunderten bewährt.

Ach ist das süß ;-) Hex ist also ein Sonderfall... wie macht man das 
dezimal, oktal?

> [...] dass es erstmal alles auf seine mindestens 16 Bit presst [...]

ah, mindestens... er lernt^^

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Die Konstante hat sinnvoller Weise die Länge, die angegeben ist. 0x80
> sind ein Byte, 0x0080 sind 2 Byte.

In C hängt die Länge vom Wert und von der Basis ab.

Michael R. schrieb:
> Ach ist das süß ;-) Hex ist also ein Sonderfall... wie macht man das
> dezimal, oktal?

Stimmt aber. Der Typ einer lexikalischen Konstanten hängt zwar vom Wert 
ab, nicht von der Länge im Quelltext, aber das bestimmt sich oktal/hex 
tatsächlich anders als dezimal. Bei dezimaler Angabe werden die 
vorzeichenlosen Typen übergangen, weshalb 32768 in einer 16-Bit Umgebung 
"long" ist, 0x8000 aber "unsigned".

: Bearbeitet durch User
von Einer K. (Gast)


Lesenswert?

Ach, wenn unser Karl sich wenigstens darüber beschweren würde, dass ein 
Byte auch mal 9 Bit breit sein darf, dann hätte ich ja halbes 
Verständnis.

Aber so....
Denn das Verhalten von C ist eigentlich recht klar definiert.
Sicher kann man diese Definitionen in Zweifel ziehen, oder sich fragen 
ob sie geschickt definiert sind.

Es macht allerdings keinen Sinn darüber zu schimpfen, denn die 
Definition kann man nicht ändern. Das ist, wie darüber jammern, dass man 
nass wird, wenn es regnet.


Man/Karl könnte sich eine andere Sprache suchen, wenn man/Karl C nicht 
mag. Aber deswegen anderen die Sprache madig machen...?
Neee...

Ich selber bin auch kein C Fan.
Habs eher mit OOP, also wenn, dann C++ auf meinen AVR Zwergen.

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Hex ist also ein Sonderfall... wie macht man das
> dezimal, oktal?

Naja, oktal in C ist ja noch bescheuerter: 010 ist ein Oktalzahl. Was 
haben die geraucht, um auf sowas zu kommen?

Michael R. schrieb:
> ah, mindestens...

Falls Du damit wieder auf die vergötterte Integer-Promotion ansprichst: 
Wenn ein NOT aus einem Byte zwei Byte macht, ist das keine 
Integer-Promotion, sondern einfach nur bescheuert. Das Ergebnis von 
einem NOT auf ein Byte ist wiederum nur ein Byte. Genauso wie das 
Ergebnis ein Byte AND ein Byte wieder nur ein Byte ist. Bitweise 
Operatoren. Wie soll sich da die Anzahl der Bits erhöhen?

Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit, 
erstmal alles in 16 Bit zu pressen, weil 16 Bit damals(TM) halt hipp 
waren. Das ist schlichtweg ein Designfehler von C, den man immer weiter 
mitschleifen muss.

von Einer K. (Gast)


Lesenswert?

> Kritik an Anderen schützt vor eigener Leistung nicht!

Zeige mir bitte deinen eigenen "besseren" Kompiler.

von Karl (Gast)


Lesenswert?

Arduino F. schrieb:
> Man/Karl könnte sich eine andere Sprache suchen, wenn man/Karl C nicht
> mag. Aber deswegen anderen die Sprache madig machen...?

Done!

Allerdings ist es im Gegenteil eher so, dass die C-Fans anderen "ihre" 
Sprachen madig machen wollen, weil C und dessen Derivate das einzig 
Wahre sind und man nur in C richtig programmieren könne.

Witzig zu sehen ist allerdings, wie krampfhaft die "Eigenheiten" sprich 
Designfehler von C verteidigt werden, anstatt einfach mal zuzugeben: Ja 
ok, das war damals eine Fehlentscheidung, mit der wir heute leider leben 
müssen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit,
> erstmal alles in 16 Bit zu pressen, weil 16 Bit damals(TM) halt hipp
> waren. Das ist schlichtweg ein Designfehler von C, den man immer weiter
> mitschleifen muss.

wir müssen damals im selben Kommunikationstraining gewesen sein... 
"Blamiere dich täglich" war der Wahlspruch, aber du übertreibst es 
etwas... dabei warst du schon beim "mindestens".

es wird mitnichten auf 16 bit, sondern auf die native Wortbreite (aber 
mindestens 16 bit) erweitert. Und das nicht weil es hipp ist oder war, 
sondern weil es in der Mehrzahl der Fälle sinnvoll ist. Deswegen ist es 
auch kein Designfehler, sondern wurde absichtlich so definiert, weil es 
eben Vorteile bietet. Das damit ein paar Fallstricke für Anfänger 
verbunden sind, damit hast du recht. Aber C hatte (im Gegensatz zu zB 
Pascal) niemals den Anspruch, eine Anfänger-Sprache zu sein.

von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Naja, oktal in C ist ja noch bescheuerter: 010 ist ein Oktalzahl. Was
> haben die geraucht, um auf sowas zu kommen?

Oktale Darstellung war in der Phase ziemlich gebräuchlich, in der C 
erfunden wurde. Wenn man die binäre Codierung der Befehle der PDP11 und 
anderer Maschinen dieser Ära ansieht, dann weiss man auch warum.

> Falls Du damit wieder auf die vergötterte Integer-Promotion ansprichst:
> Wenn ein NOT aus einem Byte zwei Byte macht,

Tut es nicht. ~0x80 macht aus einer "int" eine "int", denn 0x80 ist 
schon eine "int". ~ ändert also nicht den Typ. Auch in
  uint8_t c = 0x80;
  ... ~c ...
erweitert nicht speziell der ~ Operator die variable "c" auf "int", 
sondern der Umstand, dass "c" im Kontext einer Expression verwendet 
wird. Aber auch da wird "c" vor der Berechung erweitert, also nicht 
durch den Operator.

> Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit,
> erstmal alles in 16 Bit zu pressen,

Da wird nichts gepresst. Konstanten, die nicht in 16 Bits passen, werden 
nicht reduziert. Der Typ passt sich an, mindestens aber "int", was per 
Definition mindestens 16 Bits sein muss.

C ist definitiv nicht die bestmögliche aller Welten. Muss man nicht 
mögen, ist aber so und wird so verwendet. Deutsch ist auch nicht die 
einfachste aller Sprachen, wird aber von uns trotzdem verwendet.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Michael R. schrieb:
> KaUnd das nicht weil es hipp ist oder war,
> sondern weil es in der Mehrzahl der Fälle sinnvoll ist.

Es war damals nicht unüblich, Bytes seitens der Maschine automatisch zu 
einem Wort zu erweitern, wenn sie aus dem Speicher geladen wurden - mal 
vorausgesetzt, es gab überhaupt Byteadressierung. So auch die PDP11, die 
in der Entstehungsphase eine nicht unmassgebliche Rolle spielte.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> Die einzig sinnvolle Verwendung des
>> bitweisen NOT '~'...
>
> ... die Du Dir vorstellen kannst. Für Deinen beschränkten Horizont kann
> ja sonst keiner was.

Natürlich kann ich mir mehr vorstellen, aber eben nichts sinnvolles. 
Invertieren kann man auch mit XOR '^', sogar ganz beliebige Bits.

c^=0xFF;

Invertiert genau ein Byte, ohne dass die Typlänge eine Rolle spielt.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

A. K. schrieb:
> C ist definitiv nicht die bestmögliche aller Welten. Muss man nicht
> mögen, ist aber so und wird so verwendet.

Ein wahres Wort!

Um nochmal bei Karl zu bleiben: Karl mag C nicht. Ich mag Python nicht. 
Karl wird seine Gründe haben, zwischen mir und Python steht (vor allem) 
die off-side rule (Einrückung). Damit kann ich nicht.

Ich würde aber nie so weit gehen, das als Design-Fehler zu bezeichnen. 
Es ist nur ein (legitimes) Design welches mir nicht gefällt.

Weil ich Python nicht mag, verwende ich es nicht. Weil ich es nicht 
verwende, weiß ich nichts (oder nur sehr wenig) drüber. Weil ich nichts 
drüber weiß, schreibe ich nicht in Python-Threads mit (und schon gar 
nicht mokiere ich mich dort über Design-Fehler).

Bei Karl scheint da irgendwas anders zu sein ;-)

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Nun, ich mag Python auch nicht, genau aus genanntem Grund.

Im Gegensatz zum Einrücken bei Python, welches offensichtlich ist, ist 
obiger Fehler in C eben nicht offensichtlich.

Der Compiler macht hier etwas, was zu einer der Intention - nicht zu 
Verwechseln mit Indention - des Codes widersprechenden Aussage führt, 
aufgrund einer Typwandlung, die im Verborgenen erfolgt.

Da weiter oben der FPC erwähnt wurde: Der FPC auf dem PC kompiliert das 
gar nicht erst, man müsste ihn durch einen expliziten Typecast dazu 
zwingen. Und der FPC für Embedded AVR kompiliert das plattformkonform 
mit 8 Bit, und das Ergebnis ist korrekt.

Geht also durchaus.

von Einer K. (Gast)


Lesenswert?

Mutter Teresa sagte mal:
> Wenn du die Menschen verurteilst,
> hast du keine Zeit, sie zu lieben.

Das gilt natürlich auch, im übertragenen sinne, für vieles andere.
z.B. für Computersprachen.
(auch wenn sie das nicht direkt damit gemeint haben sollte)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael R. schrieb:
> Karl schrieb:
>> Nein, was Du für Integer Promotion hältst ist einfach nur die Eigenheit,
>> erstmal alles in 16 Bit zu pressen, ...
>
> ...
> es wird mitnichten auf 16 bit, sondern auf die native Wortbreite (aber
> mindestens 16 bit) erweitert.

Genau dieses "aber mindestens 16 bit" ist jedoch ein Punkt, wo ich Karl
teilweise recht gebe.

Der ursprüngliche Grund, die Größe von int und damit den defaultmäßigen
Wertebereich für Integer-Berechnungen an die Wortbreite des verwendeten
Prozessors anzugleichen, liegt schlicht im Wunsch nach maximaler
Performanz. Von diesem Standpunkt aus gesehen müsste bei einem
AVR-C-Compiler int 8 bit breit sein, denn ein AVR rechnet in 8-Bit-
Arithmetik deutlich schneller als in 16 Bit.

Der Grund, warum für int trotzdem mindestens 16 Bit vorgeschrieben sind,
liegt IMHO vor allem darin, dass einige als int deklarierte Funktionen
der Standardbibliothek (bspw. fgetc) einen Wertebereich von mehr als
0..255 voraussetzen. Die Signatur dieser Funktionen wurden bereits
festgelegt lange bevor der erste C-Compiler für einen 8-Bit-Prozessor
geschrieben wurde.

Dieses Problem wurde dadurch umgangen, dass 16 Bit als minimale Größe
von int festgesetzt wurde. Das führte bei den ersten C-Compilern für
8-Bit-Prozessoren tatsächlich zu Performanzeinschränkungen, die aber in
neueren, stark optimierenden Compilern weitgehend behoben sind.

Dennoch bleiben ein paar wenige Fälle, wo auf 8-Bit-Prozessoren die
Integer-Promotion lästig ist. Da diese Fälle selten sind und i.Allg.
durch Umschrieben des betreffenden Ausdrucks oder Einfügen von Casts
leicht abgehandelt werden können, besteht für mich keinerlei Grund, auf
8-Bit-Prozessoren in einer anderen Sprache als C (oder allenfalls noch
Assembler) zu programmieren.

An dem von Karl angeprangerten Fall

1
  irgendwas1 == ~irgendwas2

störe ich mich überhaupt nicht, da ich einen solchen Ausdruck noch nie
gebraucht habe, obwohl schon seit vielen Jahren Mikrocontroller aus
vielen unterschiedlichen 8-, 16- und 32-Bit-Familien in C programmiere.

Übrigens kann man beim AVR-GCC die int-Größe mit -mint8 auf nicht 8 bit
einstellen. Dann verhält er sich wie von Karl gewünscht, allerdings ist
er damit nicht mehr ISO-konform, und die Standardbibliothek kann nicht
mehr verwendet werden bzw. müsste angepasst und neu gebaut werden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

@Karl:

Bisher hast du dich ja um die Beantwortung der bereits mehrfach
gestellten Frage nach dem von dir verwendeten Compiler erfolgreich
herumgedrückt:

W.S. schrieb:
> Meinst du Mikroe oder was anderes?

Yalu X. schrieb:
> Das sieht nach Pascal aus, aber welcher Dialekt bzw.Compiler?

Arduino F. schrieb:
> Zeige mir bitte deinen eigenen "besseren" Kompiler.

Aber so langsam scheint Licht ins Dunkel zu kommen:

Karl schrieb:
> Da weiter oben der FPC erwähnt wurde: Der FPC auf dem PC kompiliert das
> gar nicht erst, man müsste ihn durch einen expliziten Typecast dazu
> zwingen. Und der FPC für Embedded AVR kompiliert das plattformkonform
> mit 8 Bit, und das Ergebnis ist korrekt.

Du arbeitest also mit dem AVR-FPC?

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Lesenswert?

"Die Länge einer Diskussion ist invers proportional zur Wichtigkeit des 
Themas." So verlangt es das Gesetz.

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Weil ich Python nicht mag, verwende ich es nicht. Weil
> ich es nicht verwende, weiß ich nichts (oder nur sehr
> wenig) drüber. Weil ich nichts drüber weiß, schreibe
> ich nicht in Python-Threads mit (und schon gar nicht
> mokiere ich mich dort über Design-Fehler).

Das ist menschlich gesehen zwar nobel, sachlich aber
nicht zielführend: Der Fortschritt wird durch die
Unzufriedenen erzwungen :)

von Stefan F. (Gast)


Lesenswert?

>  Der Fortschritt wird durch die Unzufriedenen erzwungen

Ich vertrete ja eher die Ansicht, dass Fortschritt meisten zufällig 
passiert und der Versuch, ihn zu erzwingen, meistens scheitert.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Der ursprüngliche Grund, die Größe von int und damit den
> defaultmäßigen Wertebereich für Integer-Berechnungen an
> die Wortbreite des verwendeten Prozessors anzugleichen,
> liegt schlicht im Wunsch nach maximaler Performanz.

Der Gedanke lässt sich weiterspinnen:

Wenn die Performanz allein das entscheidende Argument
(gewesen) wäre, hätte sich C nicht so weit verbreiten
dürfen -- dann hätte man nämlich bei Assembler bleiben
müssen.
Es ist daher nicht unsinnig zu vermuten, dass auch die
anderen Eigenschaften von C -- wie die im Vergleich zum
Assembler höhere Abstraktion und die bessere Portabili-
tät -- eine wichtige Rolle gespielt haben.

Spätestens hier sollte aber auffallen, dass logisch
widersprüchliche Ziele vorliegen: Man kann nicht
gleichzeitig maximale Performance (die optimale
Ausnutzung maschinenspezifischer Besonderheiten
erfordert) und maximale Portabilität (die maximale
Unabhängigkeit von maschinenspezifischen Besonder-
heiten notwendig macht) haben -- wenigstens nicht ohne
zusätzlichen Aufwand.

von Possetitjel (Gast)


Lesenswert?

Stefan U. schrieb:

>>  Der Fortschritt wird durch die Unzufriedenen erzwungen
>
> Ich vertrete ja eher die Ansicht, dass Fortschritt
> meisten zufällig passiert und der Versuch, ihn zu
> erzwingen, meistens scheitert.

Unsere Aussagen sind vollständig kompatibel.

Von den 100 Unzufriedenen, die versuchen, den Fortschritt
zu erzwingen, scheitern 99, und nur einer hat Erfolg.
Trotzdem wird der Fortschritt von den Unzufriedenen
getragen -- die anderen haben nämlich kein Motiv, etwas
zu ändern :)

von W.S. (Gast)


Lesenswert?

Yalu X. schrieb:
> Der ursprüngliche Grund, die Größe von int und damit den defaultmäßigen
> Wertebereich für Integer-Berechnungen an die Wortbreite des verwendeten
> Prozessors anzugleichen, liegt schlicht im Wunsch nach maximaler
> Performanz.

Nö.

Ich verbuche sowas unter Geburtsfehler, von denen C eine Menge hat. 
Immerhin versemmelt man sich genau damit aus Sicht der zu lösenden 
Probleme jegliche Sicherheit, Allgemeingültigkeit und Portierbarkeit. 
Und um die "Performanz" sollte sich der Compiler bzw. dessen Architekt 
kümmern.

Die Sicherheit, daß ein Datentyp eben genau definiert ist, sollte man 
nicht unterschätzen. Das grundsätzliche Ziel einer Programmiersprache 
oberhalb des Assemblers ist es ja gerade, dem Programmierer eine 
Sicherhait für seine problemabhängigen Algorithmen zu geben und nicht 
etwa, daß er sich mit maschinenabhängigen Besonderheiten herumschlagen 
muß. Das ist Obliegenheit der Tools.

Die Sichtweise in C ist da mal wieder nicht die Sicht aus 
Anwenderperspektive, sondern die Sicht des Compiler-Erfinders.

Nochwas: Die Maschinen, die ich damals programmiert hatte, konnten 
überhaupt keinen byteweisen Zugriff. Der Speicher bestand kompletissimo 
aus 16 Bit WORD's und wenn man Text (7 Bit) speichern wollte, dann mußte 
man das selbst per Swap in diese 16 Bits einpassen.

So ging das damals - mit Kernspeicher-Maschinen.

W.S.

von Egon N. (egon2321)


Lesenswert?

Walter T. schrieb:
> "Die Länge einer Diskussion ist invers proportional zur
> Wichtigkeit des
> Themas." So verlangt es das Gesetz.

Quatsch, der Nazivergleich kam noch nicht.

https://de.wikipedia.org/wiki/Godwin%E2%80%99s_law

von Yalu X. (yalu) (Moderator)


Lesenswert?

W.S. schrieb:
> Yalu X. schrieb:
>> Der ursprüngliche Grund, die Größe von int und damit den defaultmäßigen
>> Wertebereich für Integer-Berechnungen an die Wortbreite des verwendeten
>> Prozessors anzugleichen, liegt schlicht im Wunsch nach maximaler
>> Performanz.
>
> Nö.

Gibt es deiner Ansicht nach denn einen anderen möglichen Grund für die
maschinenabhängige Größe von int? Oder haben dei C-Entwickler diese
Entscheidung etwa völlig grundlos getroffen?

> Ich verbuche sowas unter Geburtsfehler, von denen C eine Menge hat.

C hat zwar tatsächlich einige Geburtsfehler, aber die maschinenabhängige
int-Größe gehört definitiv nicht dazu.

> Immerhin versemmelt man sich genau damit aus Sicht der zu lösenden
> Probleme jegliche Sicherheit, Allgemeingültigkeit und Portierbarkeit.

Sprachen mit guter Portabilität (bspw. Fortran) oder guter Performanz
(Assembler) gab es bereits. C sollte ein guter Kompromiss aus beiden
Kriterien werden. Das ist es IMHO auch geworden, aber es ist und bleibt
eben ein Kompromiss, der in keinem der Kriterien das absolute Optimum
erreicht.

Wem heute bei High-Level-Anwendungen Portabilität über alles geht,
programmiert nicht in C, sondern bspw. in Java. Maschinennahe Software,
hingegen (bspw. ein Betriebssystem) ist per se nur eingeschränkt
portabel, so dass andere Kriterien in den Vordergrund treten, in denen
Java wiederum ganz schlecht abschneidet.

Die Programmiersprache, die für alle Anwendungen vom PID-Regler auf
einem 8-Bit-Controller bis hin zum hochkomplexen KI-System gleichermaßen
optimal geeignet ist, gibt es leider noch nicht und wird es vermutlich
auch nie geben.

> Und um die "Performanz" sollte sich der Compiler bzw. dessen Architekt
> kümmern.

Werden die Basisdatentypen erst einmal ungünstig festgelegt, kann das
auch der beste Optimierer nicht mehr richten. Man muss auch sehen, dass
in den 70ern die Compilertechnik noch nicht so weit fortgeschritten war
wie heute, und starke Optimierer, wie wir sie heute gewohnt sind, die
RAM-Kapazität der damaligen Rechner um Größenordnungen sprengen würden.

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> störe ich mich überhaupt nicht, da ich einen solchen Ausdruck noch nie
> gebraucht habe

Ich auch nicht. Das ist ein Beispiel, an dem das Problem kurz und 
prägnant deutlich wird.

Die "Zutaten" zu diesem Beispiel allerdings gibt es durchaus: Ein NOT, 
um eingelesene low-aktive Pins zu wandeln, eine Konstante, die irgendwo 
definiert eine Maske der verwendeten Pins enthält, Prüfen ob keiner der 
Pins gesetzt ist, oder alle, oder...

Das ist auch nicht mein Beispiel, das findest Du auf diversen Seiten, 
und das ist nicht 8-Bit spezifisch, das funktioniert unter anderem 
Systemen ebenso.

Yalu X. schrieb:
> Übrigens kann man beim AVR-GCC die int-Größe mit -mint8 auf nicht 8 bit
> einstellen.

Wovon mir, als das Thema vor einigen Monaten mal aufkam, vehement 
abgeraten wurde, es würde zu vielen Problemen führen.

Damals ging es darum, dass C viele Operationen unnötigerweise auf 16 Bit 
aufbläht, wenn 8 Bit auch reichen würden.

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> Damals ging es darum, dass C viele Operationen
> unnötigerweise auf 16 Bit aufbläht, wenn 8 Bit
> auch reichen würden.

Das Problem hat FreePascal eine Etage höher (32bit
vs. 64bit) aber auch.

Ich bin grundsätzlich ein Freund starker Typisierung,
aber das Problem ist mMn damit nicht lösbar.

von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Damals ging es darum, dass C viele Operationen unnötigerweise auf 16 Bit
> aufbläht, wenn 8 Bit auch reichen würden.

Als C definiert wurde kam niemand auf die Idee, es wäre etwas für 8 Bit 
Mikroprozessoren. Es gab keine. Die kleinste Klasse von Rechnern, auf 
denen ein C Compiler sinnvoll schien, waren 16-Bit Minicomputer. Es 
dauerte Jahrzehnte, bis sich C als Crosscompiler-Sprache für 8-Bit 
Mikrocomputer verbreitete.

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Karl schrieb:
>> Damals ging es darum, dass C viele Operationen
>> unnötigerweise auf 16 Bit aufbläht, wenn 8 Bit
>> auch reichen würden.
>
> Als C definiert wurde kam niemand auf die Idee,
> es wäre etwas für 8 Bit Mikroprozessoren. Es gab
> keine.

Kopf-an-Kopf-Rennen:
C     - 1972 erschienen
i8008 - 1972 erschienen
i8080 - 1974 erschienen
Z80   - 1976 erschienen

> Die kleinste Klasse von Rechnern, auf denen ein C
> Compiler sinnvoll schien, waren 16-Bit Minicomputer.

Ja - wegen der klassischen Bindung von C an Unix.

> Es dauerte Jahrzehnte, bis sich C als Crosscompiler-
> Sprache für 8-Bit Mikrocomputer verbreitete.

... was man als Beweis dafür ansehen kann, dass frühe
Fehlentscheidungen NICHT ewigen Bestand haben müssen,
sondern auch korrigiert werden können.

Beitrag #5379147 wurde von einem Moderator gelöscht.
von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Übrigens kann man beim AVR-GCC die int-Größe mit -mint8 auf nicht 8 bit
>> einstellen.
>
> Wovon mir, als das Thema vor einigen Monaten mal aufkam, vehement
> abgeraten wurde, es würde zu vielen Problemen führen.

Ich habe zwar nicht viel Erfahrung mit -mint8, aber solange du in deinen
Programmen keinen Fremdcode benutzt, der nicht für eine int-Größe von 8
bit geschrieben ist (also auch nicht die AVR Libc), dürften IMHO keine
größeren Probleme entstehen.

Hier ist übrigens noch ein kleines Beispiel für den AVR-FPC, dessen
Verhalten auch nicht immer ganz der menschlichen Intuition entspricht:

1
procedure test;
2
  var
3
    w: word;
4
    b: byte;
5
6
  begin
7
8
    { Wegen $e0 + $40 > $ff wird die not-Operation 16-bit-breit ausgeführt }
9
    w := not ($e0 + $40);   { -> $fedf }
10
    do_something_with(w);
11
12
    { Wird $e0 durch eine Byte-Variable mit gleichem Inhalt ersetzt, wird
13
      die not-Operation nur noch 8-bit-breit ausgeführt }
14
    b := $e0;
15
    w := not ( b  + $40);   { -> $00df }
16
    do_something_with(w);
17
18
  end;

Vom FPC generierter Assemblercode (R1 hat den Inhalt 0):

1
PsTEST_ss_TEST:
2
  ldi  r24,-33
3
  ldi  r26,-2
4
  mov  r25,r26
5
  call  PsTEST_ss_DO_SOMETHING_WITHsWORD
6
  ldi  r18,-32
7
  ldi  r24,-33
8
  mov  r25,r1
9
  jmp  PsTEST_ss_DO_SOMETHING_WITHsWORD

Leider konnte ich in der Dokumentation des FPC keine Erklärung für
dieses Verhalten finden.

In C liefern beide Ausdrücke das gleiche Ergebnis:

1
void test(void) {
2
  uint16_t w;
3
  uint8_t b;
4
5
  w = ~(0xe0 + 0x40);     // -> 0xfedf
6
  do_something_with(w);
7
8
  b = 0xe0;
9
  w = ~( b + 0x40);
10
  do_something_with(w);   // -> 0xfedf
11
}

Vom GCC generierter Assemblercode:

1
test:
2
  ldi r24,lo8(-33)
3
  ldi r25,lo8(-2)
4
  call do_something_with
5
  ldi r24,lo8(-33)
6
  ldi r25,lo8(-2)
7
  jmp do_something_with

: Bearbeitet durch Moderator
von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Das Problem hat FreePascal eine Etage höher (32bit
> vs. 64bit) aber auch.

Auf dem PC sehe ich ja ein, dass es sinnvoller ist die Bitbreite der 
Matheunit voll auszunutzen, da geht eine Division mit 32 oder 64 Bit 
auch schnell.

Allerdings ist Pascal hier wenigstens konsequent und sagt: Wenn wir auf 
64 Bit Breite rechnen, dann alles, und wenn Du das anders willst musst 
Du das explizit casten. C wandelt halt einfach nach Belieben.

Auf dem AVR hab ich mir meine eigenen Matheroutinen geschrieben. Da 
Pascal Multiplikation oder Division immer auf gleicher Bitbreite macht, 
wird unnötig viel Overhead erzeugt, genau wie bei C. Wenn ich aber nur 
16bitx16bit=>32bit oder 32bit/8bit=>32bit brauche, spare ich enorm. Eine 
Division in 5usec statt in 200usec ist schon ein Nummer.

Aber wenigstens versucht Pascal nicht meinem 8-Bitter unbedingt 16 Bit 
aufzunötigen.

Was mir unter Pascal deutlich besser gefällt ist die Matheunterstützung. 
Letztens hab ich einen - aktuellen - Beitrag über C gelesen, wo als Tipp 
kam: Nehmen Sie mathematische Funktionen möglichst auseinander. Nur eine 
Operation pro Zeile. Da dachte ich so: Echt jetzt? Euer Ernst? Leider 
finde ich die Seite nicht mehr, vielleicht wurde sie aus Scham gelöscht. 
War das bei heise...?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Letztens hab ich einen - aktuellen - Beitrag über C gelesen, wo als Tipp
> kam: Nehmen Sie mathematische Funktionen möglichst auseinander. Nur eine
> Operation pro Zeile. Da dachte ich so: Echt jetzt? Euer Ernst?

Das denke ich auch ;-)

Bist du sicher, dass es um C und nicht um Bascom ging?

Bei letzterem muss man sogar für jede Rechenoperation eine eigene
Anweisung schreiben.

> Leider
> finde ich die Seite nicht mehr, vielleicht wurde sie aus Scham gelöscht.

So wird es wohl sein ;-)

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Hier ist übrigens noch ein kleines Beispiel für den AVR-FPC, dessen
> Verhalten auch nicht immer ganz der menschlichen Intuition entspricht:

not($E0 + $40) gibt bei mir einen fetten range check error. Ich muss das 
explizit auf uint16 casten. Und dann isses klar, einmal wird das Not auf 
ein Word angewendet, einmal auf ein Byte, und dann erst auf ein Word 
erweitert.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Yalu X. schrieb:
>> Hier ist übrigens noch ein kleines Beispiel für den AVR-FPC, dessen
>> Verhalten auch nicht immer ganz der menschlichen Intuition entspricht:
>
> not($E0 + $40) gibt bei mir einen fetten range check error.

Stimmt, auch wenn "fett" etwas übertrieben ist, denn es erscheint
lediglich eine Warnung, die den Kompiliervorgang nicht abbricht.

Sie kommt daher, dass der Compiler das Ergebnis $fedf als negativ
ansieht, so dass es außerhalb des Wertebereichs von word liegt.

> Ich muss das explizit auf uint16 casten.

Die Warnung verschwindet auch ohne Cast, wenn man für w einen
passenderen Datentyp wählt. Ersetze also

1
    w: word;

durch

1
    w: smallint;

so dass w auch negative Werte annehmen kann. Die Ergebnisse sind aber
immer noch verschieden. Es ändert sich nicht einmal der generierte
Assemblercode.

Was nun?

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Die Ergebnisse sind aber immer noch verschieden. Es
> ändert sich nicht einmal der generierte Assemblercode.
>
> Was nun?

Naja, schätzungsweise ist "not" einfach überladen.

Anders ausgedrückt: "$E0 + $40" liefert dieser
Vermutung nach im ersten Beispiel nicht deswegen ein
word, weil es größer als 255 ist, sondern weil als
Ergebnis ein Word erwartet wird. (Die Konstanten
selbst haben ja in Pascal, wenn ich mich recht
entsinne, keinen Typ.)
Das kannst Du ja leicht prüfen: Sieh' einfach, was
bei "$30 + $50" passiert, und berichte.

Analog im zweiten Beispiel: Byte plus Konstante gibt
erstmal ein Byte; der unäre Operator "not" macht ein
Byte aus dem Byte, und die Zuweisung expandiert auf
das Word. Genau das kommt heraus.

Ich habe da jetzt nicht tiefer drübernachgedacht,
aber ich finde das auch völlig logisch so.

von Possetitjel (Gast)


Lesenswert?

Possetitjel schrieb:

> Analog im zweiten Beispiel: Byte plus Konstante gibt
> erstmal ein Byte; der unäre Operator "not" macht ein
> Byte aus dem Byte, und die Zuweisung expandiert auf
> das Word. Genau das kommt heraus.

Ich bekomme hier noch eine Meise. Warum liefert...
1
program multest; 
2
3
var b1, b2, b3 : byte; 
4
             w : word; 
5
begin 
6
  b1:=100; 
7
  b2:=200; 
8
  b3:=1; 
9
  w:=not((b1*b2)+b3); 
10
  writeln('w=',w);
11
end.

... als Ergebnis "w=45534" (=0xB1DE)?

Meiner Theorie nach hätte Byte mal Byte plus Byte
wiederum Byte ergeben sollen, so dass ich ein
Ergebnis der Art "0x00??" erwartet hätte.

> Ich habe da jetzt nicht tiefer drübernachgedacht,
> aber ich finde das auch völlig logisch so.

Ich nehme das frustriert zurück.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
> Auf dem PC sehe ich ja ein, dass es sinnvoller ist die Bitbreite der
> Matheunit voll auszunutzen, da geht eine Division mit 32 oder 64 Bit
> auch schnell.

Dabei geht es nicht nur um (mathematische) Berechnungen; mWn profitieren 
alle Register- und Speicheroperationen von der nativen Wortbreite.

> C wandelt halt einfach nach Belieben.
"nach Belieben" wäre mir noch nicht aufgefallen.

> Auf dem AVR hab ich mir meine eigenen Matheroutinen geschrieben.
Das kann aber nicht Sinn der Übung sein, oder?

> Was mir unter Pascal deutlich besser gefällt ist die Matheunterstützung.
Inwiefern? Grad vorher schreibst du, du hättest das alles selbst 
geschrieben?

> Damals ging es darum, dass C viele Operationen unnötigerweise auf 16 Bit
> aufbläht, wenn 8 Bit auch reichen würden.
ich mach jetzt doch schon einige Jahre in C, etwas kürzer auf dem AVR, 
aber ich kann mich nicht erinnnern da auf signifikante Probleme gestoßen 
zu sein (was nicht heißt dass solche nicht existieren).

Hast du mal ein wirklich konkretes Beispiel? Also nicht so ein 
konstruiertes wie
1
irgendwas1 == ~irgendwas2
von dem du ja selbst schreibst dass du es noch nie gebraucht hast.

von Rolf M. (rmagnus)


Lesenswert?

Karl schrieb:
> Allerdings ist Pascal hier wenigstens konsequent und sagt: Wenn wir auf
> 64 Bit Breite rechnen, dann alles, und wenn Du das anders willst musst
> Du das explizit casten. C wandelt halt einfach nach Belieben.

Nein, C macht das gleiche. Gerade aus diesem Grund gibt es die 
Integer-Promotion, die dich so stört, doch. Berechnungen werden immer in 
int durchgeführt, es sei denn, die Eingangsdatentypen sind größer. 
Gleiches gilt entsprechend für den Datentyp von Integerkonstanten.
Deshalb sollte int möglichst die native Breite der CPU sein. Es gibt 
allerdings eben die Einschränkung, dass es mindestens 16 Bit breit sein 
muss, was für den Spezialfall einer 8-Bit-CPU nicht ideal ist. Die 
Compiler-Optimierungen sorgen aber dafür, dass der Impact auf Laufzeit 
und Codegröße minimal ist.

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Es gibt allerdings eben die Einschränkung, dass es mindestens 16 Bit
> breit sein muss, was für den Spezialfall einer 8-Bit-CPU nicht ideal
> ist.

...und deshalb dort meist abgestellt werden kann. Für optimierten 
8-bit-code.

Wer portable(r) sein will, lässt es.

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Kopf-an-Kopf-Rennen:
> C     - 1972 erschienen
> i8008 - 1972 erschienen

1972 erschien das Buch, Sprache und Compiler entstanden logischerweise 
vorher. Dass der Dennis Ritchie mit Intel unter einer Decke steckte darf 
man wohl ausschliessen.

Aber ein C Compiler, der auf einem 8008 läuft, das wär schon eine 
Vorstellung für 1972 ;-). Immerhin entstand C als Sprache eines 
laufenden Systems, nicht als Crosscompiler-Sprache für Drittsysteme, wie 
es heute bei Mikrocontrollern üblich ist.

: Bearbeitet durch User
von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Ich bekomme hier noch eine Meise. Warum liefert...
> program multest;
>
> var b1, b2, b3 : byte;
>              w : word;
> begin
>   b1:=100;
>   b2:=200;
>   b3:=1;
>   w:=not((b1*b2)+b3);
>   writeln('w=',w);
> end.
>
> ... als Ergebnis "w=45534" (=0xB1DE)?
>
> Meiner Theorie nach hätte Byte mal Byte plus Byte
> wiederum Byte ergeben sollen, so dass ich ein
> Ergebnis der Art "0x00??" erwartet hätte.

Das zeigt doch schön, das dass grundsätzliche Problem weder bei C noch 
bei Pascal liegt, sondern beim unären Operator 'not' bzw '~'. Sein 
Verhalten ist naturgemäß typgebunden.

Wie ich schon weiter oben geschrieben habe, ist seine einzig sinnvolle 
Anwendung das Zurücksetzen von Bits zusammen mit 'and' bzw '&', wodurch 
die Typgebundenheit aufgehoben wird.

Wenn es um Invertierung geht, ist der binäre Operator 'xor' bzw '^' 
immer die bessere und sicherere Wahl.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
>> Die kleinste Klasse von Rechnern, auf denen ein C
>> Compiler sinnvoll schien, waren 16-Bit Minicomputer.
>
> Ja - wegen der klassischen Bindung von C an Unix.

Weniger. Sondern weil man oft nur den einen Rechner hatte, nämlich den 
Zielrechner. Auf dem sollte der Compiler laufen, Unix oder nicht. So 
wars bei mir Ende der 70er auch, Compiler (PL/65) und Assembler liefen 
auf dem 6502 Rechner selbst. Einen anderen hatte ich nicht.

Mikrocontroller mit Entwicklungssystem auf einem PC gab es sinngemäss in 
den 70ern zwar auch. Minicomputer mit Assembler und ggf. Compiler als 
Entwicklungssystem für Mikrocomputer und Mikrocontroller. Aber in dieser 
Szene war C völlig unbekannt.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Jobst Q. schrieb:
> Das zeigt doch schön, das dass grundsätzliche Problem weder bei C noch
> bei Pascal liegt, sondern beim unären Operator 'not' bzw '~'. Sein
> Verhalten ist naturgemäß typgebunden.

Hier noch ein Beispiel ohne Bitoperationen, sondern nur mit gewöhnlichen
Additionen:

1
procedure test;
2
  const
3
    b1 = 100;
4
5
  var
6
    b2: byte;
7
    i: smallint;
8
9
  begin
10
    i := b1 + 200;         { -> 300 }
11
    do_something_with(i);
12
13
    b2 := 100;
14
    i := b2 + 200;         { ->  44 }
15
    do_something_with(i);
16
  end;

Wie es scheint, liegt dieses (zumindest für C-Programmierer seltsam
anmutende) Verhalten an der Auswertung konstanter Ausdrücke:

Solche Ausdrücke werden durch den Compiler ausgewertet, wobei ein
Wertebereich von -2⁶³..+2⁶⁴-1 (das ist die Vereinigung von int64 und
uint64) abgedeckt wird. Wird dieser Wertebereich überschritten, meldet
der Compiler einen Overflow-Error, ansonsten ist das Ergebnis immer
mathematisch korrekt. Anders als in C wird das Ergebnis also nicht auf
eine bestimmte Bitbreite beschnitten.

Beispiel:

1
Ausdruck: 1000 * 1000
2
AVR-FPC:  -> 1000000
3
AVR-GCC:  ->   16960  (= 1000000 mod 2¹⁶)

Erst nach der Auswertung des konstanten Ausdrucks wird sein Typ
bestimmt, und zwar abhängig vom berechneten Wert.

Beispiel:

1
Ausdruck          Wert    Typ
2
——————————————————————————————————————————————
3
10 + 10             20    uint8 bzw. byte
4
10 - 30            -20    int8
5
1000 * 1000    1000000    uint32 bzw. longword
6
usw.
7
——————————————————————————————————————————————

In einem Ausdruck, der auch Variablen enthält, werden zuerst die
konstanten Teilausdrücke ausgewertet und typisiert, dann kommen die in
der FPC-Dokumentation beschriebenen impliziten Typkonvertierungen zur
Anwendung (auf der Seite nach "type conversion" suchen):

  https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-250003.1.1

In C wird für jede Teiloperation erst der Typ des oder der Operanden
bestimmt und danach mit einer von den Operandentypen abhängigen
Bitbreite das Ergebnis berechnet. Der Ablauf ist also genau andersherum
als in Free Pascal. Dieselbe Methode wird auch für variable Ausdrücke
angewandt, die erst zur Laufzeit berechnet werden.

Das führt letztendlich zu den folgenden Eigenschaften der beiden
Methoden:

Free-Pascal:

- Der Wert konstanter Ausdrücke ist immer richtig (oder der Compiler
  bricht mit einer Fehlermeldung ab, wenn der Wert nicht mit 64 Bit
  darstellbar ist, was aber selten passieren dürfte).

- Ausdrücke mit Variablen können zu abweichenden Ergebnissen führen, da
  dort andere Auswertungsregeln gelten.

C:

- Der Wert wird bei Überläufen beschnitten und ist deswegen nicht immer
  mathematisch korrekt.

- Das Ergebnis eines Ausdrucks hängt nur von dessen Operandenwerten und
  -typen ab, ist aber unabhängig davon, ob es sich bei den Operanden um
  Konstanten oder Variablen handelt.

Beide Sprachen haben also ein paar Überraschungen parat. Wenn man die
genannten Regeln genau kennt, kann man mit beiden Methoden gut leben.
Deswegen sollte man sich das Regelwerk einmal genau durchlesen, die
Frage ist nur, wo:

- Bei Free Pascal habe ich in der Dokumentation immer noch keine
  Beschreibung der Auswertung und Typisierung konstanter Ausdrücke
  gefunden, aber vielleicht habe ich auch einfach nur Tomaten auf den
  Augen :)
  Die ISO-Normen für Pascal und Extended Pascal stellen leider
  keine Hilfe bei Fragen zu aktuellen Pascal-Implementationen dar.

- Für C gibt es viel mehr Informationsquellen (sowohl auf Papier als
  auch im Netz). Im Zweifelsfall kann man die ISO-Norm zu Rate ziehen,
  die solche (und andere) Dinge naturgemäß mit absoluter Präzision
  beschreibt.

: Bearbeitet durch Moderator
von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

>> Meiner Theorie nach hätte Byte mal Byte plus Byte
>> wiederum Byte ergeben sollen, so dass ich ein
>> Ergebnis der Art "0x00??" erwartet hätte.
>
> Das zeigt doch schön, das dass grundsätzliche Problem
> weder bei C noch bei Pascal liegt, sondern beim unären
> Operator 'not' bzw '~'.

Hmm. Weiss nicht. Vielleicht ist das Problem nur mein
schlechtes Gedächtnis. Ich war GANZ sicher, das TurboPascal
bei
1
 
2
  b1:=100; 
3
  b2:=200; 
4
  w:=b1*b2;

als Ergebnis "32" geliefert hätte. FreePascal liefert jedoch
20'000.


> Sein Verhalten ist naturgemäß typgebunden.

Sicher -- aber lt. Online-Hilfe ist "not" nur für Bytes (!)
definiert.

Im dargestellten Beispiel
1
 
2
  b1:=100; 
3
  b2:=200; 
4
  b3:=1; 
5
  w:=not((b1*b2)+b3);

kommt lt. Test "w=45534" heraus, was das Negat von 20001 ist.

Wie kann es sein, dass in einem Ausdruck, der angeblich nur
aus Byte-Operanden und Byte-Operatoren besteht, bereits die
einzelnen Faktoren der Multiplikation (!!) auf 16bit erweitert
werden, obwohl in dem arithmetischen Ausdruck nichts steht,
das auch nur entfernt nach 16Bit aussieht?

von (prx) A. K. (prx)


Lesenswert?

Wer die integer promotion von C nicht mag - ist der Umgang von Free 
Pascal mit ganzzahligen Rechnungen tatsächlich einfacher?
https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-260003.1.1

Possetitjel schrieb:
> Wie kann es sein, dass in einem Ausdruck, der angeblich nur
> aus Byte-Operanden und Byte-Operatoren besteht, bereits die
> einzelnen Faktoren der Multiplikation (!!) auf 16bit erweitert
> werden, obwohl in dem arithmetischen Ausdruck nichts steht,
> das auch nur entfernt nach 16Bit aussieht?

Das geschieht gemäss obiger Referenz, wenn die "native integer size" 16 
Bits beträgt:

`Every platform has a ”native” integer size, depending on whether the 
platform is 8-bit, 16-bit, 32-bit or 64-bit. e.g. On AVR this is 8-bit.

Every integer smaller than the ”native” size is promoted to a signed 
version of the ”native” size. Integers equal to the ”native” size keep 
their signedness.´

von (prx) A. K. (prx)


Lesenswert?

Wer das alles zu unintuitiv ist, der kann sich bei PL/I umsehen. Da legt 
man bei der Deklaration von Variablen die Anzahl Bits vor und nach dem 
Komma fest, "fixed binary (15,0)" ist eine 16 Bit Integer in binärer 
Darstellung - das Vorzeichen geht extra, 15 Bits insgesamt, davon 0 nach 
dem Komma.

Bei den Operatoren darf man dann kopfrechnen. Wenn man fixed(P,Q) und 
fixed(R,S) addiert oder subtrahiert, dann kommt ein 
fixed(MIN(N,1+MAX(P-Q,R-S)+MAX(Q,S)),MAX(Q,S)) raus, mit N als 
implementiertem Maximum. Multiplikation ist natürlich einfacher: 
fixed(MIN(N,P+R+1),Q+S). Also solange es in die Grenzen der 
Implementierung passt gibts keinen Überlauf.

http://documentation.microfocus.com/help/topic/com.microfocus.eclipse.infocenter.studee60ux/BKPFPFEXPRMATHOPS.html

Bei Bitoperation war man ebenfalls sehr korrekt. Das macht man nicht mit 
Zahlen, sondern mit Bitstrings deklarierter Länge. Und weil das eben 
Strings sind, erweitern Operatoren auf Operanden unterschiedlicher Länge 
den kürzeren String linksbündig. '10000'B | '01'B ergibt '11000'B

http://documentation.microfocus.com/help/topic/com.microfocus.eclipse.infocenter.studee60ux/BKPFPFEXPRS011.html

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Das geschieht gemäss obiger Referenz,

Vielen Dank für den Link.


> wenn die "native integer size" 16 Bits beträgt:
>
> `Every platform has a ”native” integer size, [...]

Oh Gott. Nicht auch Pascal... !

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
>> `Every platform has a ”native” integer size, [...]
>
> Oh Gott. Nicht auch Pascal... !

;-)

Nur dass Free Pascal auch 8 Bits als native integer zulässt.

Aber es ist schon ein wenig anders als in C. Komplizierter nämlich. 
Beispielsweise weil es Ausnahmen gibt, etwa indem die Differenz zweier 
vorzeichenloser Typen ein Vorzeichen hat. Und wenn man Typen mit und 
ohne Vorzeichen mischt, kann es je nach Grösse auf ein Ergebnis 
rauslaufen, das doppelt so gross ist. Beides ist nicht unlogisch, aber 
kompliziert. Und natürlich ist das abhängig vom konkreten Pascal, Delphi 
anders als Turbo Pascal.

: Bearbeitet durch User
von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Wie ich schon weiter oben geschrieben habe, ist seine einzig sinnvolle
> Anwendung das Zurücksetzen von Bits zusammen mit 'and' bzw '&', wodurch
> die Typgebundenheit aufgehoben wird.

Ja klar, alle blöd ausser Du...

Die wohl berühmteste Routine des Forums: 
https://www.mikrocontroller.net/articles/Entprellung#Timer-Verfahren_.28nach_Peter_Dannegger.29

Siehe unten bei C-Code.

Jobst Q. schrieb:
> Wenn es um Invertierung geht, ist der binäre Operator 'xor' bzw '^'
> immer die bessere und sicherere Wahl.

Schon deswegen nicht, weil xor immer eine Hilfsvariable braucht.

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Hast du mal ein wirklich konkretes Beispiel? Also nicht so ein
> konstruiertes wie

Ich weiss ja nicht, ob Dir das konstruiert ist:

[code]void test(void) {
  char a;
  a = a * 4;
  a = a << 2;
}

  ldd r24,Y+1
  lsl r24
  lsl r24
  std Y+1,r24
  ldd r24,Y+1
  clr r25
  sbrc r24,7
  com r25
  lsl r24
  rol r25
  lsl r24
  rol r25
  std Y+1,r24[code]

Ich hab das damals mit uint8_t gemacht, aber das nimmt der GCC im 
Codeexplorer nicht an, und AvrStudio 7 habe ich wegen exzessiver 
Platzverschwendung von der Platte gehauen.

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Erst nach der Auswertung des konstanten Ausdrucks wird sein Typ
> bestimmt, und zwar abhängig vom berechneten Wert.

Und das ist gut so:
1
const
2
  Cfcpu = 16000000;  // 16MHz Quarz
3
...
4
const
5
  fmul = 1 * Cfcpu div 1000000;

Bin ich ganz froh, das fmul hier nur ein Byte ist und nicht 4 Bytes.

A. K. schrieb:
> Wer die integer promotion von C nicht mag - ist der Umgang von Free
> Pascal mit ganzzahligen Rechnungen tatsächlich einfacher?

Ähm nein, da ist auch historisch viel Mist gewachsen. Deswegen verwende 
ich für den AVR prinzipiell nur uint8, uint16, int8, int16. Auf dem PC 
allerdings habe ich mir angewöhnt, dass ein Zähler von 1 bis 10 durchaus 
integer sein darf, und damit je nach Maschine 32 oder 64 Bits, weil es 
eh keinen Unterschied macht.

A. K. schrieb:
> Da legt
> man bei der Deklaration von Variablen die Anzahl Bits vor und nach dem
> Komma fest, "fixed binary (15,0)" ist eine 16 Bit Integer in binärer
> Darstellung - das Vorzeichen geht extra, 15 Bits insgesamt, davon 0 nach
> dem Komma.

Ja und, Festkommazahl, geht bei Ada auch und für FreePascal gibts glaub 
ich eine Lib für. Der AVR kann das ja sogar bedingt, allerdings scheint 
es so ein Hirnkrampf für den Programmierer zu sein, dass es kaum jemand 
nimmt.

von Possetitjel (Gast)


Lesenswert?

Karl schrieb:

> const
>   fmul = 1 * Cfcpu div 1000000;
>
> Bin ich ganz froh, das fmul hier nur ein Byte ist und
> nicht 4 Bytes.

Verstehe ich nicht. Würde Dir die Hand abfaulen, wenn Du
1
 
2
fmul = byte(1 * Cfcpu div 1000000);

schreiben müsstest?

von (prx) A. K. (prx)


Lesenswert?

Karl schrieb:
> Ja und, Festkommazahl, geht bei Ada auch und für FreePascal gibts glaub
> ich eine Lib für.

Es ging mir dabei weniger um die Nachkommastellen. Sondern darum, dass 
das Ergebnis einer Addition von zwei 16-Bit Operanden ein Typ mit 17 
Bits war. Das war die normale Integer-Rechnung, eine andere gab es 
nicht.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Und das ist gut so:
> const
>   Cfcpu = 16000000;  // 16MHz Quarz
> ...
> const
>   fmul = 1 * Cfcpu div 1000000;
> Bin ich ganz froh, das fmul hier nur ein Byte ist und nicht 4 Bytes.

fmul ist eine Compilezeitkonstante. Die belegt auf dem Zielsystem kein
einziges Byte.

von Karl (Gast)


Lesenswert?

Michael R. schrieb:
> Inwiefern? Grad vorher schreibst du, du hättest das alles selbst
> geschrieben?

Beides.

Ich habe ein Projekt mit mehreren umfangreichen Berechnungen von C auf 
Pascal übertragen und meiner Beobachtung nach produziert hier Pascal 
besseren Code bzw. nutzt Register besser aus für die gleichen 
Berechnungen.

Allerdings setzt Pascal genau wie C einige Berechnungen zu aufwändig um. 
Für a32 = b16 x c16 muss zwingend b und c auf 32 Bit erweitert werden 
und eine 32 Bit Multiplikation ausgeführt werden. Mit einer 16 Bit 
Multiplikation ist a auch nur 16 Bit.

In Assembler kann ich aber b16 und c16 problemlos zu a mit 32 Bit 
multiplizieren.

Nun müssen bei 32x32 Bit natürlich selbst unter Verwendung des 
Hardware-Multiplizierers deutlich mehr Register rumgeschubst werden als 
bei 16 x 16 Bit, einschließlich des pushens und poppens dieser Register, 
der längeren Laufzeit und des größeren Speicherbedarfs. Da dauert die 
Berechnung durchaus 20mal so lange. Bei Division ist es noch extremer, 
da alles in Software.

Und nun kommts drauf an: Muss ich nur ab und zu einen Wert berechnen, 
nehm ich die fertigen Routinen. Muss ich was in Echtzeit berechnen, über 
viele Werte, sprich es muss einfach schnell sein, nehm ich die 
optimierten Assembler-Routinen.

von Jobst Q. (joquis)


Lesenswert?

Karl schrieb:
> Jobst Q. schrieb:
>> Wenn es um Invertierung geht, ist der binäre Operator 'xor' bzw '^'
>> immer die bessere und sicherere Wahl.
>
> Schon deswegen nicht, weil xor immer eine Hilfsvariable braucht.

Meistens keine Variable, sondern eine Konstante. Und mit dieser 
Konstante kann man genau bestimmen, wieviele und welche Bits invertiert 
werden. Unabhängig vom Compiler, vom Zielprozessor und von irgendwo 
festgelegten Typen. Also nur Vorteile.

von Karl (Gast)


Lesenswert?

Jobst Q. schrieb:
> Meistens keine Variable, sondern eine Konstante.

Es gibt kein xor mit Konstante auf dem AVR. Du musst eine Konstante 
immer erst in ein Register laden und dann kannst Du das Register mit 
Deinem Wert ver-xor-en.

von Jobst Q. (joquis)


Lesenswert?

Es geht hier um C oder Pascal, nicht um Assembler. Was der Compiler 
daraus macht ist seine Sache.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Karl schrieb:
> Allerdings setzt Pascal genau wie C einige Berechnungen zu aufwändig um.
> Für a32 = b16 x c16 muss zwingend b und c auf 32 Bit erweitert werden
> und eine 32 Bit Multiplikation ausgeführt werden.

Der AVR-GCC macht das schon seit geraumer Zeit besser, zumindest bei
Controllern mit Hardwaremultiplizierer. Johann, der auch hier im Forum
aktiv ist, hat's gerichtet:

  https://gcc.gnu.org/ml/gcc-patches/2011-06/msg02114.html

Die selbstgeschriebene 16-Bit-Multiplikation mit 32-Bit-Ergebnis
benötigst du nur noch für Pascalprogramme.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Und mit dieser Konstante kann man genau bestimmen,
> wieviele und welche Bits invertiert werden.
> Unabhängig vom Compiler, vom Zielprozessor und von
> irgendwo festgelegten Typen. Also nur Vorteile.

Mit Verlaub -- aber da widerspreche ich.

Von einer Hochsprache, die den Namen verdient, erwarte
ich schon, dass ich die üblichen booleschen Operationen
einfach im Klartext (egal, ob Schlüsselwort oder
Sonderzeichen-Operator) hinschreiben kann -- und nicht
erst den Karnaugh-Plan im Kopf aufstellen muss. Als
"üblich" würde ich mal NOT, AND, OR, XOR auffassen.

(Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
es ist ein ziemlicher Krampf, dort einen Flankendetektor
zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)

Vielleicht übersehe ich ja etwas, aber mir erschließt
sich das fundamentale Problem mit "NOT" nicht. Das ist
ein unärer Operator, der Datentyp des Ergebnisses kann
(=sollte) also derselbe wie der des Operanden sein.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Possetitjel schrieb:
>>> `Every platform has a ”native” integer size, [...]
>>
>> Oh Gott. Nicht auch Pascal... !
>
> ;-)
>
> Nur dass Free Pascal auch 8 Bits als native integer
> zulässt.

Naja, ich halte, drastisch formuliert, native integers
generell für Schwachsinn und einen Irrweg.

Hochsprachen sollten die Portabilität fördern -- und
nicht noch dazu einladen, die Plattformabhängigkeiten
gleichmäßig über den Quelltext zu verteilen.

> Beides ist nicht unlogisch, aber kompliziert. Und natürlich
> ist das abhängig vom konkreten Pascal, Delphi anders als
> Turbo Pascal.

Furchtbar.

Aber an die heilige Kuh, die seit Jahrzehnten tradierte
implizite Modulo-Arithmetik, hat sich wieder einmal niemand
herangetraut. Typisch.

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Naja, ich halte, drastisch formuliert, native integers
> generell für Schwachsinn und einen Irrweg.

Viele Prozessoren favorisieren bestimmte Datentypen. 32-Bit RISCs tun 
sich bei Rechnungen mit 32-Bit Integers leichter als mit kleineren 
Typen. Wenn eine Sprache also der Implementierung die Möglichkeit gibt, 
in der favorisierten Breite zu rechnen ohne bei jedem Schritt auf 
Einhaltung einer kleineren Breite zu bestehen, dann ist das schlicht ein 
Tribut an die Effizienz.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Viele Prozessoren favorisieren bestimmte Datentypen. 32-Bit
> RISCs tun sich bei Rechnungen mit 32-Bit Integers leichter
> als mit kleineren Typen.

Ja, ich weiss. Das ist mir klar.


> Wenn eine Sprache also der Implementierung die Möglichkeit
> gibt, in der favorisierten Breite zu rechnen ohne bei jedem
> Schritt auf Einhaltung einer kleineren Breite zu bestehen,
> dann ist das schlicht ein Tribut an die Effizienz.

Sicher -- aber dazu ist kein "native integer" notwendig.

Es wäre völlig ausreichend, wenn man beim Definieren
der Variablen keinen "Typ", sondern außer dem Hinweis
"integer" noch den notwendigen Wertebereich angeben könnte:
1
 
2
var 
3
  menuepunkt : integer range(1..20);
Das würde genau denselben Effekt erzielen; der Compiler
wäre völlig frei in seiner Entscheidung, auf welchen
plattform-eigenen Typ er diese Variable abbildet -- aber
es wäre dennoch vollständig portabel.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Hochsprachen sollten die Portabilität fördern -- und
> nicht noch dazu einladen, die Plattformabhängigkeiten
> gleichmäßig über den Quelltext zu verteilen.

in Ergänzung zu A.K.s richtigem Kommentar meine ich, dass "native 
integers" genau das tun - Portabilität fördern.

Wenn ich schreibe
1
for (int i = 42; i> 0; i--) {
2
   magic_smoke(i)
3
}
dann möchte ich dass auf einer 64-bit-CPU i 64 bit hat, detto mit 32, 16 
(mal abgesehen davon dass der hochoptimierende Compiler noch eingreift)

von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Von einer Hochsprache, die den Namen verdient, erwarte
> ich schon, dass ich die üblichen booleschen Operationen
> einfach im Klartext (egal, ob Schlüsselwort oder
> Sonderzeichen-Operator) hinschreiben kann -- und nicht
> erst den Karnaugh-Plan im Kopf aufstellen muss. Als
> "üblich" würde ich mal NOT, AND, OR, XOR auffassen.

Die Probleme treten ja nicht bei den boolschen (logischen) Operatoren 
auf, sondern bei den Bitoperatoren. C unterscheidet das aus gutem Grund 
mit unterschiedlichen Zeichen. ! && || für die Logik und ~ & | ^ für die 
Bitmanipulationen.

Bei den Bitoperatoren ist das NOT ~ als unärer ein typabhängiger 
Sonderfall. Es hängt vom Typ ab, wieviele Bits betroffen sind. Bei den 
binären Operatoren AND, OR und XOR wird das von den Operanden bestimmt.

Deshalb würde ich das ~ vermeiden, so gut es geht, aus Gründen der 
Vorhersehbarkeit und der Portabilität, die du ja auch sehr schätzt.

Possetitjel schrieb:
> (Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
> es ist ein ziemlicher Krampf, dort einen Flankendetektor
> zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)

Wieso?

U Neu; UN Alt; = PosFlanke;
UN Neu; U Alt; = NegFlanke;
U Neu; X Alt; = Aenderung;
...
U Neu; = Alt;

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Possetitjel schrieb:
>> Hochsprachen sollten die Portabilität fördern -- und
>> nicht noch dazu einladen, die Plattformabhängigkeiten
>> gleichmäßig über den Quelltext zu verteilen.
>
> in Ergänzung zu A.K.s richtigem Kommentar meine ich,
> dass "native integers" genau das tun - Portabilität
> fördern.

Nur in speziellen Fällen.

> Wenn ich schreibe
> for (int i = 42; i> 0; i--) {
>    magic_smoke(i)
> }

Das darf man m.W. in Pascal nicht schreiben; diese
"inline-Deklaration" war zumindest bisher nicht erlaubt.


> dann möchte ich dass auf einer 64-bit-CPU i 64 bit hat,
> detto mit 32, 16 (mal abgesehen davon dass der
> hochoptimierende Compiler noch eingreift)

Bei allem gebotenen Respekt glaube ich, dass Du EIGENTLICH
etwas anderes willst: Den Integer-Datentyp, der
1. maximale Rechengeschwindigkeit ermöglicht und
2. den notwendigen Zahlbereich abdeckt.

Oder möchtest Du im (leicht modifizierten) Beispiel:
1
 
2
for (int i = 300; i> 0; i--) {
3
   magic_smoke(i)
4
}
auf einer 8-bit-Maschine einen Compilerfehler bekommen?

Ich würde das nicht wollen.

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> var
>   menuepunkt : integer range(1..20);

> Das würde genau denselben Effekt erzielen; der Compiler
> wäre völlig frei in seiner Entscheidung, auf welchen
> plattform-eigenen Typ er diese Variable abbildet -- aber
> es wäre dennoch vollständig portabel.

Eine solche Spezifikation geht am Problem vorbei. Denn das Problem 
besteht nicht in der Breite der Speicherung von Variablen. Sondern in 
der Breite der Berechnung von Ausdrücken, die Variablen verwenden. Wenn 
du dieses Regelwerk nur von solchen Deklarationen abhängig machst, dann 
landest du ungefähr beim erwähnten PL/I, also mit individuellen 
Rechenbreiten für jeden Rechenschritt. Nur eben in Wertebereichen, statt 
wie in PL/I in Bits oder Digits.

Die Multiplikation zweier [1..100] Variablen ergäbe also ein 
Zwischenergebnis [1..10000] und müsste somit mit mindestens 16 Bits 
stattfinden. Denn du wirst mit Variablen, die als [1..100] deklariert 
sind, wohl kaum meinen, dass dies auch auf das Produkt zutreffen sollte.

Weitere Rechenschritte führen dann zu weiteren möglichen Änderungen in 
der Rechenbreite. Wird dabei von der Implementierung vorgegebene 
Maximalbreite überschritten - und das ist bei Multiplikationen schnell 
der Fall - dann dürfte nicht implizit abgeschnitten werden, denn das 
wäre ja nicht portabel. Es liefe also darauf hinaus, bei vielen 
Rechenausdrücken für einzelne Zwischenschritte jeweils explizit die 
Rechenbreite im Programm anzugeben. Also sowas wie
 x *[0..50000] y
statt
 x * y
um dem Compiler mitzuteilen, dass er nur mit diesem Wertebereich rechnen 
muss, auch wenn die Teilrechnungen hinter x und y eigentlich eher eine 
nicht vorhandene 256-Bit Multiplikation erzwängen.

Bei Operationen auf Bits gibts das ähnlich, nämlich bei Linksshifts, 
wenn die rechte Seite nicht konstant ist. Da lässt sich die 
erforderliche Rechenbreite im Programm nur schlecht statisch aus den 
Typen der Teilausdrücke ableiten, so dass auch hier der Programmierer 
bei jeder einzelnen solchen Operation explizit abgeben müsste, wie breit 
sie durchgeführt werden muss.

Alternativ wären ausschliesslich Berechnungen der Form
  a = b <op> c
und
  a = <op> c
mit Variablen a,b,c zulässig, weil sich dann aus dem Typ von a,b,c die 
Breite der Berechnung ableiten liesse. Komplexere Rechenausdrücke 
müssten entsprechend zerlegt werden. Also nix mit a = b * c - d.

Viel Spass.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Oder möchtest Du im (leicht modifizierten) Beispiel:
>   for (int i = 300; i> 0; i--) {
>     magic_smoke(i)
>   }
> auf einer 8-bit-Maschine einen Compilerfehler bekommen?

In einer Sprache, in der sich der Typ von "i" aus dem Typ der 
Initialisierung und der Verwendung ergibt, liesse sich das in diesem 
Beispiel vermeiden. Allerdings müsste dann die interne Darstellung von 
"i" in
   for (integer i = 100; i > x; i--)
      magic_smoke(i)
eigentlich vom Typ von "x" (kann -1000000 sein) und vielleicht auch von 
der Parameterdeklaration von magic_smoke abhängen. In komplexeren 
Beispielen blickt dann aber schnell niemand mehr durch.

In einem C Statement
    for (int i = 300000; i > 0; i--)
würde ich es allerdings schon vorziehen, gewarnt zu werden, wenn int nur 
16 Bits haben sollte. Du wirklich nicht?

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Possetitjel schrieb:
>> var
>>   menuepunkt : integer range(1..20);
>
>> Das würde genau denselben Effekt erzielen; der Compiler
>> wäre völlig frei in seiner Entscheidung, auf welchen
>> plattform-eigenen Typ er diese Variable abbildet -- aber
>> es wäre dennoch vollständig portabel.
>
> Eine solche Spezifikation geht am Problem vorbei. Denn
> das Problem besteht nicht in der Breite der Speicherung
> von Variablen. Sondern in der Breite der Berechnung von
> Ausdrücken, die Variablen verwenden.

Nee, Moment.

Ich kann Dir folgen -- aber das sind unterschiedliche
Teilprobleme.

Wir waren zwischendurch auf die "native Integers" abge-
schweift, darauf, dass ich sie für einen Irrweg halte, und
Deine Entgegnung, dass man sie aus Performancegründen
dennoch haben will.
Meine Erwiderung DARAUF ist: Die Typsysteme gängiger
Programmiersprachen spezifizieren z.T. das Falsche. Für
eine Laufvariable ist u.U. völlig wurscht, wievel Speicher
sie benötigt -- man will einfach, dass die Kiste schnell
und richtig rechnet. Man sollte daher eine Möglichkeit
schaffen, solche Variablen nach WERTEBEREICH und nicht
nach SPEICHERBEDARF zu deklarieren.

Das bringt nämlich die gegensätzlichen Forderungen nach
Performance und Portabilität unter einen Hut: Wenn den
Programmierer der Speicherbedarf nicht interessiert,
sondern nur ein bestimmter Wertebereich gefordert wird,
dann soll er gefälligst auch den Wertebereich angeben --
und nicht die Speichergröße!

Der Unterschied meines Vorschlages zum "native integer"
ist: Der Compiler kann prüfen, ob der Zahlbereich
ausreicht, und bei Portierung auf eine andere Plattform
mit anderer native-integer-Größe einen geeigenten
non-native-integer wählen!

Das hat aber mit der hauptsächlich diskutierten Frage
nach "integer promotion", impliziter Typkonvertierung
usw. nicht direkt zu tun, das war ein Seitenast.

von F. F. (foldi)


Lesenswert?

Thomas H. schrieb:
> Zitat von einem unserer Informatiker: "Code soll für
> einen selbst übersichtlich und lesbar sein. Optimierung macht der
> Compiler".

Sollte über (und unter) jedem Tutorial stehen.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> In einer Sprache, in der sich der Typ von "i" aus
> dem Typ der Initialisierung und der Verwendung ergibt,
> liesse sich das in diesem Beispiel vermeiden.

Ich möchte nicht missverstanden werden: Es ging mir
nicht darum, die üblichen Typen generell abzuschaffen
und durch die Wertebereichs-Spezifikation oder irgend
eine Art von Heuristik zu ersetzen.

Der Vorschlag war nur, ZUSÄTZLICH eine Typspezifikation
über den Wertebereich zu haben, damit der Programmierer
die (plattformunabhängige) Kontrolle über den Wertebereich
behält, die Auswahl der internen Zahldarstellung aber dem
Compiler überlassen kann.

> Allerdings müsste dann die interne Darstellung von
> "i" in
>    for (integer i = 100; i > x; i--)
>       magic_smoke(i)
> eigentlich vom Typ von "x" (kann -1000000 sein) und
> vielleicht auch von der Parameterdeklaration von
> magic_smoke abhängen.

Naja, ich hatte eigentlich Pascal vor Augen, wo diese
on-the-fly-Deklarationen m.W. nicht erlaubt sind.

> In komplexeren Beispielen blickt dann aber schnell
> niemand mehr durch.

Klar -- deswegen ja mein primitiver Vorschlag, den
gewünschten Wertebereich explizit durch Konstanten
anzugeben.

> In einem C Statement
>     for (int i = 300000; i > 0; i--)
> würde ich es allerdings schon vorziehen, gewarnt zu
> werden, wenn int nur 16 Bits haben sollte. Du wirklich
> nicht?

Naja, in meinem Universum würde es das Problem nicht
geben, weil in
1
 
2
var 
3
  pixelcount : integer range(0..300000);
das "integer" nicht für einen spezifischen Datentyp
stehen sollte, sondern als Aufforderung an den Compiler,
aus den auf der jeweiligen Plattform zur Verfügung
stehenden Datentypen einen auszuwählen, der den geforderten
Zahlbereich -- im Beispiel also 1-300'000 --  abbilden kann.

Auf einer 32bit-Maschine und Optimierung auf Geschwindigkeit
wäre das ganz sicher ein 32bit-native-integer, auf einer
16-bit-Maschine sicherlich irgend etwas anderes.

Programme profitieren auf diese Art bei Portierung
"automatisch" von einer größeren Wortbreite, ohne dass bei
kleineren Prozessoren die Gefahr von unerkannten Überläufen
besteht.

von Possetitjel (Gast)


Lesenswert?

Jobst Q. schrieb:

> Possetitjel schrieb:
>> (Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
>> es ist ein ziemlicher Krampf, dort einen Flankendetektor
>> zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)
>
> Wieso?

Das weiss ich leider nicht mehr genau. Kann sein, dass es
nicht auf der S7, sondern der S5 war; bin nicht sicher.

Der Kernpunkt war jedenfalls, das "not(x)" durch "x xor 1"
nachgebildet werden musste. Natürlich geht das -- aber es
ist doch irgendwie Krampf. Muss das im dritten Jahrtausend
WIRKLICH noch sein?

von Karl (Gast)


Lesenswert?

Yalu X. schrieb:
> Der AVR-GCC macht das schon seit geraumer Zeit besser, zumindest bei
> Controllern mit Hardwaremultiplizierer. Johann, der auch hier im Forum
> aktiv ist, hat's gerichtet:

Anscheinend 2011, der GCC 4.6.4 scheints nicht zu können, der ist von 
2013.
1
  ldd r24,Y+1
2
  ldd r25,Y+2
3
  ldd r18,Y+3
4
  ldd r19,Y+4
5
  mov r22,r18
6
  mov r23,r19
7
  rcall __mulhi3
8
  clr r26
9
  sbrc r25,7
10
  com r26
11
  mov r27,r26
12
  std Y+5,r24
13
  std Y+6,r25
14
  std Y+7,r26
15
  std Y+8,r27

von Karl (Gast)


Lesenswert?

Possetitjel schrieb:
> Klar -- deswegen ja mein primitiver Vorschlag, den
> gewünschten Wertebereich explizit durch Konstanten
> anzugeben.

Kannst Du machen, nimmst Du Ada.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Man sollte daher eine Möglichkeit
> schaffen, solche Variablen nach WERTEBEREICH und nicht
> nach SPEICHERBEDARF zu deklarieren.

Ist das (speziell bei Integer) nicht das Selbe? Zumindest bei mir im 
Kopf ist das so... Die Wertebereiche von 8- und 16 bit signed/unsigned 
hab ich eingebrannt, bei 32 Bit wirds schon eng ("irgendwas mit 4 
vorne") und in 99.999% der Fälle muss ich nicht nachdenken ob ich 0..255 
oder 0..65535 oder mehr brauche.

Aber wie oben schon richtig festgestellt wurde, Variablen sind nicht so 
schwierig, komplexer sind Ausdrücke. Aber auch hier habe zumindest ich 
keine Probleme mit der Art wie C das angeht. Die Fälle wo ich hier 
Fehler suchen und durch einen cast beheben musste, kann ich vermutlich 
an einer Hand abzählen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Possetitjel schrieb:
> Aber an die heilige Kuh, die seit Jahrzehnten tradierte
> implizite Modulo-Arithmetik, hat sich wieder einmal niemand
> herangetraut. Typisch.

Erklärst du mir was du hier meinst? Bezieht sich das auf pascal? (dann 
muss ichs nicht verstehen, siehe ich & Python)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
>> Hast du mal ein wirklich konkretes Beispiel? Also nicht so ein
>> konstruiertes wie
>
> Ich weiss ja nicht, ob Dir das konstruiert ist:
>
>
1
> void test(void) {
2
>   char a;
3
>   a = a * 4;
4
>   a = a << 2;
5
> }
6
>
>
>
1
>   ldd r24,Y+1
2
>   lsl r24
3
>   lsl r24
4
>   std Y+1,r24
5
>   ldd r24,Y+1
6
>   clr r25
7
>   sbrc r24,7
8
>   com r25
9
>   lsl r24
10
>   rol r25
11
>   lsl r24
12
>   rol r25
13
>   std Y+1,r24[code]
14
>

Wenn ich dein Beispiel so kompiliere, kommt nix raus, a wird nicht 
verwendet und deshalb komplett rausoptimiert.

ich habs aber mal leicht modifiziert:
1
char Karl (char a)
2
{
3
  a = a * 4;
4
  a = a << 2;
5
  return a;
6
}

Daraus wird bei mir:
1
Karl:
2
swap r24
3
andi r24,lo8(-16)
4
ret
5
.size  Karl, .-Karl
keine 16 bit, und optimaler code (ich denke das lässt sich auch von Hand 
nicht mehr verbessern)

Das von dir beschriebene Ergebnis kriege ich, wenn ich mit -O0 (disable 
all optimizations) kompiliere. Warum machst du das?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Karl schrieb:
>> Der AVR-GCC macht das schon seit geraumer Zeit besser, zumindest bei
>> Controllern mit Hardwaremultiplizierer. Johann, der auch hier im Forum
>> aktiv ist, hat's gerichtet:
>
> Anscheinend 2011, der GCC 4.6.4 scheints nicht zu können, der ist von
> 2013.

Auch -O0 ? Damit löst mein avr-gcc 4.8.1 (auch 2013) das auch noch nach 
__mulhi3 auch, mit -Os (was eigentlich Standard auf AVR sein sollte) 
aber sehr wohl nach __umulhisi3 (der 32=16*16 widening multiplication)

von (prx) A. K. (prx)


Lesenswert?

Possetitjel schrieb:
> Der Vorschlag war nur, ZUSÄTZLICH eine Typspezifikation
> über den Wertebereich zu haben, damit der Programmierer
> die (plattformunabhängige) Kontrolle über den Wertebereich
> behält, die Auswahl der internen Zahldarstellung aber dem
> Compiler überlassen kann.

Das hatte ich schon verstanden. Aber du stelltest diese Datentypen 
direkt in den Kontext deiner Ablehnung von native integers und eine 
solche (sinnvolle) Typdeklaration ändert nichts daran, dass man Regeln 
benötigt, in welcher Breite sowohl damit als auch mit normalen Integers 
gerechnet werden soll.

Pascals native Integers sind kein Aspekt der Speicherung von Daten, 
sondern betreffen nur die Breite der Berechnung in Ausdrücken. Mit einer 
möglichen Wertebereichsdefinition von Variablen wirst du sie nicht los.

> Naja, ich hatte eigentlich Pascal vor Augen, wo diese
> on-the-fly-Deklarationen m.W. nicht erlaubt sind.

Solche Details sind für Grundfragen zur Typisierung von Integers 
irrelevant. In C geht das auch erst seit C99.

> stehenden Datentypen einen auszuwählen, der den geforderten
> Zahlbereich -- im Beispiel also 1-300'000 --  abbilden kann.

Damit beschreibst du wieder die Festlegung von Variablen ...

> Auf einer 32bit-Maschine und Optimierung auf Geschwindigkeit
> wäre das ganz sicher ein 32bit-native-integer, auf einer
> 16-bit-Maschine sicherlich irgend etwas anderes.

... um sofort zur Breite der Berechnung von Ausdrücken umzuschwenken. 
Denn eine 32-Bit Maschine kann (heute) Daten im RAM stets auch kleiner 
speichern, tut sich aber im Umgang mit diesen Daten evtl leichter, wenn 
sie dafür auf native integer erweitert werden.

Die sinnvolle Möglichkeit, [1..100] im Array als Byte zu speichern, in 
einer explizit definierten lokalen Variable, die im Register liegt, aber 
in voller Wortbreite, erspart nicht die Festlegung, in welche Breite man 
mit diesen Daten umgehen sollte. Und da kommen die native integers in 
Spiel.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

PS: Da war doch was... Deine Wertebereichstypen gibts in Pascal doch 
sowieso schon seit Anbeginn der Zeit.

https://www.freepascal.org/docs-html/current/ref/refsu4.html#x26-290003.1.1

von Yalu X. (yalu) (Moderator)


Lesenswert?

A. K. schrieb:
> PS: Da war doch was... Deine Wertebereichstypen gibts in Pascal doch
> sowieso schon seit Anbeginn der Zeit.

Genau.

Und auch in C gibt es mit [u]int_fast<n>_t und [u]int_least<n>_t
flexible Datentypen, mit denen auf portable Weise Wertebereiche für
Variablen festgelegt werden können, zwar nicht so feingranular wie in
Pascal, dafür kann man mit "fast" oder "least" angeben, ob man lieber
Rechenzeit oder Speicherplatz sparen möchte.

Possetitjel schrieb:
> A. K. schrieb:
>> Eine solche Spezifikation geht am Problem vorbei. Denn
>> das Problem besteht nicht in der Breite der Speicherung
>> von Variablen. Sondern in der Breite der Berechnung von
>> Ausdrücken, die Variablen verwenden.
>
> Nee, Moment.
>
> Ich kann Dir folgen -- aber das sind unterschiedliche
> Teilprobleme.

Das erste Teilproblem, um das es dir schwerpunktmäßig geht, sehe ich in
Pascal und C als weitgehend gelöst an.

Das zweite, viel schwierigere Teilproblem hat A. K. diesem Beitrag sehr
gut umrissen:

A. K. schrieb:
> Denn das Problem besteht nicht in der Breite der Speicherung von
> Variablen. Sondern in der Breite der Berechnung von Ausdrücken, die
> Variablen verwenden.
> ...

Ich kenne keine (auch keine exotische) Programmiersprache, in der dieses
Problem zufriedenstellen gelöst wäre.

Interessant ist, dass selbst eine extrem "maschinenferne" Sprache wie
Haskell neben Integer-Typen mit fester und dynamischer Bitbreite einen
plattformspezifischen Integer-Typ (Int) hat. Im Gegensatz zu C wird Int
für Rechenoperationen aber nur dann verwendet, wenn auch die Operanden
von diesem Typ sind.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> Ich kenne keine (auch keine exotische) Programmiersprache, in der dieses
> Problem zufriedenstellen gelöst wäre.

ich denke das liegt auch daran, dass dieses Problem von Der Sprache bzw. 
dem Compiler gar nicht gelöst werden kann; hier sehe ich den 
Programmierer in der Pflicht. Nur er (oder sie) kann die Wertebereiche 
abschätzen, und entsprechend reagieren.

Das zeigt sich schon bei einer einfachen Multiplikation: Die Bitbreite 
des Ergebnisses ergibt sich aus der Summe der Bitbreiten der Operanden. 
Wenn ich also zwei 16-bit-Werte multipliziere, müsste ich die Operation 
in 32 bit ausführen, um sicher keinen Überlauf zu erhalten.

Wenn ich aber weiß (woher auch immer), dass das Ergebnis immer in 16 
bit Platz finden wird, kann ich die Multiplikation in 16 bit ausführen, 
was (zB am AVR) schneller und kürzer ist.

Dieses Wissen kann mir aber keine Sprache und kein Compiler abnehmen.

von Jobst Q. (joquis)


Lesenswert?

Possetitjel schrieb:
> Jobst Q. schrieb:
>
>> Possetitjel schrieb:
>>> (Auf der Siemens-S7-SPS geht das in AWL nicht so einfach;
>>> es ist ein ziemlicher Krampf, dort einen Flankendetektor
>>> zu formulieren, denn (NOT(alt) AND neu) funktioniert nicht.)
>>
>> Wieso?
>
> Das weiss ich leider nicht mehr genau. Kann sein, dass es
> nicht auf der S7, sondern der S5 war; bin nicht sicher.
>
> Der Kernpunkt war jedenfalls, das "not(x)" durch "x xor 1"
> nachgebildet werden musste. Natürlich geht das -- aber es
> ist doch irgendwie Krampf. Muss das im dritten Jahrtausend
> WIRKLICH noch sein?

Die AWL-Anweisungen sind nicht für alle CPUs gleich, nichtmal für die 
Serien. Im Laufe der Zeit sind wohl einige dazugekommen.

Habe gerade gelesen, dass es zum Invertieren die Anweisungen INVI (16 
Bit) und INVD (32 Bit) gibt, aber evtl erst ab S7-1500.

XOW und XOD gibt es aber wohl schon länger.

von (prx) A. K. (prx)


Lesenswert?

Michael R. schrieb:
> ich denke das liegt auch daran, dass dieses Problem von Der Sprache bzw.
> dem Compiler gar nicht gelöst werden kann;

Der aus dem Unix-Universum stammende "bc" kommt dem recht nahe. ;-)
Geht halt als Interpreter einfacher.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael R. schrieb:
> Yalu X. schrieb:
>> Ich kenne keine (auch keine exotische) Programmiersprache, in der dieses
>> Problem zufriedenstellen gelöst wäre.
>
> ich denke das liegt auch daran, dass dieses Problem von Der Sprache bzw.
> dem Compiler gar nicht gelöst werden kann; hier sehe ich den
> Programmierer in der Pflicht.

Richtig. Man müsste dann für jede einzelne Rechenoperation den zu
erwartenden Wertebereich angeben, bspw. so:

A. K. schrieb:
> x *[0..50000] y
> statt
>  x * y

Aber das will man ja auch nicht wirklich.

A. K. schrieb:
> Der aus dem Unix-Universum stammende "bc" kommt dem recht nahe. ;-)
> Geht halt als Interpreter einfacher.

Das geht auch problemlos in kompilierten Sprachen (wird ja teilweise
tatsächlich auch gemacht), ist aber nicht unbedingt effizienzfördernd.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> Richtig. Man müsste dann für jede einzelne Rechenoperation den zu
> erwartenden Wertebereich angeben, bspw. so:
>
> A. K. schrieb:
>> x *[0..50000] y
>> statt
>>  x * y
>
> Aber das will man ja auch nicht wirklich.

Das reicht ja nicht mal!

ich konstruiere mal ein Beispiel: Länge eines Vektors der sich im 
Inneren des Einheitskreises bewegt, bei auf 255 skaliertem 
Einheitskreis. Basierend auf den Wertebereichen könnte der Vektor 360 
lang werden, praktisch ist er aber nie größer als 255.

Oder anders gesagt: Immer wenn die Variablen nicht unabhängig sind, 
können sich geringere Wertebereiche ergeben.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

Yalu X. schrieb:
> Das geht auch problemlos in kompilierten Sprachen (wird ja teilweise
> tatsächlich auch gemacht),

Nur mit dynamischen Datentypen, deren Breite sich also zur Laufzeit 
entwickelt. Klar, kann man als C++ Klasse machen und tut man sicherlich 
auch. Angewandt auf alle normalen Integers der Sprache wäre das aber, 
verglichen mit dem was man hier im Forum üblicherweise als Compiler 
versteht, eher ein als Compiler getarnter Interpreter.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael R. schrieb:
> Das reicht ja nicht mal!
> ...
> Oder anders gesagt: Immer wenn die Variablen nicht unabhängig sind,
> können sich geringere Wertebereiche ergeben.

Genau deswegen braucht man ja die Wertebereichsangaben für die einzelnen
Rechenoperationen, für die A. K. die Syntax

  <op>[<lo>..<hi>]

vorgeschlagen hat. Für dein Beispiel würde das so aussehen:

1
x, y, length: 0..255;
2
3
length := isqrt(sqr(x) +[0..65025] sqr(y))

Damit weiß der Compiler, dass die Addition nur 16- und nicht etwa
17-bit-breit ausgeführt werden muss, wie es bei unabhängigen x und y der
Fall wäre.

Entsprechendes gilt auch für Funktionsaufrufe. Wenn der Compiler nicht
von sich aus erkennt, dass der Aufruf von sqr([0..255]) einen
Wertebereich von [0..65025] hat, müsste der obige Ausdruck
folgendermaßen ergänzt werden:

1
length := isqrt(sqr[0..65025](x) +[0..65025] sqr[0..65025](y))

Ja, irgendwann ist ein Ausdruck dann so sehr mit Wertebereichshinweisen
gespickt, dass überhaupt keiner mehr durchblickt ;-)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Yalu X. schrieb:
> <op>[<lo>..<hi>]

ok, das hatte ich falsch verstanden.

Yalu X. schrieb:
> Ja, irgendwann ist ein Ausdruck dann so sehr mit Wertebereichshinweisen
> gespickt, dass überhaupt keiner mehr durchblickt ;-)

Richtig, und diese (fiktive) Programmiersprache würde ich dann nicht 
sooo gerne verwenden ;-)

Übrigens: Respekt, du hast mein (krudes) Beispiel besser verstanden als 
ich selbst... der Trick liegt in der Addition, für die 16 Bit 
ausreichend sind.

: Bearbeitet durch User
von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Possetitjel schrieb:
>> Man sollte daher eine Möglichkeit schaffen, solche
>> Variablen nach WERTEBEREICH und nicht nach SPEICHERBEDARF
>> zu deklarieren.
>
> Ist das (speziell bei Integer) nicht das Selbe?

Für den Menschen: Fast.
Für den Compiler: Nein.

> Zumindest bei mir im Kopf ist das so...

Ich weiss... anerkannte Berufskrankheit bei Programmierern :)

> Die Wertebereiche von 8- und 16 bit signed/unsigned
> hab ich eingebrannt, bei 32 Bit wirds schon eng [...]

Nein, ich meine das anders: Wenn Du auf einer 8-bit-Maschine
einen kleinen endlichen Automaten mit - was weiss ich - 12
Zuständen programmierst, dann wirst Du für die Zustandsvariable
ein Byte wählen.
Wenn Du jetzt diesen Automaten auf einem ARM verwenden willst,
weil er super funktioniert, zwingst Du den Compiler, auch auf
dem ARM mit einem Byte zu operieren, obwohl ein 32bit-Wort viel
sinnvoller wäre.

Es gibt aber keine Möglichkeit, dem Compiler zu sagen: "Nimm
einen Dir passend scheinenden Integer-Typ, der (mindestens)
die Zahlen 1 bis 12 kennt".

Der Witz dieser Idee zeigt sich erst, wenn man sich einen
Zahlbereich 1..1000 und die Portierung in die umgekehrte
Richtung (vom ARM auf 8 bit) vorstellt: Theoretisch wäre
ein ja word (16bit) ausreichend.
Wenn es meine wertebereichsgesteuerte Typauswahl gäbe, würde
der Compiler auf dem ARM schätzungsweise ein longint wählen --
auf einer 8-bit-Maschine aber tatsächlich ein word, weil das
der kleinste Integer ist, der den Zahlbereich abdeckt.

Die Bereichsangabe 1..1000 ist vollständig portabel; dennoch
könnte vom Compiler immer der optimale Datentyp gewählt werden.

> Aber wie oben schon richtig festgestellt wurde, Variablen
> sind nicht so schwierig, komplexer sind Ausdrücke.

Kommt darauf an. Fließkomma kann außer Betracht bleiben, weil
dort die niederwertigsten Bits (und nicht die höchstwertigen)
wegfallen. Das fällt in die Zuständigkeit der Numerik.

Bitoperationen spielen auch keine Rolle, weil die keinen
Überlauf erzeugen, und wenn sie vernünftig implementiert bzw.
definiert sind, auch keine versteckte Typabhängigkeit haben.

Als Problemfälle bleiben nur die Ganzzahl-Ausdrücke.

> Aber auch hier habe zumindest ich keine Probleme mit der
> Art wie C das angeht. Die Fälle wo ich hier Fehler suchen
> und durch einen cast beheben musste, kann ich vermutlich
> an einer Hand abzählen.

Sicher eine Sache der Gewohnheit.
Ich bin der Meinung, dass
1. die Regeln EINFACH sein und
2. sich nicht dauernd ändern sollten.
Der Rest ist ziemlich wahlfrei.

von Possetitjel (Gast)


Lesenswert?

Michael R. schrieb:

> Possetitjel schrieb:
>> Aber an die heilige Kuh, die seit Jahrzehnten tradierte
>> implizite Modulo-Arithmetik, hat sich wieder einmal niemand
>> herangetraut. Typisch.
>
> Erklärst du mir was du hier meinst?

Klar.

> Bezieht sich das auf pascal?

Nee.

Jeder hält es für normal, dass bei...
1
 
2
var b : byte; 
3
... 
4
b:=255; 
5
incr(b);
... für b Null herauskommt.

Nicht nur, dass das mathematisch falsch und unter dem
Gesichtspunkt der Anwendungslogik häufig unsinnig ist, es
ist auch deshalb ärgerlich, weil der Prozessor intern den
Überlauf sehr wohl registriert, das Flag aber nicht an die
Hochsprache durchreicht.

Dass es auch anders geht, zeigen die diversen SIMD-Einheiten,
die (auch) eine Sättigungsarithmetik haben.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> Das hatte ich schon verstanden. Aber du stelltest diese
> Datentypen direkt in den Kontext deiner Ablehnung von
> native integers und eine solche (sinnvolle) Typdeklaration
> ändert nichts daran, dass man Regeln benötigt, in welcher
> Breite sowohl damit als auch mit normalen Integers gerechnet
> werden soll.
>
> Pascals native Integers sind kein Aspekt der Speicherung von
> Daten, sondern betreffen nur die Breite der Berechnung in
> Ausdrücken. Mit einer möglichen Wertebereichsdefinition von
> Variablen wirst du sie nicht los.

Du hast Recht, ich zwei verschiedene Fragen vermischt.
Entschuldigung.

Mein zügelloses Wettern gegen "native integers" bezog sich
auf den "int"-Datentyp, wie er in C existiert. Während man
z.B. bei "uint8_t" gleichzeitig SOWOHL Speicherbedarf ALS
AUCH Wertebereich festlegt, legt man bei "int" außer der
Tatsache, dass es ganze Zahlen sein sollen, ÜBERHAUPT NICHTS
fest -- also Übertreibung in die andere Richtung.

Die Sachlage in Pascal ist anders; da geht es, wie Du richtig
bemerkst, um die Auswertung von Ausdrücken. Ich muss jetzt,
beim erneuten ruhigen Durchdenken, auch zugeben, dass die
implementierte Lösung einen gewissen Charme hat -- aber
ärgerlich ist sie andererseits doch, denn es entsteht eine
verdeckte Plattformabhängigkeit.

von Possetitjel (Gast)


Lesenswert?

A. K. schrieb:

> PS: Da war doch was... Deine Wertebereichstypen gibts
> in Pascal doch sowieso schon seit Anbeginn der Zeit.

Die haben natürlich bei meinem Vorschlag Pate gestanden.

Ich weiss aber nicht, ob sie genau das leisten, was ich
haben will: Ich will ja keinen NEUEN Datentyp erzeugen, der
dann Pascal-typisch wieder nur zu sich selbst zuweisungs-
kompatibel ist -- ich will, dass der Compiler aus den
EXISTIERENDEN Integertypen einen passenden auswählt.

Aus meinen antiken Pascal-Büchern kann ich nicht heraus-
lesen, wie die Teilbereichstypen im Detail funktionieren.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

> Und auch in C gibt es mit [u]int_fast<n>_t und
> [u]int_least<n>_t flexible Datentypen, mit denen
> auf portable Weise Wertebereiche für Variablen
> festgelegt werden können, zwar nicht so feingranular
> wie in Pascal, dafür kann man mit "fast" oder "least"
> angeben, ob man lieber Rechenzeit oder Speicherplatz
> sparen möchte.

Ahh... richtig, da war was. Danke für die Erinnerung;
das war mir komplett entfallen.


> A. K. schrieb:
>> Denn das Problem besteht nicht in der Breite der
>> Speicherung von Variablen. Sondern in der Breite
>> der Berechnung von Ausdrücken, die Variablen verwenden.
>> ...
>
> Ich kenne keine (auch keine exotische) Programmiersprache,
> in der dieses Problem zufriedenstellen gelöst wäre.

Ich weiss ja nicht, was für Dich "zufriedenstellend" ist.
Da Tcl überhaupt kein für den Programmierer zugängliches
Typkonzept kennt, ist in Tcl natürlich auch die Unterscheidung
verschieden langer Integers hinfällig.

von Possetitjel (Gast)


Lesenswert?

Yalu X. schrieb:

>> Oder anders gesagt: Immer wenn die Variablen nicht
>> unabhängig sind, können sich geringere Wertebereiche
>> ergeben.
>
> Genau deswegen braucht man ja die Wertebereichsangaben
> für die einzelnen Rechenoperationen, [...]

Nee... die braucht man nur für die µC.net-typische
Übertreibung... :)

Es gibt doch erstmal drei separate Probleme:
- Portabilität,
- Auswertungsregeln, die Überlauf verhindern,
- Effizienz.

Mein Ausgangspunkt war gar nicht, dass ich immer und
überall den Überlauf zuverlässig verhindern will --
es ging mir nur darum, dass nicht plattformabhängig
mal ein Überlauf entsteht und mal nicht!

Anders ausgedrückt: Ich wollte keine plattformabhängigen
Auswertungsregeln.

von Wilhelm M. (wimalopaan)


Lesenswert?

Sicher gibt es keine ideale Sprache, die diesem Problem in Gänze gerecht 
wird. Allerdings ist für mich C++ nah dran. Jeder C++-Programmierer 
lernt / sollte lernen ziemlich am Anfang, dass eine der Ideen der 
Sprache die (fast) Gleichbehandlung der primitive DT und UDT sind, mit 
dem Hintergrund, sich ein domänenspezifisches Typsystem zu erzeugen 
(nicht alles ist ein String oder ein int, es gibt Bytes, Meter, Volt, 
etc. und entspr. Operationen).

Insofern adressiere ich das Problem mit Typen wie

[c]
uint_ranged<23, 57> x;
uint_ranged_NaN<0, 19999> y;
uint_circular<0, 15> z;
[\c]

Die notwendigen unterliegenden primitiven DT werden hier bspw. aus dem 
Wertebereich bestimmt. Die Plattformabhängigkeit löst der Standard durch 
uint_fast8_t, etc.

Statische Überläufe erkennt der Compiler, Laufzeitüberläufe als 
Verlassen des Wertebereiches prüfen natürlich Assertionen (sind ja auch 
abschaltbar).

Natürlich kann man sich mehr wünschen, aber so bin ich bzgl. Sicherheit 
und Expressivität schon ein ziemliches Stück weiter als der "alles ist 
ein unsigned char" Ansatz ...

von F. F. (foldi)


Lesenswert?

Wilhelm M. schrieb:
> Allerdings ist für mich C++ nah dran.

Endlich! Ich kaufe gleich Popcorn ein, für heute Abend und die nächsten 
Wochen.
Wenn C++ ins Spiel kommt, wird es lustig.

von Wilhelm M. (wimalopaan)


Lesenswert?

F. F. schrieb:
> Wilhelm M. schrieb:
>> Allerdings ist für mich C++ nah dran.
>
> Endlich! Ich kaufe gleich Popcorn ein, für heute Abend und die nächsten
> Wochen.

Ja, mich hat es auch schon gewundert, dass das noch keiner ins Spiel 
gebracht hatte ... nun, ich muss ja meinem Ruf gerecht werden ;-)

von S. R. (svenska)


Lesenswert?

Possetitjel schrieb:
> Nein, ich meine das anders: Wenn Du auf einer 8-bit-Maschine
> einen kleinen endlichen Automaten mit - was weiss ich - 12
> Zuständen programmierst, dann wirst Du für die Zustandsvariable
> ein Byte wählen.

Nein. Für die Zustandsvariable wähle ich eine Enumeration. Die kann man 
auf einem AVR (allerdings per Compilerschalter) auch auf 8 Bit 
eindampfen und wird je nach Wertebereich automatisch vergrößert.

> Es gibt aber keine Möglichkeit, dem Compiler zu sagen: "Nimm
> einen Dir passend scheinenden Integer-Typ, der (mindestens)
> die Zahlen 1 bis 12 kennt".

Doch, und in C nennt der sich "enum" (mit benannten Werten).
Außerdem kennt C solche Datentypen wie uint_fast8_t und uint_least8_t, 
mit denen ich genau solch ein Verhalten ausdrücken kann.

> Die Bereichsangabe 1..1000 ist vollständig portabel; dennoch
> könnte vom Compiler immer der optimale Datentyp gewählt werden.

CPUs arbeiten auf Vielfachen von Bytes, und außerdem durchgängig im 
Binärsystem. Wenn ich einen Wertebereich von 1..1000 angebe, dann 
erwarte ich auch, dass dieser immer und ausnahmslos eingehalten wird.

Das kostet entweder Performance zur Laufzeit oder ich kann mit Über- 
oder Unterläufen ungültige Werte produzieren. Möchte ich nicht.

> Als Problemfälle bleiben nur die Ganzzahl-Ausdrücke.

Also genau das, wofür Computer gebaut werden - Rechenoperationen. :-)

Possetitjel schrieb:
> [Modulo-Arithmetik]
> Dass es auch anders geht, zeigen die diversen SIMD-Einheiten,
> die (auch) eine Sättigungsarithmetik haben.

Man kann dank der Modulo-Arithmetik bestimmte Dinge sehr effizient 
umsetzen, was mit sättigender Arithmetik nicht ginge. Zumal CPUs bei 
normaler Integer-Arithmetik nicht sättigen können.

Alles, was du möchtest, gibt es in höheren Sprachen, bei denen die 
Laufzeitkosten kein Problem darstellt. Im Extremfall steht dann sowas 
wie coq, bei dem jede Operation passend bewiesen werden muss (und wo die 
Laufzeitkosten wieder wegfallen).

Possetitjel schrieb:
> Mein zügelloses Wettern gegen "native integers" bezog sich
> auf den "int"-Datentyp, wie er in C existiert.

Wie es ihn auch in Pascal gibt, siehe oben.
Wie es ihn in so ziemlich jeder Programmiersprache gibt, denn wenn man 
darauf verzichtet, muss man mit arbitrary precision logic arbeiten und 
das kostet Performance und Speicher.

> Während man
> z.B. bei "uint8_t" gleichzeitig SOWOHL Speicherbedarf ALS
> AUCH Wertebereich festlegt, legt man bei "int" außer der
> Tatsache, dass es ganze Zahlen sein sollen, ÜBERHAUPT NICHTS
> fest -- also Übertreibung in die andere Richtung.

Das ist falsch. Ein "int" ist mindestens 16 Bit lang und auf der CPU 
effizient. Das sind grundsätzlich sinnvolle Randbedingungen und etwas 
völlig anderes als "nichts".

Und wie gesagt, du darfst auch mit den fastN- und leastN-Typen 
hantieren. Oder dich bei Java umschauen, wo Integer ein Objekt ist (dort 
gibt es "int" nur auf Performance-Gründen, nur signed und m.W. mit 
relativ undefinierter Breite).

Was du möchtest, ist an sich nicht verkehrt, aber es ist für 
maschinennahe/effiziente Programmierung (und für nichts anderes ist C 
entwickelt worden) aus den genannten Gründen nicht optimal. Da sind 
andere Kompromisse wichtiger.

von Possetitjel (Gast)


Lesenswert?

Wilhelm M. schrieb:

> Sicher gibt es keine ideale Sprache, die diesem
> Problem in Gänze gerecht wird. Allerdings ist für
> mich C++ nah dran.

Nun ja, ich bin dabei, C zu lernen. Zielpunkt sind
Mikrocontroller.

Wäre es tatsächlich hilfreich, statt C eine noch
wesentlich mächtigere und komplexere Sprache zu
wählen? Ich habe da Zweifel...

von F. F. (foldi)


Lesenswert?

Possetitjel schrieb:
> Wilhelm M. schrieb:
>
>> Sicher gibt es keine ideale Sprache, die diesem
>> Problem in Gänze gerecht wird. Allerdings ist für
>> mich C++ nah dran.
>
> Nun ja, ich bin dabei, C zu lernen. Zielpunkt sind
> Mikrocontroller.
>
> Wäre es tatsächlich hilfreich, statt C eine noch
> wesentlich mächtigere und komplexere Sprache zu
> wählen? Ich habe da Zweifel...

C++ ist schon klasse und ich wollte das nicht verunglimpfen. Es gab 
einmal einen Thread hier, da wurde ein Problem so über Wochen (glaube 
das ging eine Weile) zerrissen und es kamen quasi täglich neue "Götter 
der C++ Kunst" hinzu und der Thread wurde eher philosophisch (vielleicht 
übertreibe ich ein bisschen, aber deshalb das Popcorn) und war nicht 
mehr zu lesen.

von Wilhelm M. (wimalopaan)


Lesenswert?

Possetitjel schrieb:
> Wilhelm M. schrieb:
>
>> Sicher gibt es keine ideale Sprache, die diesem
>> Problem in Gänze gerecht wird. Allerdings ist für
>> mich C++ nah dran.
>
> Nun ja, ich bin dabei, C zu lernen. Zielpunkt sind
> Mikrocontroller.
>
> Wäre es tatsächlich hilfreich, statt C eine noch
> wesentlich mächtigere und komplexere Sprache zu
> wählen? Ich habe da Zweifel...

Die Sprache C++ ist zwar komplexer und wesentlich mächtiger als C, aber 
nicht notwendigerweise schwerer zu lernen. Das wird leider oft in einen 
Topf geworfen. Ja, C++ ist eine Multiparadigmensprache: imperativ / 
prozedural, objektorientiert, generisch / meta-programmatisch, 
funktional. Doch man muss nicht alles auf einmal benutzen, sondern man 
kann sich je nach Einsatzzweck das Richtige heraussuchen.

Sich auf C als Sprache oder auf den C-Anteil in C++ allein zu 
beschränken, macht m.E. keinen Sinn. In einer general-purpose Umgebung 
wie *nix/Win$$ greift man gerne auf OOP zurück, in einer eingeschränkten 
Umgebung wie bare-metal µC findet man schnell eine Kombination aus 
prozedural und meta-programmatisch hilfreich, ggf. mit einer Prise OOP.

Ich finde die Sichtweise "alles ist ein Integer oder ein hoffentlich 
null-terminiertes char-Array" sehr einengend. In meinen Augen sind 
solche SW-Konstrukte extrem schwer zu lesen, zu warten oder weiter zu 
entwickeln. Hingegen fördern die richtigen Abstraktionen die Lesbarkeit 
bzw. Expressivität und verhindern zur Compile-Zeit viele Fehler, die man 
sonst zu Laufzeit suchen muss. Zudem hilft ein reiches Typsystem dem 
Compiler bei der Optimierungsarbeit.

von (prx) A. K. (prx)


Lesenswert?

Wilhelm M. schrieb:
> Insofern adressiere ich das Problem mit Typen wie

Wobei Possetitjel damit auch den Platzbedarf optimieren wollte. Wie 
macht man das in C++? Also dass der Programmierer nicht doch wieder 
explizit den Grundtyp "uint" in "uint_ranged<23, 57>" angeben muss, egal 
ob wie hier hardcoded, oder als Parameter vom Template. Sondern der 
Compiler sich das aus dem angegeben Bereich selbst ableiten kann. Für 
die frühen C++ Versionen fällt mir da nichts sinnvolles ein.

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

A. K. schrieb:
> Wilhelm M. schrieb:
>> Insofern adressiere ich das Problem mit Typen wie
>
> Wobei Possetitjel damit auch den Platzbedarf optimieren wollte. Wie
> macht man das in C++? Also dass der Programmierer nicht doch wieder
> explizit den Grundtyp "uint" in "uint_ranged<23, 57>" angeben muss, egal
> ob wie hier hardcoded, oder als Parameter vom Template. Sondern der
> Compiler sich das aus dem angegeben Bereich selbst ableiten kann. Für
> die frühen C++ Versionen fällt mir da nichts sinnvolles ein.

Mit einer Meta-Funktion, etwa so:
1
    template<uint64_t V>
2
    struct TypeForValue {
3
        using type = typename std::conditional<(V > std::numeric_limits<uint32_t>::max()), uint64_t, 
4
                              typename std::conditional<(V > std::numeric_limits<uint16_t>::max()), uint32_t,
5
                                typename std::conditional<(V > std::numeric_limits<uint8_t>::max()), uint16_t, uint8_t>::type>::type>::type;
6
    };

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.