Hallo liebe Gemeinde,
mit einem ATMega16 möchte ich Ziffern einer uint32_t Variable auf einer
6 Stelligen 7Segmentanzeige ausgeben. Die entsprechenden Ziffern picke
ich mir mit einer geschriebenen Modulo-Funktion heraus. Folgendes
Problem beschäftigt mich derzeit:
Ich berechne eine momentane Verbrauchsleistung mit Divison und
Multiplikation, sodass mir wie zu erwarten die Nachkommastellen des
Ergebnises verloren gehen. Ich benötige jedoch 3 der Nachkommastellen
für weitere Funktionen in der selben Variable. Nun würde ich gerne meine
unsigned long Variable so gestalten, dass die ersten drei Bits den
Nachkommastellen entsprechen und die weiteren Bits eben der linken Seite
der Fließkommazahl entsprechen. Da der Wertebereich von einer double
bzw. float Variable meinem Ergebnis nicht genügt, fehlt mir der Ansatz
um dieses Problem zu lösen.
Ich bin relativ neu in der Materie und habe etwas über Typecast gelesen,
welches entsprechende Variablen als bestimmten Variablentyp behandelt.
Könnte mir diese Möglichkeit die Nachkommastellen in meine uint32_t
Variable einbinden oder bewege ich mich mit dieser Lösungsmögichkeit auf
dem Holzweg?
Für effektivere bzw. sinnvollere Lösungsansätze / -vorschläge würde ich
mich sehr freuen.
Danke im Voraus.
Mr. Xton schrieb:> Da der Wertebereich von einer double bzw. float Variable meinem Ergebnis> nicht genügt, fehlt mir der Ansatz um dieses Problem zu lösen.
Das mußt du jetzt mal näher erklären. Der Wertebereich von float ist
RIESIG und um da rauszukommen, muss man schon einiges anstellen.
Was spricht dagegen, deine Zahlen als Milli-Irgendwas in der Integer
Variablen abzulegen. Dann hast du die Tausendstel mit drin.
1. Typecast==Holzweg
2. Auszugebender Wertebereich ist unklar.
6 Stellen wäre 0..999999, es sei denn, das Komma ist verschiebbar.
Welchen Wertebereich willst Du ausgeben (z.B. 1mW..1000W) ?
3. Zweiter Absatz ist völlig unklar (3 Nachkommastellen ./. 3 Bits?!?)
4. "Da der Wertebereich von einer double bzw. float Variable
meinem Ergebnis nicht genügt," - Glaube ich erstmal nicht,
meinst Du was anderes?
Danke euch beiden für die schnelle Antwort.
Hier ein Teilcode fürs Verständnis:
uint32_t wert = 0;
wert = 1 / IMPULSE_PRO_KWH; // Umrechnung der Impulse in kWH
wert = wert * 1000; // Umrechnung kWH in WH
wert = wert / 3600; // Umrechnung WH in Ws
wert = wert / 100; // Umrechnung Ws in Wms
wert = wert / Gemessene_Zeit_ms; // Umrechnung mit verstrichener
Zeit(ms)
wert = wert * 100; // Rückrechnung Wms in Ws
Da ich die zeit wegen der Genauigkeit in ms ermittel, bin ich gezwungen
kurzzeitig die Leistung in W/ms umzurechnen. Bereits in der 1. Rechnung
gehen mir die Nachkommastellen verloren, sodass das Ergibnis an der
Stelle Null ist. Würde ich hier mit float rechnen, bringt dieses jedoch
den Nachteil mit sich, das mir die Vorkommastellen verloren gehen (wenn
auch nich in der 1. Rechnung).
Also ist mein Ansatz, das ich die 1. Zeile in eine floatvariable
schieben möchte.
Sprich:
float kwh_impuls = 1 / IMPULSE_PRO_KWH;
Jetzt benötige ich aber eine Rechnung aus uint32_t und float:
wert = (unsinged long) kwh_impuls * 1000;
...
Leider ist die Hardware noch nicht einsatzbereit und ich weiß nicht was
die Variable wert dann beinhalten würde, da ich noch nie mit typecast
gearbeitet habe.
lg
Rechne einfach mit Float. Da gehen keine Vorkommastellen verloren.
Und stelle mal eine Gesamtformel auf. Du wirst sehen, das sich einige
Probleme durch kürzen ohnehin auflösen.
Mr. Xton schrieb:> Würde ich hier mit float rechnen, bringt dieses jedoch> den Nachteil mit sich, das mir die Vorkommastellen verloren gehen
Kannst du diese Aussage bitte etwas erläutern?
Jetzt kann ein Wert zwischen 0 und 10 Millionen rauskommen. Da musst Du
nur noch gedanklich bzw. bei der Ausgabe ein Komma an die richtige
Stelle reinsetzen und Du bist fertig.
Mit float gehts natürlich auch, ist aber auf einem Mikrocontroller viel
langsamer und hier unnötig.
Ergänzung: Der Wertebereich ist natürlich kleiner. Der Maximalwert ist
10 Millionen durch (36 IMPULSE_PRO_KWH 1). Wenn das nicht reicht,
hängst Du halt noch mehr Nullen an und gehst auf uint64_t.
Danke euch!
@Hmm:
die Zeilen sind ausführlich aufgeführt um das Verständnis zu
veranschaulichen. Werde es mir in einer Formel zu gemüte führen.
Zum Thema Vorkommastelle: Der Wertebereich von float kann Daten bergen,
die sowohl im negativen, als auch im positiven Bereich sein können. Und
im Vergleich zum unsigned long Typ kann dieser im positiven Bereich eine
kleinere Zahl ablegen. Da weder die Zeit noch die Leistungsgröße pro
Impuls bekannt ist, kann auch keine feste Aussage über die Größe der
Momentanleistung getroffen werden. Sie soll im Endeffekt vielseitig
einsetzbar sein.
> Werde es mir in einer Formel zu gemüte führen.
Mache es jetzt . Nicht später.
>Da weder die Zeit noch die Leistungsgröße pro>Impuls bekannt ist, kann auch keine feste Aussage über die Größe der>Momentanleistung getroffen werden.
Genau darüber aber musst Du Dir klar werden und Aussagen machen. Erst
dann kannst Du Entwurfsentscheidungen treffen.
Vielen Dank xfr.
Die Formel (Äquivalent:) habe ich vorliegen und ist schlüssig.
xfr schrieb:> wert = 10UL 1000 1000 / (36 IMPULSE_PRO_KWH Gemessene_Zeit_ms);
Kannst du mir erklären was das 10UL bedeutet und wie es sich auswirkt?
lg
Hmm schrieb:> Vor allem reden wir hier über 10 hoch 14 Yottawatt. Hast Du die ganze> Galaxie an Deinem Stromzähler? :-)
Das ist eben das Problem, ich kann zum jetzigen Zeitpunkt wirklich keine
Aussage darüber treffen wieviel MW momentan verbraucht werden ;)
Spaß bei Seite, es handelt sich um ein Projekt für einen Abnehmer XY,
und ich wollte es eben diesen so recht wie möglich machen, indem ich die
Wertebereiche ziemlich groß gestalte. Man befindet sich einfach auf der
sicheren Seite. Änderungen kann man im Praxiseinsatz immernoch bei
Bedarf vornehmen. Und steht der Code einmal kann er auch bei anderen
Abnehmern eingesetzt werden.
Vielen Dank nochmal, für eure Bemühungen.
lg
Standardmäßig sind Konstanten Integer-Werte, die auf dem AVR nur 16 Bit
breit sind. Wenn man jetzt rechnet:
1
10*1000*1000
Dann passt das Ergebnis nicht in einen 16-Bit-Integer und es kommen
seltsame Werte bei raus.
Also muss man mit 32 Bit rechnen. Das UL steht für unsigned long:
1
10UL*1000*1000
Ebenso geht:
1
(uint32_t)10*1000*1000
Damit stimmt dann der Wert wie erwartet. Für 64 Bit muss man ULL bzw.
uint64_t nehmen.
Außerdem sollte man möglichst zuerst alle Konstanten zuerst miteinander
verrrechnen, dann macht der Compiler das schon beim Optimieren. Im
Maschinencode ist dann im Endeffekt nur noch eine 32-Bit-Division:
OK. Megawatt. Das wären dann 10 Hoch 32 Megawattsekunden. Der ganze
Weltenergiebedarf betrug 2010 etwa 140.168 Mrd.kWh. Also so über den
Daumen das 1/10^24 stel des Zahlenbereichs von (üblichen) Floats.
Dein Kunde muss seeeeeeeeeeeeeeeeeeeeeeeeeeeehr gross sein.
Ich möchte nochmal betonen wie wichtig es ist, sich vor einer
Implementierung über die Grössenordnungen klar zu werden. An sich gehört
sowas in das Lastenheft. Es dem Kunden so recht wie möglich zu machen
ist ohnehin vergebliche Liebesmüh, da endlos.
xfr schrieb:> Standardmäßig sind Konstanten Integer-Werte, die auf dem AVR nur 16 Bit> breit sind. Wenn man jetzt rechnet:>
1
10*1000*1000
> Dann passt das Ergebnis nicht in einen 16-Bit-Integer und es kommen> seltsame Werte bei raus.>> Also muss man mit 32 Bit rechnen. Das UL steht für unsigned long:>
1
10UL*1000*1000
> Ebenso geht:>
1
(uint32_t)10*1000*1000
>> Damit stimmt dann der Wert wie erwartet. Für 64 Bit muss man ULL bzw.> uint64_t nehmen.
Super, vielen Dank für die ausführliche Erklärung!
Hmm schrieb:> OK. Megawatt. Das wären dann 10 Hoch 32 Megawattsekunden. Der ganze> Weltenergiebedarf betrug 2010 etwa 140.168 Mrd.kWh. Also so über den> Daumen das 1/10^24 stel des Zahlenbereichs von (üblichen) Floats.>> Dein Kunde muss seeeeeeeeeeeeeeeeeeeeeeeeeeeehr gross sein.>> Ich möchte nochmal betonen wie wichtig es ist, sich vor einer> Implementierung über die Grössenordnungen klar zu werden. An sich gehört> sowas in das Lastenheft. Es dem Kunden so recht wie möglich zu machen> ist ohnehin vergebliche Liebesmüh, da endlos.
Du hast natürlich vollkomen Recht mit dem was du schreibst.
Ich muss mich entschuldigen, da ich einige andere Funktionen des
weiteren Programmcodes vorenthalten habe, wie zbsp. die Ermittlung der
verbrauchten Leistung, die nach Jahren einen XY Betrag haben kann. Mein
Programm soll für "jedermann" verwendbar sein, wodurch dieses letztlich
von der Anzahl der Digits als auch der Impulse pro kwh abhängt. So sind
bspw. die genannte Modulofunktion so geschrieben worden, dass eine Zahl
(uint32_t) übergeben wird und somit für Verbrauchte- und
Momentanleistung fungiert. Neben der genannten Funktion gibt es noch
viele weitere, die ebenso zur Verfügung stehen. Im Gesamt handelt es
sich um ein hobbymäßiges Projekt um meinen Lernerfolg im Umgang mit
µCern und der Programmiersprache zu fördern.
Mr. Xton schrieb:> Ich muss mich entschuldigen, da ich einige andere Funktionen des> weiteren Programmcodes vorenthalten habe, wie zbsp. die Ermittlung der> verbrauchten Leistung, die nach Jahren einen XY Betrag haben kann.
Die Leistung ist durch den Anschlußwert des Gebäudes nach oben begrenzt
und von der Zeit unabhängig.
Falls du die Energie meinst, wird dir hauptsächlich die Auflösung Sorgen
machen, da sich über die Zeit einerseits ein hoher Wert
aufsummieren kann und andererseits bei bei jedem Impuls nur ein geringer
Betrag dazukommt. Float-Zahlen sind da eher kontraproduktiv, weil die
Auflösung vom bereits aufsummierten Wert abhängt und außerdem noch Bits
für den Exponenten verloren gehen.
Mr. Xton schrieb:> Mein Programm soll für "jedermann" verwendbar sein, wodurch dieses> letztlich von der Anzahl der Digits als auch der Impulse pro kwh abhängt.
Trotzdem muss dich der Wert IMPULSE_PRO_KWH nur wenig kümmern. Zur
Messung der Gesamtenergie reicht es, die Impulse zu zählen und später
für die Anzeige in absolute Einheiten umzurechnen. Dann gibt es keine
Probleme mit der Auflösung und du hast einen Ganzzahl-Zähler, der
optimal an den Impulsgeber angepaßt ist und trotzdem nichts verschenkt.