Forum: Compiler & IDEs Umrechnung in Kommazahl aber wie ?


von Micha (Gast)


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
1
uint16_t byte01 = buffer[0];
2
         byte01 = buffer[1] << 8;
3
4
//geht nicht
5
sprintf(disp_msg, "Test: %d.%01d", (byte01*7)/4095, ((byte01*7)/4095)%10);
6
7
//geht auch nicht
8
sprintf(disp_msg, "Test: %2.2f", (byte01*7)/4095);

Was mache ich falsch?

Gruß Micha

von Martin (Gast)


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.

von Peter (Gast)


Lesenswert?

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


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

von M. B. (manubaum) Benutzerseite


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;

von M. B. (manubaum) Benutzerseite


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].

von Martin (Gast)


Lesenswert?

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

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

von Karl H. (kbuchegg)


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?

1
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

von M. B. (manubaum) Benutzerseite


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)

von Martin (Gast)


Lesenswert?

@ M. B. hab' nicht aufgepasst :)

von Micha (Gast)


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 :-(
1
uint16_t byte01 = buffer[0];
2
         byte01 |= buffer[1] << 8;
3
4
//geht nicht
5
sprintf(disp_msg, "Test: %2.2f", (byte01*7.2)/40950);

von Micha (Gast)


Lesenswert?

Oops, bei der Berechnung sollte am Ende 4095 stehen und NICHT 40950 !

von Karl H. (kbuchegg)


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

1
uint16_t byte01 = buffer[0];
2
         byte01 |= buffer[1] << 8;
3
4
uint16_t transformed = byte01 * 72 / 4095;
5
6
  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?

von U.R.Schmitt (Gast)


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.

von U.R.Schmitt (Gast)


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.

von Micha (Gast)


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
1
uint16_t byte01 = buffer[0];
2
         byte01 |= buffer[1] << 8;
3
4
uint16_t transformed = byte01 * 72 / 4095;
5
6
  sprintf(disp_msg, "Test: %d.%01d", transformed/10, transformed%10 );

von Εrnst B. (ernst)


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...

von Micha (Gast)


Lesenswert?

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

von U.R.Schmitt (Gast)


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?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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

von Karl H. (kbuchegg)


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.

von U.R.Schmitt (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Natürlich ist das klar.

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

von Horst H. (horha)


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
1
uint16_t transformed = byte01 >>2;  //  /4
2
transformed *= 3;  //  *3/4
3
transformed >= 2; //  *3/16
4
transformed *= 3;  //  *9/16
5
transformed >= 5; //  *9/512
6
7
//Alternativ in einem Schritt
8
uint32_t transformed = (byte01 *72) >>12;  
9
10
//Um modulo zu sparen, falls byte01 jetzt frei ist
11
byte01 = transformed;
12
transformed /= 10;
13
byte01 = byte01-10*transformed;
14
sprintf(disp_msg, "Test: %d.%01d", transformed/10,byte01 );
15
16
17
// oder funktioniert div() aus stdlib.h ??
18
// http://avr-libc.nongnu.org/user-manual/group__avr__stdlib.html
19
div_t transformed;
20
transformed::quot =byte01; // nominator = Zaehler?
21
transformed::rem  =10;        // denominator
22
div(transformed);
23
sprintf(disp_msg, "Test: %d.%01d", transformed::quot, transformed::rem );

von Karl H. (kbuchegg)


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.

von Horst H. (horha)


Lesenswert?

Hallo,

es hat mich gewurmt, das AVRstudio sprintf nicht richtig ausführte, so 
wie sich dies Micha ( und ich auch ) vorgestellt hat.
1
uint16_t byte01 = buffer[0];
2
         byte01 |= buffer[1] << 8;
3
//geht nicht
4
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/using-sprintf--function-for-float-numbers-in-avr-gcc.html 
brachte die Rettung mit den richtigen Einstellungen fuer Kompiler und 
Linker des Projektes.
1
#include <stdio.h>
2
#include <inttypes.h>
3
4
/* entry point */
5
int main(void)
6
{  
7
  volatile uint16_t byte01;  
8
  volatile uint8_t puffer[20];
9
10
  for (byte01= 56;byte01 < 65535;byte01++)
11
    {
12
    sprintf( puffer, "Zählerstand: %5.1f", byte01*7.2/4096);
13
    }
14
    return 0;
15
}
16
----------------
17
18
Kompilerausgabe
19
Device: atmega88pa
20
21
Program:    3738 bytes (45.6% Full)
22
(.text + .data + .bootloader)
23
24
Data:         20 bytes (2.0% Full)
25
(.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.

von Karl H. (kbuchegg)


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#Aktivieren_der_Floating_Point_Version_von_sprintf_beim_WinAVR_mit_AVR-Studio

von Horst H. (horha)


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.

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.