Forum: Mikrocontroller und Digitale Elektronik Typenproblem


von jemand (Gast)


Lesenswert?

Hallo

in einer Funktion, die mir einen float zurückgibt, habe ich einfach 
direkt 2 Zahlen eingegeben:

float xxx()
{
  return 537/6
}

das ergibt normalerweise 89.5, aber das Resultat is 89.

Ich bin in die Falle getappt und habe zuerst auf Anhieb nicht sagen 
können, was das Problem ist, aber dann ist mir eingefallen, dass ja C im 
Hintergrund mit int's rechnet.

Wenn ich 537/6.0 oder 537.0/6 rechne stimmt es.
Was passiert nochmals genau im Detail? Ich habe das hier vor kurzem 
irgendwo gelesen, wo es Falk so schön erklärt hat (irgendwas mit: C 
rechnet von rechts nach links), aber ich finde diesen Thread leider 
nicht mehr. Dort hat bei jemandem auch die einfachste Rechnung nicht 
funktioniert und zwar genau aus diesem Grund oben.

Könnte mir jemand kurz erklären, wie C da genau vorgeht?

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Du teilst einen Int durch einen Int, warum sollte da für den Compiler 
was anderes als wieder ein Int rauskommen?

von Peter D. (peda)


Lesenswert?

Berechnungen erfolgen immer im höchsten Format der beiden Operanden bzw. 
mindestens in int, der Zieltyp spielt keine Rolle. Ein Konversion 
erfolgt erst nach der Berechnung.
Eine Ausnahme sind Schiebeoperatoren, da erfolgt die Berechnung im Typ 
des ersten Operanden.

von Stefan F. (Gast)


Lesenswert?

Das ist wie in der Grundschule. Dort ergeben solche Divisionen ein 
Ergebnis und einen Rest. So rechnet der Computer auch - es sei denn, 
einer der Operanden ist eine Fließkommazahl.

Ein verwandtes Problem bekommst du bei großen Zahlen. Ich gehe jetzt mal 
von einem AVR Mikrocontroller aus, wo die int Typen 16bit groß sind:
1
int a=10000 * 8 / 100;

Da kommt etwas völlig falsches bei heraus, weil "10000 * 8" mit einem 
Algorithmus für int nicht korrekt berechenbar ist. Das Ergebnis ist 
nämlich zu groß.

Die Division durch 100 wird erst danach durchgeführt, aber nach dem 
kaputten Zwischenergebnis wird es nicht mehr besser.

Man löst das so:
1
int a=10000L * 8 / 100;

Das L sagt dem Compiler, dass diese Zahl ein long Integer sein soll. 
Dann wendet er einen Algorithmus für Long Integer an, und der kann 
"10000 * 8" korrekt berechnen.

von jemand (Gast)


Lesenswert?

Peter D. schrieb:
> Ein Konversion erfolgt erst nach der Berechnung.

Danke, das wollte ich wissen.

Peter D. schrieb:

> Eine Ausnahme sind Schiebeoperatoren, da erfolgt die Berechnung im Typ
> des ersten Operanden.

Also:

zB 537.0 / 128 >> 2 =  537.0 / 64 = wäre das Ergebnis also ein float, 
aber dann kann ich es ja gleich so schreiben, dass der erste oder 2. 
Operand einen Dezimalpunkt hat. 537.0/64 oder 537/64.0
Oder was genau ist mit dem Schiebeoperator gemeint?

Wo kann ich künftig solche Informationen in schriftlicher Form 
nachschlagen?
Einen C-Standard habe ich nicht, kostet die denn etwas? Wenn ja, was 
macht ein Privatnutzer wie ich dann?

von jemand (Gast)


Lesenswert?

Stefanus F. schrieb:
> Das ist wie in der Grundschule. Dort ergeben solche Divisionen ein
> Ergebnis und einen Rest. So rechnet der Computer auch - es sei denn,
> einer der Operanden ist eine Fließkommazahl.
>
> Ein verwandtes Problem bekommst du bei großen Zahlen. Ich gehe jetzt mal
> von einem AVR Mikrocontroller aus, wo die int Typen 16bit groß sind:
> int a=10000 * 8 / 100;
>
> Da kommt etwas völlig falsches bei heraus, weil "10000 * 8" mit einem
> Algorithmus für int nicht korrekt berechenbar ist. Das Ergebnis ist
> nämlich zu groß.
>
> Die Division durch 100 wird erst danach durchgeführt, aber nach dem
> kaputten Zwischenergebnis wird es nicht mehr besser.
>
> Man löst das so:int a=10000L * 8 / 100;
>
> Das L sagt dem Compiler, dass diese Zahl ein long Integer sein soll.
> Dann wendet er einen Algorithmus für Long Integer an, und der kann
> "10000 * 8" korrekt berechnen.

Super Danke, das hilft mir. Gute Erklärung.

Jetzt würde ich noch gerne das mit dem Schiebeoperator klären und die 
paar Fragen die ich oben gestellt habe und das wars dann auch schon.

von Stefan F. (Gast)


Lesenswert?

jemand schrieb:
> Oder was genau ist mit dem Schiebeoperator gemeint?

Er meinte solche Situationen:
1
PORTD = PINB << 1;

Hier wird Der Port B eingelesen und alle Bits um 1 Position verschoben 
auf Port D ausgegeben. Die 1 ist ein Integer, das Ergebnis der 
Shift-Operation ist aber ein Byte, weil der linke Operand (PINB) ein 
Byte ist.

Wo kann ich künftig solche Informationen in schriftlicher Form
nachschlagen?

Was den Standard angeht, schaue ich dort nach: 
https://de.wikibooks.org/wiki/C-Programmierung

Da ich aber ausschließlich den gcc verwende, ist dieses Dokument für 
mich die erste Anlaufstelle: 
https://www.gnu.org/software/gnu-c-manual/gnu-c-manual.html

Wo das hier diskutierte Verhalten dokumentiert ist, weiß ich allerdings 
nicht. Da hatte ich so wie du durch überraschende Effekte kennen 
gelernt.

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


Lesenswert?

jemand schrieb:
> Oder was genau ist mit dem Schiebeoperator gemeint?
Wenn du einen (long)24 um einen char(2) verschiebst, dann ist das 
Ergebnis logischerweise ein long Wert.

Wenn du aber einen (char)24 um (long)2 verschiebst, dann ist das Egebnis 
eben kein long sondern bleibt ein char, obwohl der zweite Operator ja 
einen größeren Wertebereich hat.

EDIT: Hoppla, nur Zweiter... ;-)

: Bearbeitet durch Moderator
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

jemand schrieb:
> Einen C-Standard habe ich nicht, kostet die denn etwas? Wenn ja, was
> macht ein Privatnutzer wie ich dann?

Die Google-Suche "C Standard" spuckt als erstes Wikipedia aus:

https://de.wikipedia.org/wiki/C_(Programmiersprache)

Dort findest Du einige Hinweise zu den C-Standards, die sich historisch 
von K&R-C bis C18 entwickelt haben.

In den Fußnoten dazu findest Du Links auf die ISO-Standards.

Wenn Du diesen folgst, wirst Du sehen: ja, die kosten etwas.

Hol Dir lieber ein gutes Buch zu C, da hast Du mehr davon. Dein 
geschildertes Problem im Ausgangsposting ist absolutes Basic, was schon 
im K&R aus den 70ern erklärt wird.

: Bearbeitet durch Moderator
von Dirk B. (dirkb2)


Lesenswert?

jemand schrieb:
> Einen C-Standard habe ich nicht, kostet die denn etwas?

Ja, die kosten was

> Wenn ja, was
> macht ein Privatnutzer wie ich dann?

Er schlägt im Draft nach. Das ist die vorletzte Version, die ohne 
Änderung als Standard übernommen wird.
Die kann man im Netz finden.

Allerdings stehen die Sachen da oft nicht so leicht verständlich drin.

Bemerkung:
Bei 537/6 * 3.5 wird die Division auch in int durchgeführt

Die Rangfolge der Operatoren (u.a. Punkt vor Strich) findet man auch im 
Netz oder im Buch.

von Dirk B. (dirkb2)


Lesenswert?

Ich verweise mal auf 
https://www.c-plusplus.net/forum/topic/300567/linkliste-f%C3%BCr-neulinge

Ok, C18 gibt es auch schon.

von Oliver S. (oliverso)


Lesenswert?

jemand schrieb:
> Könnte mir jemand kurz erklären, wie C da genau vorgeht?

Vollständig, aber in Englisch, gibt es daß hier:

https://en.cppreference.com/w/c/language/conversion

Oliver

von zitter_ned_aso (Gast)


Lesenswert?

jemand schrieb:
> zB 537.0 / 128 >> 2 =  537.0 / 64 = wäre das Ergebnis also ein float,

Nein, double.

jemand schrieb:
> float xxx()
> {
>   return 537/6
> }
>
> ....
>
> Wenn ich 537/6.0 oder 537.0/6 rechne stimmt es.



Bei Fließkommazahlen wird mit "double" gerechnet. Willst du eine 
Fließkommazahl als "float" deklarieren, so musst du ein "f"/"F" 
anhängen: 537.0f (das ist ist eine float-Zahl, vorher war wie vom Typ 
"double").



double = doppelte Genauigkeit. So rechnest du in der Funktion und gibt 
die Zahl aber mit einer einfachen Genauigkeit zurück.
1
printf("sizeof(%f)=%zd\n", 10.5, sizeof(10.5));
2
printf("sizeof(%ff)=%zd\n", 10.5f, sizeof(10.5f));
Ausgabe:
1
sizeof(10.500000)=8 byte
2
sizeof(10.500000f)=4 byte

von Stefan F. (Gast)


Lesenswert?

double und float ergibt bei AVR allerdings ausnahmsweise das gleiche, 
weil der avr-gcc nur float kann.

von Dirk B. (dirkb2)


Lesenswert?

Bei Funktionen mit variablen Argumenten (z.B. printf) wird bei 
Fließkommazahlen mindestens double übergeben. Bei Ganzzahltypen 
mindestens int.

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.