www.mikrocontroller.net

Forum: Compiler & IDEs Umrechnung in Kommazahl aber wie ?


Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

ich stehe vor einem Berechnungsproblem.
Und zwar möchte ich aus zwei 8Byte Variable eine 16Byte Variable und 
muss diese mit einem bestimmten Faktor Multiplizieren und das Ergebnis 
als Kommazahl ausgeben.

Ich habe schon im Forum gesucht nach Möglichkeiten aber die Vorschläge 
funktionieren bei mir nicht.

Hier mein Code den ich bis jetzt habe
uint16_t byte01 = buffer[0];
         byte01 = buffer[1] << 8;

//geht nicht
sprintf(disp_msg, "Test: %d.%01d", (byte01*7)/4095, ((byte01*7)/4095)%10);

//geht auch nicht
sprintf(disp_msg, "Test: %2.2f", (byte01*7)/4095);

Was mache ich falsch?

Gruß Micha

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da es sich um einen ganzzahligen Ausdruck ((byte01*7)/4095)) handelt - 
kommt immer Null heraus. Nimm eine 'float' Variable, die du mit 7 
multiplizierst und dann durch 4095 dividierst.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
uint16_t byte01 = buffer[0];
         byte01 = buffer[1] << 8;


das macht auch wenig seinn, die erste zuweisung wird wieder 
überschrieben.

Autor: M. B. (manubaum) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Micha schrieb:
> uint16_t byte01 = buffer[0];
>          byte01 = buffer[1] << 8;


die zweite Zeile überschreibt den Wert in byte01! Deswegen musst du 
buffer[1] dazu OR'en.

uint16_t byte01 = buffer[0];
         byte01 |= buffer[1] << 8;

Autor: M. B. (manubaum) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Martin schrieb:
> Da es sich um einen ganzzahligen Ausdruck ((byte01*7)/4095)) handelt -
> kommt immer Null heraus. Nimm eine 'float' Variable, die du mit 7
> multiplizierst und dann durch 4095 dividierst.

Falsch. (byte01 * 7 / 4095) ist in [0, 112].

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>> (byte01 * 7 / 4095) ist in [0, 112]

Es gibt nur 2 Lösungen - 0 und 12. Glaube ich nicht!

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Micha schrieb:
> Hallo Leute,
>
> ich stehe vor einem Berechnungsproblem.
> Und zwar möchte ich aus zwei 8Byte Variable eine 16Byte Variable und
> muss diese mit einem bestimmten Faktor Multiplizieren

welches ist dein Faktor?

sprintf(disp_msg, "Test: %d.%01d", (byte01*7)/4095, ((byte01*7)/4095)%10);
das ergibt keinen Sinn.

Wenn dein Faktor 7/4095 ist, dann multiplizier mit 70 und nicht mit 7

Autor: M. B. (manubaum) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wollte sagen zwischen 0 und 112 (falsche klammern)

Martin schrieb:
>>> (byte01 * 7 / 4095) ist in [0, 112]
>
> Es gibt nur 2 Lösungen - 0 und 12. Glaube ich nich

Wollte sagen zwischen 0 und 112 (falsche klammern)

Autor: Martin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ M. B. hab' nicht aufgepasst :)

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

danke erstmal für die vielen Antworten aber irgendwie hilft mir das 
alles nicht weiter.
Ich habe jetzt mal meine 16bit int Variable verODERt aber die Berechnung 
stimmt immer noch nicht.

@Karlheinz: Mein Faktor ist eigentlich 7.2/4095, wollte es jetzt aber 
erstmal mit 7/4095 zum laufen bekommen.

Also so sieht der aktuelle Stand aus aber das Erg. ist immer falsch, 
egal ob ich es wie unten abgebildet mache oder mit %10 
Restwertberechnung :-(
uint16_t byte01 = buffer[0];
         byte01 |= buffer[1] << 8;

//geht nicht
sprintf(disp_msg, "Test: %2.2f", (byte01*7.2)/40950);


Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oops, bei der Berechnung sollte am Ende 4095 stehen und NICHT 40950 !

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Micha schrieb:

>
> @Karlheinz: Mein Faktor ist eigentlich 7.2/4095, wollte es jetzt aber
> erstmal mit 7/4095 zum laufen bekommen.

machs doch gleich richtig mit 7.2

uint16_t byte01 = buffer[0];
         byte01 |= buffer[1] << 8;

uint16_t transformed = byte01 * 72 / 4095;

  sprintf(disp_msg, "Test: %d.%01d", transformed/10, transformed%10 );

Aufpassen musst du nur, dass dir byte01 * 72 keinen Overflow beschert. 
Schlimmstenfalls müsste man auf long ausweichen oder aber man kürzte 
72/4095 durch, so dass die Zahlen kleiner werden aber im Ergebnis noch 
nichts verloren wird.

BTW: die 4095 stimmen? Oder sollten das nicht doch eher 4096 sein?

Autor: U.R.Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> uint16_t byte01 = buffer[0];
>          byte01 |= buffer[1] << 8;
>
> uint16_t transformed = byte01 * 72 / 4095;
>
>   sprintf(disp_msg, "Test: %d.%01d", transformed/10, transformed%10 );

Äh Karl Heinz, dir ist aber klar, daß in der Variable "transformed" 
jetzt ein 10 mal zu großer Wert steht (für den Fall das man 
weiterrechnen möchte).

Wenn ihr einen Faktor 7.2/4095 benutzt könnt ihr euch den Wert in 
Buffer[0] sparen, der wird durch das Teilen durch eine Zahl größer 256 
sowiso komplett verschwinden.

Autor: U.R.Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
U.R.Schmitt schrieb:
> Wenn ihr einen Faktor 7.2/4095 benutzt könnt ihr euch den Wert in
> Buffer[0] sparen, der wird durch das Teilen durch eine Zahl größer 256
> sowiso komplett verschwinden.

Sorry ich hatte das Ursprungsposting nicht gut genug gelesen. Der TE 
will nicht weiterrechnen, sondern nur als Kommazahl ausgeben. Ich habe 
also Blödsinn geschrieben.

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Karlheinz,

das mit der 4095 stimmt schon aber bei der Modulo Berechnung müsste es 
doch dann (transformed/10)%10 heißen, da aus der eigentlichen 7.2 eine 
72 gemacht wurde oder täusche ich mich ?

Gruß Micha
uint16_t byte01 = buffer[0];
         byte01 |= buffer[1] << 8;

uint16_t transformed = byte01 * 72 / 4095;

  sprintf(disp_msg, "Test: %d.%01d", transformed/10, transformed%10 );

Autor: Εrnst B✶ (ernst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Micha schrieb:
> das mit der 4095 stimmt schon

ganz sicher? Woher kommt der Wert?
Divisionen durch 1023, 2047, 4095 usw sind fast immer ein sicheres 
Anzeichen dafür, dass der Programmierer nicht verstanden hat wie ein ADC 
funktioniert.
Oder ohne nachzudenken irgendwo falschen Quelltext abgetippt hat.

Wenn dein Wert natürlich nicht von einem ADC kommt, könnte es eventuell 
natürlich schon sein, dass dieser so seltsam skaliert ist...

Autor: Micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe ein Lastenheft vor mir liegen, indem die Berechnung mit dieser 
Formel gemacht wird... also ich habe keinen Einfluss auf diesen Faktor!

Autor: U.R.Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Micha schrieb:
> das mit der 4095 stimmt schon aber bei der Modulo Berechnung müsste es
> doch dann (transformed/10)%10 heißen, da aus der eigentlichen 7.2 eine
> 72 gemacht wurde oder täusche ich mich ?

Genau darum stimmt es ja. Durch das multiplizieren mit 72 ist der Wert 
in der Variable transformed 10 mal zu groß.
transformed/10 teilt durch 10 und schneidet ab. Das ist der Wert vor dem 
Dezimalpunkt. transformed%10 liefert den rest bei Division durch 10 also 
für den korrekten Wert den Betrag nach dem Dezimalpunkt.
Und mit "Test: %d.%01d" gibst Du beide Werte, getrennt mit einem Punkt 
aus.

Woher kommen die 4095?

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Εrnst B✶ schrieb:
> Divisionen durch 1023, 2047, 4095 usw sind fast immer ein sicheres
> Anzeichen dafür, dass der Programmierer nicht verstanden hat wie ein ADC
> funktioniert.
ACK. Allerdings ohne das "fast immer"...  ;-)

Micha schrieb:
> Ich habe ein Lastenheft vor mir liegen, indem die Berechnung mit dieser
> Formel gemacht wird...
Das würde mir zu denken geben...  :-o

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
U.R.Schmitt schrieb:
> Karl heinz Buchegger schrieb:
>> uint16_t byte01 = buffer[0];
>>          byte01 |= buffer[1] << 8;
>>
>> uint16_t transformed = byte01 * 72 / 4095;
>>
>>   sprintf(disp_msg, "Test: %d.%01d", transformed/10, transformed%10 );
>
> Äh Karl Heinz, dir ist aber klar, daß in der Variable "transformed"
> jetzt ein 10 mal zu großer Wert steht (für den Fall das man
> weiterrechnen möchte).

Natürlich ist das klar.
Das ist ja der Sinn der Sache, damit du 1 "Kommastelle" hast.
Genau so funktioniert Fixkomma Arithmetik.

Wenn du anstelle von in Euro eine Berechnung in Cent machst, dann sind 
alle Centwerte 100 mal so groß wie die zugehörigen Eurowerte.

Autor: U.R.Schmitt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
> Natürlich ist das klar.

Schon gut, ich hatte mich kurz darauf doch schon verbessert :-)

Autor: Horst Hahn (horha)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

nur zum Verstaendnis.
Die Eingangswerte liegen im Bereich 0..63716?
Denn 112.0*4095/7.2 = 63715,5....
Natuerlich kann man durch 4096 teilen/schieben, die Abweichung ist 
erheblich kleiner als die Aufloesung von  1/(112 * 10)
// 1/n = 1/(n+1)+ 1/(n+1)^2+1/(n+1)^3 ...
// x/n = x* 1/n = x*1/(n+1) + (x*  1/(n+1)^2 ~ 0.27 bei maximalem x = 
63716*72 = 4,587552 Mio.

Dann ist der Faktor nicht mehr 72/4095 sondern 72/4096 = 9/512
trotzdem kommt es zu einem Ueberlauf bei uint16_t
Man koennte dann ohne Ueberlauf mehrstufig rechnen
uint16_t transformed = byte01 >>2;  //  /4
transformed *= 3;  //  *3/4
transformed >= 2; //  *3/16
transformed *= 3;  //  *9/16
transformed >= 5; //  *9/512

//Alternativ in einem Schritt
uint32_t transformed = (byte01 *72) >>12;  

//Um modulo zu sparen, falls byte01 jetzt frei ist
byte01 = transformed;
transformed /= 10;
byte01 = byte01-10*transformed;
sprintf(disp_msg, "Test: %d.%01d", transformed/10,byte01 );


// oder funktioniert div() aus stdlib.h ??
// http://avr-libc.nongnu.org/user-manual/group__avr__stdlib.html
div_t transformed;
transformed::quot =byte01; // nominator = Zaehler?
transformed::rem  =10;        // denominator
div(transformed);
sprintf(disp_msg, "Test: %d.%01d", transformed::quot, transformed::rem );


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Horst Hahn schrieb:
> Hallo,
>
> nur zum Verstaendnis.
> Die Eingangswerte liegen im Bereich 0..63716?
> Denn 112.0*4095/7.2 = 63715,5....

Die 112 stammen nicht vom TO

Persönlich glaube ich, dass die Eingangswerte nicht über 4095 liegen 
werden. So nach dem Muster
Wir haben einen 12 Bit ADC und einer Referenzspannung von 7.2V
Wie rechne ich den ADC Wert auf die Spannung zurücke, wenn ich eine 
Nachkommastelle haben will.

Das alles ist jetzt von mir allerdings nur geraten und nur der TO weiß, 
ob das so stimmen kann oder nicht.

Autor: Horst Hahn (horha)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

es hat mich gewurmt, das AVRstudio sprintf nicht richtig ausführte, so 
wie sich dies Micha ( und ich auch ) vorgestellt hat.
uint16_t byte01 = buffer[0];
         byte01 |= buffer[1] << 8;
//geht nicht
sprintf(disp_msg, "Test: %2.2f", (byte01*7.2)/4095);
O.k %2.2f ist wohl eher als  %5.2f gemeint, aber das ist ja nicht das 
wesentliche hier.
http://winavr.scienceprog.com/avr-gcc-tutorial/usi... 
brachte die Rettung mit den richtigen Einstellungen fuer Kompiler und 
Linker des Projektes.
#include <stdio.h>
#include <inttypes.h>

/* entry point */
int main(void)
{  
  volatile uint16_t byte01;  
  volatile uint8_t puffer[20];

  for (byte01= 56;byte01 < 65535;byte01++)
    {
    sprintf( puffer, "Zählerstand: %5.1f", byte01*7.2/4096);
    }
    return 0;
}
----------------

Kompilerausgabe
Device: atmega88pa

Program:    3738 bytes (45.6% Full)
(.text + .data + .bootloader)

Data:         20 bytes (2.0% Full)
(.data + .bss + .noinit)

Bei diesem kurzen Schnipsel geht es ohne volatile nicht, der Kompiler 
weiß nichts von den inneren Zuweisung in sprintf.
Natürlich braucht das enorm viel Programmspeicher, die Variante mit %2d 
braucht etwa die Hälfte.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Horst Hahn schrieb:

> Bei diesem kurzen Schnipsel geht es ohne volatile nicht,

Das wage ich zu bezweifeln. volatile hat (zumindest in deinem Schnipsel) 
nichts damit zu tun.

> der Kompiler
> weiß nichts von den inneren Zuweisung in sprintf.

Was willst du uns damit sagen?


Fakt ist, dass es 2 Varianten der Standard-Library gibt. In der einen 
ist eine abgespeckte Version von sprintf drinnen, die zwar keine 
float/double Unterstützung bietet dafür aber kleiner ist. Die andere ist 
die voll aufgeblasene Version, die zwar alles kann, dafür aber auch mehr 
Speicher benötigt, eben weil dann auch die komplette Floating Point 
Arithmetik notwendig ist und mit eingebunden werden muss.

http://www.mikrocontroller.net/articles/FAQ#Aktivi...

Autor: Horst Hahn (horha)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Jetzt nach dem Neustart, tut es der simulator es auch ohne volatile, 
etwas suspekt das.
Keine Sorge, die volatile habe erst davor gesetzt als im simulator keine 
Aenderung in der Varaibel puffer im Watch-Fenster war.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.