Problem mit Fließkommazahlen

Schnelle objektorientierte, kompilierende Programmiersprache.
Antworten
Santuciy
Beiträge: 3
Registriert: Do Mai 28, 2020 10:31 am

Problem mit Fließkommazahlen

Beitrag von Santuciy » Do Mai 28, 2020 10:57 am

Guten Tag!

Heute habe ich mein erstes C-Programm geschafft!
Jedoch bleibt ein Problem ungelöst, das ich als Anfänger nicht verstehen kann und um dessen Lösung ich bitte.

Im flogenden Programm entsteht ein Problem mit Fließkommazahlen in float.
Die Ein- und Ausgabezahlen werden zufälligerweise verändert.

Bei der Ausführung sieht es so aus:

Bild

Meine Fragen sind:
Warum ist der durch scanf() eingegebene Wert 333.329987 und nicht 333.330000?
Warum ist der gerundete Wert 166.660004 und nicht 166.660000?
Wie lässt sich das Problem lösen?

Der Code:

Code: Alles auswählen

#include <stdio.h>

void Umrechnung();

float Runden001(float);

int main()
{
    int eingabe=0;
    printf("Das Programm zur Umrechnung DM - Euro.\n\n");
    do
        {
            Umrechnung();
            printf("Geben Sie \"1\" ein, um noch eine Umrechnung zu machen.\n");
            printf("Geben Sie \"0\", um das Programm zu beenden: ");
            scanf("%d", &eingabe);
        }
    while(eingabe!=0);
        return 0;
}

void Umrechnung ()
{
    float DMBetrag=0, EuroBetrag=0;
    printf("\nGeben Sie den Betrag in DM ein: ");
    scanf("%f", &DMBetrag);
    EuroBetrag=(DMBetrag/2);
    printf("\n%f DM entsprechen %f Euro.\n\n", DMBetrag, Runden001(EuroBetrag));
}

float Runden001(float zahl)
{
    float ergebnis=(((float)((int)((zahl+0.005)*100)))/100);
    return ergebnis;
}
Ich danke im Voraus für die Hilfe!

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3123
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Problem mit Fließkommazahlen

Beitrag von cloidnerux » Do Mai 28, 2020 11:36 am

Hallo und Willkommen im Forum,

zu allem gibt es ein XKCD, so auch hier: https://xkcd.com/217/
Das Problem ist Fließkomma-Genauigkeit. Floats und Doubles(Fließkommezahlen) werden im Computer in der Form x*2^y gespeichert, daher ein "fester" Teil x und ein Exponent y. Dies entspricht der wissenschaftlichen Zahlennotation 3.45*10^3 z.B. Dies hat den Vorteil, dass extrem kleine und auch extrem große Zahlen gehandhabt werden können, aber leider auch den Nachteil, dass dies nur Näherungen sind, da die Genauigkeit des festen Teiles x beschränkt ist.

In vielen Finanzsystemen, wo man diese Rundungen absolut nicht haben möchte verwendet man Festkommazahlen, daher ein Zahlenformat mit einer festen Breite vor und nach dem Komma. Dies verhindert die Rundungsprobleme, ist aber nicht so flexibel.
Redundanz macht wiederholen unnötig.
quod erat expectandum

Santuciy
Beiträge: 3
Registriert: Do Mai 28, 2020 10:31 am

Re: Problem mit Fließkommazahlen

Beitrag von Santuciy » Fr Mai 29, 2020 10:16 am

Vielen Dank für Ihre Erklärung, cloidnerux!

Dank Ihrem Hinweis auf die Festkommazahlen habe ich eine Lösung konkret für meinen Fall gefunden und zwar hier: https://www.mikrocontroller.net/topic/87745.
Anstatt DM und Euro als Fließkommazahlen darzustallen und damit zu rechnen, habe ich alle interne Rechnungen mit Cent gemacht und so die Fließkommazahlen und folgende Rundungsfehler vermieden.

Falls ein Suchender meinen Code brauchen wird, sieht die Umrechungsfunktion so aus:

Code: Alles auswählen

void Umrechnung ()
{
    int DMBetrag=0, DMCentBetragE=0, DMCentBetragI=0, EuroBetrag=0, EuroCentBetragI=0, EuroCentBetragE=0;   /*  Berechnungen erfolgen nur mit Cent,
                                                                                                                um Rundungen von Fließkommazahlen zu vermeiden
                                                                                                                I - interner (Rechnungs-)Wert; E - externer (Aus- /Input-)Wert */
    printf("\nGeben Sie den Betrag in DM im Format Mark.CC ein: ");
    scanf("%d.%d", &DMBetrag, &DMCentBetragE);

    DMCentBetragI=DMCentBetragE+(DMBetrag*100);
    EuroCentBetragI=(DMCentBetragI/2);
    EuroBetrag=EuroCentBetragI/100;
    EuroCentBetragE=EuroCentBetragI-(EuroBetrag*100);

    printf("\n%d.%d DM entsprechen %d.%d Euro.\n\n",DMBetrag,DMCentBetragE,EuroBetrag,EuroCentBetragE);
}
Ich musste auch für die Schleife zusätzlich den Eingabepuffer mit fflush(stdin) löschen.

mfro
Beiträge: 346
Registriert: Mi Jan 16, 2013 4:58 pm

Re: Problem mit Fließkommazahlen

Beitrag von mfro » Sa Mai 30, 2020 6:30 am

Das fflush(stdin) solltest Du dir nicht angewöhnen.

Auch wenn's auf manchen Plattformen (für stdin) funktioniert, ist es auf den meisten undefined behaviour. Und selbst wenn's (wie bei Windows) dokumentiert (und damit implementation defined) ist, ist es doch wenigstens unlogisch.

Man löscht einen Input-Stream, indem man alle anstehenden Zeichen liest.
It's as simple as that. And remember, Beethoven wrote his first symphony in C.

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Problem mit Fließkommazahlen

Beitrag von Xin » Sa Mai 30, 2020 4:23 pm

Santuciy hat geschrieben:
Do Mai 28, 2020 10:57 am
Warum ist der durch scanf() eingegebene Wert 333.329987 und nicht 333.330000?
Warum ist der gerundete Wert 166.660004 und nicht 166.660000?
Zahlendarstellung auf dem Computer
Santuciy hat geschrieben:
Do Mai 28, 2020 10:57 am
Wie lässt sich das Problem lösen?
Man kann Fix-Point-Zahlen nehmen. Das sind praktisch Integers, denen man eine andere Einheit gibt.
Statt 1,00 Euro kann man 100 Cent nehmen. Oder 10000 Hundertstel-Cent.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Santuciy
Beiträge: 3
Registriert: Do Mai 28, 2020 10:31 am

Re: Problem mit Fließkommazahlen

Beitrag von Santuciy » Di Jun 02, 2020 3:44 pm

Danke für den Ratschlag, mfro!
Man löscht einen Input-Stream, indem man alle anstehenden Zeichen liest.
Diese Zeile habe ich zwar nicht ganz nachvollzogen, habe aber fflush(stdin) durch

Code: Alles auswählen

while(getchar()=='/n'&&getchar()==EOF)
            {
                ;
            }
ersetzt und alles lief einwandfrei.

Xeon
Beiträge: 169
Registriert: So Dez 17, 2017 4:10 pm

double?

Beitrag von Xeon » Di Okt 27, 2020 1:58 pm

Hallo Santuciy

Etwas spät aber ich möchte trotzdem etwas dazu sagen.

Hier die Kurzversion von deinem Code:

Code: Alles auswählen

#include <stdio.h>


int main()
{
  float DM = 333.330000;
  float Euro = 166.660000;

  printf("%f\n", DM);
  printf("%f\n", Euro);

  return 0;
}

Die Ausgabe ist wie bei dir: DM = 333.329987, Euro = 166.660004

Jetzt das Selbe mit dem Datentyp double:

Code: Alles auswählen

#include <stdio.h>


int main()
{
  double DM = 333.330000;
  double Euro = 166.660000;

  printf("%f\n", DM);
  printf("%f\n", Euro);

  return 0;
}
Die Ausgabe ist jetzt bei mir wie gewünscht: DM = 333.330000, Euro = 166.660000

float ist einfach zu klein, um so eine große Zahl darzustellen.


Liebe Grüße
Von Xeon

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8858
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Problem mit Fließkommazahlen

Beitrag von Xin » Di Okt 27, 2020 3:45 pm

Es kann sein (und ich halte es für wahrscheinlich), dass die Darstellung in Double nicht anders ist als in float. Nur dass die Ungenauigkeiten weiter hinter dem Komma entstehen und bei der Darstellung mit 6 Nachkommastellen weggerundet werden.
Fließkommazahlen sind immer ungenau und nur ein Kompromiss.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten