C: Werte aus Array mit 3.3 multiplizieren?

Gast #4889516
Hallo,

ich erhalte von einem ADC zwei Bytes mit jeweils dem Wert 0xFF, die in 
einem Array "buffer" gespeichert werden. Den Wert aus dem Array, also 
0xFFFF, möchte ich nun mit 3.3 mutltiplizieren. Aber wie mache ich das?

Dafür habe ich den Wert aus dem Array zunächst in einer extra Variable 
"sp" gespeichert.
1
uint8_t buffer[2];
2
uint16_t sp;
3
  
4
HAL_I2C_Master_Receive(&hi2c1, 0x6B, buffer, 2, 10);
5

6
sp = *buffer;

Wenn ich mir nun die Variablen anschaue:
buffer[0]= 0xFF
buffer[0]= 0xFF
sp= 0x00FF

Wieso hat die Variable sp nun nicht den Wert 0xFFFF???
olaf schrieb:
1
> buffer[0]= 0xFF
2
> buffer[0]= 0xFF
3
> sp= 0x00FF
> Wieso hat die Variable sp nun nicht den Wert 0xFFFF???
Der zweite Index müsste ja 1 sein, du hast zwei mal den Index 0 
verwendet.

Wenn du das Ergebnis deiner Multiplikation in der Variable sp speichern 
willst, muss du den Datentyp anpassen. Ja nachdem wie "genau" dein 
Ergebnis sein muss und in welchem Wertebereich sich dein Buffer bewegt, 
gibt es unterschiedliche Möglichkeiten.
Du könntest ja deinen Wert mit 33 multiplizieren und dann durch 10 
dividieren, wenn du bei den Zwischenergebnissen keinen Überlauf hast 
wäre das eine Einfache Möglichkeit ganz ohne Datentypumwandlung.
Gast
Zupfler schrieb:
> sp = (buffer[0] * 255) + buffer[1]);

Damit erhält man 0xFF00.

Stefan W. schrieb:
> Ja nachdem wie "genau" dein
> Ergebnis sein muss und in welchem Wertebereich sich dein Buffer bewegt,
> gibt es unterschiedliche Möglichkeiten.
> Du könntest ja deinen Wert mit 33 multiplizieren und dann durch 10
> dividieren, wenn du bei den Zwischenergebnissen keinen Überlauf hast
> wäre das eine Einfache Möglichkeit ganz ohne Datentypumwandlung.

Das wäre dann ja meine nächste Aufgabe. Aber als erstes müsste ich mal 
in einer Variablen den Wert 0xFFFF erhalten.

Stefan W. schrieb:
> Wenn du das Ergebnis deiner Multiplikation in der Variable sp speichern
> willst, muss du den Datentyp anpassen.

Welchen Datentyp bräuchte ich? Habe extra uint16_t verwendet, damit ich 
zwei Bytes speichern kann, also genau den Wert 0xFFFF.
Gast
sp = (buffer[0]<<8) | buffer[1];

So würde ich den richtigen Wert 0xFFFF rausbekommen.
Aber mich würde interessieren, wie ich diesen Wert direkt aus dem Array 
in eine neue Variable speicher.
olaf schrieb:
> sp = (buffer[0]<<8) | buffer[1];
>
> So würde ich den richtigen Wert 0xFFFF rausbekommen.

So ist's auch OK, wenn's mit der Endianess vom ADC passt.

> Aber mich würde interessieren, wie ich diesen Wert direkt aus dem Array
> in eine neue Variable speicher.

So:
1
sp = *(uint16_t*)buffer;
Aber das macht sicher jede Menge Aerger, wenn buffer nicht 16bit aligned 
ist oder eben die Endianess nicht passt.

Gruss
WK
Wenn du den Wert 0xffff mit 3,3 multiplizieren willst, wird dir der 
Datentyp uint16 zu klein, da 0xffff schon der maximal darstellbare Wert 
ist. D.h. du musst schon den nächst größeren Datentyp uint32 verwenden.
Mit dem geht auch die einfache Multiplikation mit 3,3 indem du mit 33 
multiplizierst und dann durch 10 dividiert.
1
uint32_t sp = ((buffer[0]<<8) | buffer[1]) * 33;
2
sp = sp / 10;
Dabei verlierst du halt die Nachkommastellen, aber bei den großen Werte 
ist das vermutlich vernachlässigbar.
olaf schrieb:
> ich erhalte von einem ADC zwei Bytes mit jeweils dem Wert 0xFF, die in
> einem Array "buffer" gespeichert werden. Den Wert aus dem Array, also
> 0xFFFF

Wenn das eine 16-Bit Zahl ist, die lediglich für den Transport in zwei 
Bytes zerlegt wird, warum speicherst du die überhaupt erst in einem 
Array? Um auf einzelne Bytes (oder Bits) einer Ganzzahl-Variable 
zuzugreifen, hat C leistungsfähige Operatoren zum Verschieben und 
ausmaskieren (arithmetisches UND). Genauso zum zusammenkleben mit 
Schieben und arithmetischem ODER. Siehe den Artikel Bitmanipulation.

> möchte ich nun mit 3.3 mutltiplizieren. Aber wie mache ich das?

Für eine Multiplikation mit 3.3 wirst du die Zahl im allgemeinen Fall in 
eine Fließkommazahl umwandeln müssen. Eventuell reicht aber auch 
Multiplikation mit 33 und anschließende Division durch 10. Beachte aber 
mögliche Überläufe und Rundungsfehler.

> Dafür habe ich den Wert aus dem Array zunächst in einer extra Variable
> "sp" gespeichert.
1
uint8_t buffer[2];
2
uint16_t sp;
3
 
4
HAL_I2C_Master_Receive(&hi2c1, 0x6B, buffer, 2, 10);
5
 
6
sp = *buffer;

Nein, hast du nicht. *buffer ist in diesem Kontext das gleiche wie 
buffer[0]. Aber anstatt irgendwelchen Dereferenzierungsorgien, womöglich 
noch gespickt mit wilden typecasts solltest du lieber die Grundlagen 
lernen. Portabel und sicher geht das mit Bitmanipulation. Und wenn 
dein Compiler aus diesem Jahrtausend ist, dann verschiebt und verundet 
er für den Zugriff auf einzelne Bytes auch nichts, sondern greift direkt 
auf die entsprechenden Speicherstellen zu. Sofern die Zielplattform 
byteweisen Zugriff überhaupt hat.

Zupfler schrieb:
> Probier mal:
>
> sp = (buffer[0] * 255) + buffer[1]);

Nein. Nicht 255.
Wenn du (ständig) von einem ADC '0xFF' erhälst, dannkannst du den ADC 
gleich weglassen und deine 0xFFFF mit 3.3 multiplizieren ;-(

...

olaf schrieb:
> So würde ich den richtigen Wert 0xFFFF rausbekommen.

Völlig falscher Ansatz!

Ich rate jetzt mal: Das sollte nur ein Beispiel sein? da nimmt man aber 
was Griffiges, z.B. '0x12' und '0x34', dann sieht man nämlich auch, ob 
die Bytes in der für das Zielsystem richtigen Reihenfolge 
zusammengebastelt werden.
olaf schrieb:
> sp = (buffer[0]<<8) | buffer[1];
>
> So würde ich den richtigen Wert 0xFFFF rausbekommen.
> Aber mich würde interessieren, wie ich diesen Wert direkt aus dem Array
> in eine neue Variable speicher.

Genau so.
Vertraue dem Compile. Der erkennt dein Vorhaben und macht daraus (meist) 
das Optimale. Gerade bei solch trivialen Dingen.
Gast
Dirk B. schrieb:

> Vertraue dem Compile. Der erkennt dein Vorhaben und macht daraus (meist)
> das Optimale. Gerade bei solch trivialen Dingen.

Nein. Der Compiler kann ja schliesslich nicht wissen, mit welcher 
"Endianess" die Bytes im Buffer liegen, da er keinerlei Information über 
die Quelle der Daten besitzt. Das kann ihm also nur der Programmierer 
sagen.

Und wer der Programmierer kompetent genug ist, ihm das zu sagen, ist er 
auch kompetent genug, dem Compiler den optimalen Weg zur Zusammenführung 
der Daten zu sagen. Nur dann weiss man nämlich sicher, dass der Compiler 
auch tatsächlich den optimalen Weg benutzen wird.

Sprich: eigentlich braucht man keinen Compiler, wenn man diesem doch 
alles haarklein erklären muss, um zuverlässig ein richtiges und 
obendrein optimales Ergebnis zu erzielen...
c-hater schrieb:
> Nein. Der Compiler kann ja schliesslich nicht wissen, mit welcher
> "Endianess" die Bytes im Buffer liegen, da er keinerlei Information über
> die Quelle der Daten besitzt. Das kann ihm also nur der Programmierer
> sagen.

Genau das sagt der Programmierer doch mit der Zeile.
Der Compiler macht dasraus dann optimalen Maschinencode.

Der muss da nicht schieben, sondern greift gleich Byteweise darauf zu.
Oder liest 16-Bit ein und swapt die Bytes, falls es erforderlich ist.

Bei union oder cast von Pointern, bist du abhängig von der Endianess.
Gast
Dirk B. schrieb:

> Der Compiler macht dasraus dann optimalen Maschinencode.

Nur in deinen Träumen. Tatsächlich aber ist es ein Glücksspiel. Es hängt 
nicht nur vom Compiler selber und den gewählten Optimierungsoptionen ab, 
sondern auch vom konkreten Target.

Letzte Sicherheit bietet erst die Lektüre und Analyse des erzeugten 
Maschinencodes. Wenn ich aber diesen Aufwand treiben muss, um sicher 
sein zu können, dass die Sache optimal umgesetzt wird, dann schreibe ich 
sie doch lieber gleich selber optimiert hin. Und zwar in einer Sprache, 
die sich dafür eignet, deterministische Ergebnisse zu produzieren und 
somit kein Glücksspiel ist, bei dem (bestenfalls) die formale 
Korrektheit garantiert ist, aber niemals die optimale Effizienz.
Hi

Jetzt haben wir gelernt, daß der TO doch erst Mal anzugeben hat, welchen 
Compiler Er wohl benutzt, um daraus erkennen zu können, ob Dieser 
überhaupt mit dieser Aufgabe 'optimalen' Code erstellen kann.

Häh?

Der TO hat das Problem, daß bei Seiner Rechnung nicht das Ergebnis raus 
kommt, was Er erwartet hat.

OB die $FF korrekt zu einem Word zusammen gebäbbt werden, ist auch noch 
nicht klar - dafür erprobt der TO gerade die obere Grenze der Messwerte 
- was ja auch Mal passieren muß.

Was sich der TO ggf. von Euch gewünscht hätte, ist sehr wahrscheinlich, 
wie Er diese besch***grmpfl Berechnung hinzuschreiben hat, daß durch 
'$FFFF x 3.3' hinten '1B331,8' heraus kommt.

Vll. mit dem Hinweis, daß die 16-Bit-Variabel mit $FFFF bereits 
vollständig gefüllt ist und hier nicht Mehr möglich ist (eben Diese zu 
$0000 überläuft, wenn man nur 1 dazu addiert).
Er also mehr Bits braucht - denke, 32 wird die nächste Grenze sein.

Dann sollen Fließkommazahlen recht viel Rechenzeit verbraten - eine 
Integer-Multiplikation mit 33 und eine Integerdivision mit 10 erschlagen 
die Berechnung (ohne Komma) binen eines Bruchteil der Rechenzeit und des 
benötigten Speicherplatz.
Ausnahme: Float wird eh benutzt, dann wird der Compiler wohl so schlau 
sein, diese Routine hierfür erneut zu nutzen und nicht 2x einzubinden.

MfG

Antwort schreiben

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

Neuen Thread starten

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

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

Mehr zur Formatierung

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

Schon registriert? Anmelden

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

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