Forum: Mikrocontroller und Digitale Elektronik C - Zwei Zahlen zu einer Mappen


von Martin F. (martin_f70)


Lesenswert?

Hallo, ich habe zwei Zahlen:
1
uint8_t z1; uint16_t z2;

Wie kann ich diese Zwei in eine machen die dann so aussieht:

z1,z2, also dass z1 vorkommastellen sind und z2 Nachkommastellen?

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


Lesenswert?

Martin Fischer schrieb:
> Wie kann ich diese Zwei in eine machen die dann so aussieht:
> z1,z2, also dass z1 vorkommastellen sind und z2 Nachkommastellen?
Wie sieht das Format dieser "Nachkommastellen" denn aus?

Wenn z.B. die "Nachkommastellen" im Bereich von 0..9999 wären, dann 
würde das so gehen:
1
  float f = z1 + float(z2)/10000.0;

> Wie kann ich diese Zwei in eine machen die dann so aussieht:
Was ich gerne wissen würde: wofür brauchst du diese Zahl?

: Bearbeitet durch Moderator
von TriHexagon (Gast)


Lesenswert?

Festkommaarithmetik

von Martin F. (martin_f70)


Lesenswert?

Danke, berecnet habe ich geschafft, aber auf einem anderen Weg, ich 
demonstriere kurz was ich brauche:

Temperaturmessung:

uint8_t digit = 32;
uint16_t decimal = 4493;

-> Temperatur ist 32.4493 °C

Ich mag das jetzt in eine Zahl packen:

uint32_t temp = (digit *10000 + decimal)/10000

Und das dann auf dem Display ausgeben -> erst zu nem char array machen:

char buffer[20];

sprintf(buffer, "%d C", temp);

-> Fehler: temp != decimal...
Wie mache ich das?

Und ist uint32_t überhaupt richtig um die zu "addieren"?

: Bearbeitet durch User
von Jan H. (jhnet)


Lesenswert?

Wenn du nach deiner Aktion eine Kommazahl haben möchtest, dann solltest 
du keinen int-Datentyp wählen !
1
uint32_t temp = (digit *10000 + decimal)/10000
diese Zeile macht nicht viel Sinn :)

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


Lesenswert?

1
sprintf(buffer, "%3d.%04d °C", digit, decimal);

von Harald N. (haraldn)


Lesenswert?

Wenns am Ende sowieso ein String sein soll, kann man den doch aus den 
int's basteln, oder liege ich da falsch?

von Bastler (Gast)


Lesenswert?

Überleg mal was mit den decimals passiert, wenn du sie durch 10000 
teilst. Wo stehen die dann in temp?
Wenns nur um die Ausgabe geht:
1
sprintf( buffer, "%d.%04d C", digit, decimal )
Nebenbei: Temperatur mit 6 signifikanten Stellen?

von Bastler (Gast)


Lesenswert?

Verdammt! Zu lange getippt! ;-)

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


Lesenswert?

Martin Fischer schrieb:
> Ich mag das jetzt in eine Zahl packen:
> uint32_t temp = (digit *10000 + decimal)/10000
Wozu dann durch zehntausend teilen? Ein Integer kann keine 
Nachkommastellen speichern. Warum speicherst du nicht einfach milliGrad 
in einem Integer?

> Ich mag das jetzt in eine Zahl packen:
> Und das dann auf dem Display ausgeben -
Warum erst zusammenpacken und dann wieder auseinanderdröseln? Gib doch 
erst die Vorkommastellen aus, dann ein Komma, dann die Nachkommastellen. 
Das ist das Einfachste...

> Temperaturmessung:
> uint8_t digit = 32;
> uint16_t decimal = 4493;
> -> Temperatur ist 32.4493 °C
Ähm, wenn GENAU das deine Anwendung ist, dann bezweifle ich sehr, dass 
du überhaupt so genau Messen kannst...

: Bearbeitet durch Moderator
von Martin F. (martin_f70)


Lesenswert?

Ich habe diese Funktion:
1
void therm_read_temperature(uint8_t digit, uint16_t decimal){
2
    uint8_t temperature[2];
3
        
4
    therm_reset();
5
    therm_write_byte(THERM_CMD_SKIPROM);
6
    therm_write_byte(THERM_CMD_CONVERTTEMP);
7
    
8
    while(!therm_read_bit());
9
    
10
    therm_reset();
11
    therm_write_byte(THERM_CMD_SKIPROM);
12
    therm_write_byte(THERM_CMD_RSCRATCHPAD);
13
    
14
    temperature[0]=therm_read_byte();
15
    temperature[1]=therm_read_byte();
16
    therm_reset();
17
    
18
    digit=temperature[0]>>4;
19
    digit|=(temperature[1]&0x7)<<4;
20
    
21
    decimal=temperature[0]&0xf;
22
    decimal*=THERM_DECIMAL_STEPS_12BIT;
23
}

Und mag hiermit das auf dem Display ausgeben:
1
char buffertemp[20];
2
uint8_t grad;
3
uint16_t grad_dec;
4
5
        therm_read_temperature(grad, grad_dec);
6
        sprintf(buffertemp, "%d.%04u C", grad, grad_dec);
7
        lcd_string(buffertemp);

Auf dem Display steht aber nur "0.0000 C", warum?

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


Lesenswert?

Martin Fischer schrieb:
> Auf dem Display steht aber nur "0.0000 C", warum?
Weil in grad und grad_dec jeweils Null ist?

> therm_read_temperature(grad, grad_dec);
Diese Funktion KANN die Werte von grad und grad_dec nicht ändern. Du 
solltest dir dringend anschauen, wie Funktionen mehrere Werte bearbeiten 
können. Mein Tipp: man verwendet dafür dann Pointer...

Probiers also einfach mal so:
1
void therm_read_temperature(uint8_t *digit, uint16_t *decimal){
2
    uint8_t temperature[2];
3
        
4
    therm_reset();
5
    therm_write_byte(THERM_CMD_SKIPROM);
6
    therm_write_byte(THERM_CMD_CONVERTTEMP);
7
    
8
    while(!therm_read_bit());
9
    
10
    therm_reset();
11
    therm_write_byte(THERM_CMD_SKIPROM);
12
    therm_write_byte(THERM_CMD_RSCRATCHPAD);
13
    
14
    temperature[0]=therm_read_byte();
15
    temperature[1]=therm_read_byte();
16
    therm_reset();
17
    
18
    *digit=temperature[0]>>4;
19
    *digit|=(temperature[1]&0x7)<<4;
20
    
21
    *decimal=temperature[0]&0xf;
22
    *decimal*=THERM_DECIMAL_STEPS_12BIT;
23
}
24
25
:
26
:
27
28
char buffertemp[20];
29
uint8_t grad;
30
uint16_t grad_dec;
31
32
        therm_read_temperature(&grad, &grad_dec);
33
        sprintf(buffertemp, "%d.%04u C", grad, grad_dec);
34
        lcd_string(buffertemp);

: Bearbeitet durch Moderator
von Martin F. (martin_f70)


Lesenswert?

> Warum erst zusammenpacken und dann wieder auseinanderdröseln? Gib doch
> erst die Vorkommastellen aus, dann ein Komma, dann die Nachkommastellen.
> Das ist das Einfachste...

Aber ich brauche die Zahl dann noch zum weiteren Rechnen, also wie kann 
ich meine Temperatur zusätzlich noch in eine zahl packen um damit zu 
rechnen?

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


Lesenswert?

Martin Fischer schrieb:
> Aber ich brauche die Zahl dann noch zum weiteren Rechnen, also wie kann
> ich meine Temperatur zusätzlich noch in eine zahl packen um damit zu
> rechnen?
Wenn du unbedingt mit Integern weiterrechnen willst, dann rechne in 
milliGrad (aka. Festpunktartihmetik). Am einfachsten ist aber, du 
verwendest float...

von Martin F. (martin_f70)


Lesenswert?

> float

Ich brauche aber die 4 Nachkommastellen..

von ohne Name (Gast)


Lesenswert?

Martin Fischer schrieb:
> uint32_t temp = (digit *10000 + decimal)/10000

  digit * 10000 <=>   32 * 10000 = 320000
+ decimal       <=> + 4493       = 324493
/ 10000         <=> / 10000      = 32

Dein Problem ist das du nur mit Ganzzahlen(Integer) arbeitest, wenn du 
aber 324493 / 10000 erhältst du allerdings eine Zahl mit 
Nachkommastellen(Fließkommazahl). Weil temp und ALLE anderen Variablen 
vom Typ Integer sind werden die Nachkommastellen abgeschnitten.

Bei entsprechender Compiler-Optimierung wird die komplette Rechnung 
wahrscheinlich sogar entfernt werden.

Die Frage ist nun ob du mit der kompletten Zahl noch rechnen möchtest 
oder ob du sie nur zur Darstellung/Ausgabe benötigst.

Musst du noch damit rechnen, kannst du

a) Festkommaarithmetik verwenden, dann müssen alle weiteren Operanden 
noch mit 10000 multipliziert werden. Erst am Ende wenn es zur 
Darstellung kommt wandelst du die Zahl in eine Fließkommazahl.

b) Du rechnest komplett mit Fließkommazahlen so wie Lothar es dir weiter 
oben gezeigt hat.

Wenn du die Zahl nur noch darstellen möchtest und sie unbedingt in einer 
Variablen speichern möchtest kann die Zahl auch anders zusammen gepackt 
werden.

uint32 temp = (digit << 16) + decimal;
char buffer[20];
sprintf(buffer, "%d.%d C", temp >> 16, temp & 0xFF);

von Rolf Magnus (Gast)


Lesenswert?

Martin Fischer schrieb:
>> float
>
> Ich brauche aber die 4 Nachkommastellen..

Was für ein Monster-Thermometer hast du denn da, daß du dafür eine 
Auflösung von 1/10000 Kelvin brauchst, und wieviele Millionen kostet 
sowas?

von Dirk B. (dirkb2)


Lesenswert?

Martin Fischer schrieb:
>> float
>
> Ich brauche aber die 4 Nachkommastellen..
Bei Temperatur?

Welcher Messbereich schwebt dir denn vor?

von Bastler (Gast)


Lesenswert?

Wenn du Temperaturen auf 6 signifikante Stellen messen kannst, dann hast 
du auch noch Geld übrig, eine C-Programmierer zu bezahlen, der dir deine 
SopW-Probleme löst. (warum glaubt man heute eigentlich blind an jede 
Zahl, die per 7-Segment angezeigt wird?)

von Martin F. (martin_f70)


Lesenswert?

Es ist ein DS18B20 auf 12bit Auflösung...

von Bastler (Gast)


Lesenswert?

Und +-0.5C Genauigkeit!

von Dirk B. (dirkb2)


Lesenswert?

Martin Fischer schrieb:
> Es ist ein DS18B20 auf 12bit Auflösung...

LOL

Aus dem Datenblatt:
It has an operating temperature
range of -55°C to +125°C and is accurate to
±0.5°C over the range of -10°C to +85°C. In

Also reicht eine Nachkommastelle.

von Martin F. (martin_f70)


Lesenswert?

Jaja. Ich habe jetzt aber noch ein Problem:
1
float temp;
2
temp = grad + float(grad_dec)/10000.0;
3
4
sprintf(buffer, "%f C", temp);

Beim Compilieren:
main.c:28:9: warning: format ‘%f’ expects argument of type ‘double’, but 
argument 3 has type ‘float’ [-Wformat]

von Bastler (Gast)


Lesenswert?

Statt temp einfach (double)temp übergeben.

von Dirk B. (dirkb2)


Lesenswert?

Martin Fischer schrieb:
> Jaja.
heißt ""Klei mi ann Mors"

Bei Funktionen mit variabler Argumentenliste wird float immer auf double 
erweitert.

von Martin F. (martin_f70)


Lesenswert?

Error:
prop -fno-inline-small-functions -I. -mmcu=atmega328p -DF_CPU=8000000
main.c: In function ‘main’:
main.c:27:23: error: expected expression before ‘double’
make: *** [main.o] Fehler 1

Was bedeutet das denn jetzt?
Ich habe alle floats einfach mit double ersetzt

von Dirk B. (dirkb2)


Lesenswert?

Anscheinend aber nicht richtig.

Wie sollen wir das ohne Code beurteilen.

Der Fehler ist aber vor der Spalte 23 in Zeile 27.

von Bitflüsterer (Gast)


Lesenswert?

>Ich habe alle floats einfach mit double ersetzt

Ich vermute eher, das Du alle Strings "float" im Programmtext durch den 
String "double" ersetzt hast.

>main.c:27:23: error: expected expression before ‘double’
Gut, das wir die Zeile 27 nicht sehen können. Da wird das raten viel 
spannender.

von Martin F. (martin_f70)


Lesenswert?

Zeile 27:
temp = grad + double(grad_dec)/10000.0;

Und temp-definition: double temp;

von Bitflüsterer (Gast)


Lesenswert?

1
temp = grad + double(grad_dec)/10000.0;

Da hat sich Lothar verhauen. Zuviel VHDL nehme ich a. :-)
1
temp = grad + ((double) grad_dec)/10000.0;

von Dirk B. (dirkb2)


Lesenswert?

Martin Fischer schrieb:
> Zeile 27:
> temp = grad + double(grad_dec)/10000.0;

Da gehört ein Cast hin. Den schreibt man in ().
Aber der ist auch gar nicht nötig, da 10000.0 schon ein double ist und 
somit die Division schon in double berechnet wird.

von Rolf Magnus (Gast)


Lesenswert?

Martin Fischer schrieb:
> double(grad_dec)

Das ist nicht C. Sowas geht in C++, aber nicht in C. In C sieht ein 
Cast so aus:
1
(double)grad_dec

Martin Fischer schrieb:
> main.c:28:9: warning: format ‘%f’ expects argument of type ‘double’, but
> argument 3 has type ‘float’ [-Wformat]

Das ist merkwürdig. Normwalerweise wird das implizit nach double 
konvertiert, ohne Warnung.

von Martin F. (martin_f70)


Lesenswert?

Nanu???

        temp = grad + ((double) grad_dec)/10000;
        sprintf(buffertemp, "%f C", temp);

-> auf Display: "? C"

von Bitflüsterer (Gast)


Lesenswert?

Sieht aus, als wenn Du eine besondere Library benutzt.
Poste mal alle Kommandozeilen.

von Martin F. (martin_f70)


Lesenswert?

1. Ohne konvertierung zu Double hatte es ja auch geklappt.
2. Was meinst du mit Kommandozeilen?

avr-gcc main.c -c -o main.o -Wall -Os -fno-move-loop-invariants 
-fno-tree-scev-cprop -fno-inline-small-functions -I. -mmcu=atmega328p 
-DF_CPU=8000000
avr-gcc lib/lcd.c -c -o lcd.o -std=c99 -Wall -Os 
-fno-move-loop-invariants -fno-tree-scev-cprop 
-fno-inline-small-functions -I. -mmcu=atmega328p -DF_CPU=8000000
avr-gcc lib/simpleprog.c -c -o simpleprog.o -std=c99 -Wall -Os 
-fno-move-loop-invariants -fno-tree-scev-cprop 
-fno-inline-small-functions -I. -mmcu=atmega328p -DF_CPU=8000000
avr-gcc lib/ds18b20.c -c -o ds18b20.o -std=c99 -Wall -Os 
-fno-move-loop-invariants -fno-tree-scev-cprop 
-fno-inline-small-functions -I. -mmcu=atmega328p -DF_CPU=8000000
avr-gcc main.o lcd.o simpleprog.o ds18b20.o -o main.elf -Wall -Os 
-fno-move-loop-invariants -fno-tree-scev-cprop 
-fno-inline-small-functions -I. -mmcu=atmega328p -DF_CPU=8000000 
-Wl,-Map,main.map -Wl,--relax,--gc-sections
.
   text     data      bss      dec      hex  filename
   4778       46        0     4824     12d8  main.elf
.

: Bearbeitet durch User
von Rolf Magnus (Gast)


Lesenswert?

Dein Problem ist, daß du die Doku der avr-libc nicht gelesen hast. Da 
floating-point sehr viel Flash braucht, unterstützt das per default 
gelinkte printf keine Gleitkommazahlen.

von Frank M. (frank_m35)


Lesenswert?

Bitte durchdenke alles nochmal genau:

Brauchst du wirklich Dezimalzahlen? Dein Thermometer hat eine 
Genauigkeit von 0.5°C. D.h., wie schon gesagt wurde, du brauchst nur 
eine Dezimalstelle. Alle weiteren haben keine Aussagekraft!

Nun könntest du auch alles mit Integer rechnen, indem du das Ergebnis 
mit 10 multiplizierst um die eine benötigte Dezimalstelle weg zu 
bekommen. Das ist schneller und verbraucht weniger Speicher.
Zur Ausgabe auf dem Display kannst du dann entweder die Zahl mit 
Division durch 10 in ein Double speichern und wie gehabt ein Double mit 
sprintf am LCD ausgeben. Das dauert aber sehr lange, da die Umwandlung 
eines Double in einen String sehr Rechenintensiv ist.

Wieder besser ist, du machst das 'manuell'. D.h. du teilst die Zahl 
durch 10, speicherst das Ergebnis in ein Int. Die Zahl mit 10 
multipliziert und von der Ausgangszahl subtrahiert gibt dir die 
Dezimalstelle. Beides sind Int, beide können per sprintf mit minimalen 
Rechenaufwand ausgegeben werden.

(Falls ich bei obiger Rechnung einen Denkfehler habe, so korrigiert mich 
bitte)

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


Lesenswert?

Bitflüsterer schrieb:
> temp = grad + double(grad_dec)/10000.0;
> Da hat sich Lothar verhauen. Zuviel VHDL nehme ich a. :-)
Ach verd*%&/$§

Ich könnte aber auch behaupten, ich hätte das gemacht, dass Martin mal 
ein wenig mitdenkt und keiner könnte das Gegenteil beweisen... ;-)

Frank M. schrieb:
> Nun könntest du auch alles mit Integer rechnen, indem du das Ergebnis
> mit 10 multiplizierst
Ja, dann eben in ZehntelGrad statt in milliGrad...

Martin Fischer schrieb:
> Ich brauche aber die 4 Nachkommastellen...
Bitte die nächste Scheibe der Salami: Wofür brauchst du die real nicht 
vorhandenen 4 Nachkommastellen.
Nochmal zum Mitschreiben: wenn dein Sensor eh' nur eine halbe 
signifikante Nachkommastelle ausgibt, dann ist es absolut sinnlos, wenn 
du dir eine 5000 fach höhere Auflösung herrechnest. Die Genauigkeit 
bleibt trotzdem gleich schlecht.

: Bearbeitet durch Moderator
von Martin F. (martin_f70)


Lesenswert?

Ja gut, da hast du recht :D
Aber ich bin ANfänger und weiß sowas noch nicht :P
naja, ich "Spiele" einfach jetzt mit allen werten *10, ist leichter, 
schneller, besser :D

Danke trotzdem an euch allen!

von TriHexagon (Gast)


Lesenswert?

Nichts für ungut aber:

Martin Fischer schrieb:
> Aber ich bin ANfänger und weiß sowas noch nicht :P

Casten und Fehlermeldung lesen gehört sowas von zu den Grundlagen, 
deshalb lerne sie und komme mit ihnen zurecht oder hör auf. Du kannst 
nicht immer wegen jedem kleinen Scheiß das Forum zumüllen.

von Bastler (Gast)


Lesenswert?

Ganz so schlimm würde ich das nicht sehen. Nur, oft wenn (von Anfänger 
und Anderstgläubigen) auf C geschimpft wird, hat der Compiler alles 
wichtige angemerkt, nur hatte man vorgezogen es zu ignorieren. Also 
bitte lesen. Wenn's dann allzu verwirrend wirkt, hilf sicher jemand ;-)

von martin (Gast)


Lesenswert?

Frank M. schrieb:
> ...
> Wieder besser ist, du machst das 'manuell'. D.h. du teilst die Zahl
> durch 10, speicherst das Ergebnis in ein Int. Die Zahl mit 10
> multipliziert und von der Ausgangszahl subtrahiert gibt dir die
> Dezimalstelle. Beides sind Int, beide können per sprintf mit minimalen
> Rechenaufwand ausgegeben werden.
>

So:
1
#include <stdlib.h>
2
3
int temperature = ...
4
div_t a = div(temperature, 10);
5
printf("%d.%d", a.quot, abs(a.rem));

von Bitflüsterer (Gast)


Lesenswert?

@ Lothar Miller

>Ich könnte aber auch behaupten, ich hätte das gemacht, dass Martin mal
>ein wenig mitdenkt und keiner könnte das Gegenteil beweisen... ;-)

Dazu nehme ich 'ne Knoblauchzehe. Wirst schon sehen. :-)

@ Martin

Was soll Dein letztes Posting sein? Eine Frage oder eine Feststellung?
Nebenbei: Ist das nun C oder C++? Leg Dich mal fest. :-)

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.