Fehler in der Berechnung (Wechselgeldrechner)



  • Hallo cppler

    Ich habe mal wieder etwas Langweile geschoben und dachte mir, um dem Abhilfe zu schaffen, programmierst einen Wechselgeldrechner.
    Das ist mir auch gelungen, nur bei manchen Eingaben kommt er nicht klar, z.B.
    Kosten: 3.89
    Bezahlt: 4
    Rückgeld 0.11
    Ausgabe: 1 * 0.1

    Aber den einen Cent möchte er mir nicht geben, ich habe im Debugger nachgeschaut, der Compiler oder wer auch immer meint 0.11 - (1 * 0.10) = 0.009999..
    Auch bei manchen Sachen: floor(0.1/0.1) = 0
    Ich denke mal das liegt an der precision aber ich weis nicht wie ich es beheben kann.

    #include <vcl.h>
    #include <iostream.h>
    #include <math.h>
    #pragma hdrstop
    
    //---------------------------------------------------------------------------
    
    #pragma argsused
    double* Changer(double* arr,double &foo)
    {
      unsigned int high = floor(foo);
      double low = foo - high;
      short int up[8] = {500,200,100,50,20,5,2,1};
      short int count=0,tmp=0;
      for (short int i = 0; i < 8; i++)     // höherwertiger Teil
      {
        tmp = floor(high/up[i]);  // Hilfsvar
        if (tmp > 0)
        {
          arr[count] = tmp;
          arr[count+1] = up[i];
          high = high - (tmp * up[i]);
          count = count + 2;
          if (high == 0) break;
        }
      }
      double down[6] = {0.5,0.2,0.1,0.05,0.02,0.01};
      for (short int i = 0; i < 6; i++)    // niederwertiger Teil
      {
        tmp = floor(low/down[i]);
        if (tmp > 0)
        {
          arr[count] = tmp;
          arr[count+1] = down[i];
          low = low - (tmp * down[i]);
          if (low == 0) break;
          count = count + 2;
        }
      }
      return arr;
    }
    int main(int argc, char* argv[])
    {
            double price,paid,diff;
            cout << "Price to be paid ?  (max. 10000 Euro) ";
            cin >> price;
            cout << "What you paid ? ";
            cin >> paid;
            if (paid > price && paid <= 10000)
            {
              diff = paid - price;
              cout << "The remainder is: " << diff << endl;
              double rest[20]={0};
              double* remaind = Changer(&rest[0],diff);
              short int i=0;
              while (remaind[i] != 0)
              {
                cout << remaind[i] << " x " << remaind[i+1] << endl;
                i=i+2;
              }
            }
            else cout << "You are a cheater :D";
            cin.get();
            cin.get();
            return 0;
    }
    

    Ich habe da so eine Ahnung das ich es unnötig kompliziert gemacht habe, wenn wer weiß wie es viel einfacher geht dann bin ich dem nicht abgeneigt.

    Danke 😉



  • Es liegt nicht an der Precision sondern am datentyp selber.

    float und double können Zahlen nur ungenau abbilden. Bestimmte Zahlenwerte sind dabei unmöglich. Für allgemeine Berrechnungen wie z.B. Grafikengines ist das egal, aber wenn Du Genauigkeit brauchst wie z.B. in Deinem Fall dann sind float und double ungeeignet.



  • shadowmasta schrieb:

    Aber den einen Cent möchte er mir nicht geben, ich habe im Debugger nachgeschaut, der Compiler oder wer auch immer meint 0.11 - (1 * 0.10) = 0.009999..
    Auch bei manchen Sachen: floor(0.1/0.1) = 0
    Ich denke mal das liegt an der precision aber ich weis nicht wie ich es beheben kann.

    Nein daran liegt es nicht! das liegt daran, dass die Währung im 10'er-System gerechnet werden soll und der Computer nun mal im Binär-System 'denkt'. Die unmittelbare Folge davon ist, dass er Zahlen wie '0,1' (dezimal) überhaupt nicht exakt darstellen kann. Genauso wenig wie man im Dezimalsystem 1/3 exakt hinschreiben kann ( =0,33333 .. usw. bis unendlich).

    dob schrieb:

    ... aber wenn Du Genauigkeit brauchst wie z.B. in Deinem Fall dann sind float und double ungeeignet.

    nicht ganz; man kann sich schon behelfen. In diesem Fall muss man eben bei jedem Übergang von double nach int (oder short usw.) genau aufpassen was man tut. Hier reicht es aus, auf Cent (also 1/100'stel) zu runden. Im Code an den Zeilen 11 und 30

    unsigned int high = floor(foo + 0.005); // auf Cent runden
        // ..
        tmp = floor(low/down[i] + 0.005); // auf Cent runden
    

    Gruß
    Werner



  • shadowmasta schrieb:

    Ich habe da so eine Ahnung das ich es unnötig kompliziert gemacht habe, wenn wer weiß wie es viel einfacher geht dann bin ich dem nicht abgeneigt.

    Ach den Satz hatte ich fast übersehen. So geht's auch

    #include <iostream>
    #include <iomanip>      // setprecision
    #include <iterator>     // ostream_iterator
    #include <cmath>        // floor
    // #include <boost/io/ios_state.hpp>
    
    struct BarGeld
    {
        BarGeld() : m_anz(0), m_wert_in_cent(0) {}
        BarGeld( int anz, int wert_in_cent )
            : m_anz( anz ), m_wert_in_cent( wert_in_cent ) 
        {}
        int Wert_in_cent() const { return m_anz * m_wert_in_cent; }
        friend std::ostream& operator<<( std::ostream& out, const BarGeld& bg )
        {
            using namespace std;
            // boost::io::ios_base_all_saver bas( out );
            out << fixed << setprecision( 2 );
            return out << bg.m_anz << " x " << double( bg.m_wert_in_cent ) / 100;
        }
    private:
        int m_anz;
        int m_wert_in_cent;
    };
    
    template< typename OutItr >
    void Changer( double betrag, OutItr out )
    {
        const int zahlungsmittel[] = // in Cent
        { 50000, 20000, 10000, 5000, 2000, 1000, 500, 200, 100, 50, 20, 10, 5, 2, 1 };
        int cent = floor( betrag * 100 + 0.5 ); // Betrag auf Cent runden
        for( const int* zm = zahlungsmittel
            ; zm != zahlungsmittel + sizeof(zahlungsmittel)/sizeof(*zahlungsmittel); ++zm )
        {
            if( cent >= *zm )
            {
                const BarGeld bg( cent / *zm, *zm );
                *out++ = bg;
                cent -= bg.Wert_in_cent();
            }
        }
    }
    
    int main()
    {
        using namespace std;
        double price,paid,diff;
        cout << "Price to be paid ?  (max. 10000 Euro) ";
        cin >> price;
        cout << "What you paid ? ";
        cin >> paid;
        if (paid > price && paid <= 10000)
        {
            diff = paid - price;
            cout << "The remainder is: " << diff << endl;
            Changer( diff, ostream_iterator< BarGeld >( cout, "\n" ) );
        }
        else cout << "You are a cheater :D";
        cin.get();
        return 0;
    }
    

    Gruß
    Werner



  • Erstmal vielen Dank an euch beide, hat geklappt mit dem Aufrunden, gut zu wissen.
    Deine Version ist ziemlich kompliziert für meine Kenntnisse ich werde sie versuchen zu studieren danke dir 😉

    Übrigens war das eine Aufgabe aus dem unten stehenden Buch nach dem ersten Kapitel: "The basics" (fallunterscheidung,schleifen,arithmetische ausdrücke..)
    Das Buch heißt: "C++ From the Beginning"

    😃


Anmelden zum Antworten