Forum: Mikrocontroller und Digitale Elektronik Typumwandlung via Typecast?


von Lee S. (sennox)


Lesenswert?

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.

von Mr.Int (Gast)


Lesenswert?

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.

von Ingo (Gast)


Lesenswert?

Fixed Point?!? Man benötigt wirklich selten Fließkomma. Aus 10,234 
kannst du auch 10234 machen, wo is der Unterschied?


Ingo

von Dauergast (Gast)


Lesenswert?

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?

von Lee S. (sennox)


Lesenswert?

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

von Hmm (Gast)


Lesenswert?

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.

von Georg G. (df2au)


Lesenswert?

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?

von xfr (Gast)


Lesenswert?

1
wert = 1 / IMPULSE_PRO_KWH;
2
wert = wert * 1000;
3
wert = wert / 3600;
4
wert = wert / 100;
5
wert = wert / Gemessene_Zeit_ms;
6
wert = wert * 100;

Wenn man das mal zusammenfasst, ergibt das:
1
wert = 1 / IMPULSE_PRO_KWH * 1000 / 3600 / 100 / Gemessene_Zeit_ms * 100;

Jetzt stellen wir alle Divisionen ans Ende:
1
wert = 1000 * 100 / 3600 / 100 / IMPULSE_PRO_KWH / Gemessene_Zeit_ms;

Äquivalent:
1
wert = 10 / (36 * IMPULSE_PRO_KWH * Gemessene_Zeit_ms);

So, das klappt natürlich nicht, weil da nur ganzzahlige Werte zwischen 0 
und 10 rauskommen können. Also hängst Du ein paar Nullen an:
1
wert = 10UL * 1000 * 1000 / (36 * IMPULSE_PRO_KWH * Gemessene_Zeit_ms);

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.

von xfr (Gast)


Lesenswert?

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.

von Lee S. (sennox)


Lesenswert?

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.

von Hmm (Gast)


Lesenswert?

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

von Lee S. (sennox)


Lesenswert?

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

von Hmm (Gast)


Lesenswert?

Vor allem reden wir hier über 10 hoch 14 Yottawatt. Hast Du die ganze 
Galaxie an Deinem Stromzähler? :-)

von Lee S. (sennox)


Lesenswert?

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

von xfr (Gast)


Lesenswert?

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:
1
wert = (10UL * 1000 * 1000 / (36 * IMPULSE_PRO_KWH)) / Gemessene_Zeit_ms;

Oder für noch größeren Wertebereich, aber immer noch 32-Bit-Division:
1
wert = (uint32_t) (10ULL * 1000 * 1000 * 1000 / (36 * IMPULSE_PRO_KWH)) / Gemessene_Zeit_ms;

von Hmm (Gast)


Lesenswert?

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.

von Lee S. (sennox)


Lesenswert?

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!

von Lee S. (sennox)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von Werner (Gast)


Lesenswert?

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.

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.