C - Zwei Zahlen zu einer Mappen

Moderator (Firma: Titel) Persönliche Seite #3546294
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?
OP #3546298
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"?
Gast #3546318
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?
Moderator (Firma: Titel) Persönliche Seite #3546321
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...
OP #3546322
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?
Moderator (Firma: Titel) Persönliche Seite #3546328
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);
OP #3546329
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?
Moderator (Firma: Titel) Persönliche Seite #3546330
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...
Gast #3546346
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);
Gast #3546353
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?)
Gast #3546399
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.
Gast #3546421
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.
OP #3546436
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
.
#3546452
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)
Moderator (Firma: Titel) Persönliche Seite #3546460
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.
Gast #3546586
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 ;-)
Gast #3546668
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));
Gast #3546679
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. :-)

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren