Announcement

Collapse
No announcement yet.

Suche eine allgemein funktionierende Rundungsfunktion

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Hi Torsten

    Du hast recht, aber Double ist kein Real, wenn man einem Double-Wert zur Laufzeit 0.015 eines Extended-Wertes zuweist wird ja auch RICHTIG gerechnet. Mir ist schon klar das bei Float-Zahlen Informationen verloren gehen müssen. Was ich als BUG bezeichne ist nicht der Double-Type, sondern das er als Konstante definiert FALSCH gespeichert wird. Ich nehme mal an eine Double-Konstante wird als REAL gespeichert und NICHT als Double.

    Gruß Hage

    Comment


    • #17
      Hallo Hagen,

      als Real-Zahl meine ich nicht den Delphi-Typ Real sondern Float-Zahlen.

      Tschüß

      Torste

      Comment


      • #18
        Hallo Michael, also ich werden Deine beiden o.g. Zahlen auch noch ausprobieren. Aber wie bereits angedeutet, will ich ja gar nicht kaufmännisch runden, da dies in meiner Branche nicht üblich ist.
        Gruß Jürge

        Comment


        • #19
          <html>

          <head>
          <meta http-equiv="Content-Type"
          content="text/html; charset=iso-8859-1">
          <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
          <title>Normale Seite ohne Titel</title>
          </head>

          <body bgcolor="#FFFFFF">

          <p>Hallo Michael,</p>

          <p>eine die Beschreibung des internen Aufbaus von Double-Werten
          findest Du unter <a
          href="http://www.fh-jena.de/~gmueller/Kurs_halle/pas_ieee.html">http://www.fh-jena.de/~gmueller/Kurs_halle/pas_ieee.html</a></p>

          <p>Dein Problem, z.B. mit dem Wert 0.015 liegt eindeutig beim
          Datentyp Double. Die 52-Bit Mantisse reicht zur exakten
          Darstellung dieses Wertes nicht aus. Mit dem Datentyp Extended
          steht Dir ein größerer Wertebereich und ein exaktere
          Darstellungsmöglichkeit zur Verfügung (64-Bit Mantisse). Aber
          auch mit Extended können nicht alle Zahlen exakt dargestellt
          werden. <font color="#FF0000" size="3"><strong>Somit ist eine 100%
          exakte Rundung ist mit Float-Zahlen unmöglich!</strong></font>.</p>

          <p>Die FPU (Floating Point Unit) hat verschiedene Rundungsmodi.
          Als Standard wird immer zum nächstgelegenen Wert gerundet. Sind
          beide gleich weit entfernt (z.B. 2.5) wird zum nächsten
          geraden Wert gerundet (<font color="#008080"><strong>bei 2.5 auf
          2 und bei 1.5 ebenfalls auf 2!</strong></font>). </p>

          <p>Weitere Modi sind abrunden, aufrunden und abschneiden. Meine
          diesbezügliche Literatur behandelt nur Prozessoren bis zum 486'.
          Eventuell gibt es bei den neueren Prozessoren weitere Modi's (z.B.
          kaufmännisches runden). </p>

          <p>Tschüß</p>

          <p>Torsten</p>
          </body>
          </html&gt

          Comment


          • #20
            <html>

            <head>
            <meta http-equiv="Content-Type"
            content="text/html; charset=iso-8859-1">
            <meta name="GENERATOR" content="Microsoft FrontPage Express 2.0">
            <title>Normale Seite ohne Titel</title>
            </head>

            <body bgcolor="#FFFFFF">

            Hi Hagen,
            <p>
            noch eine kleine Ergänzung. Deine Aussage <font color="#0080C0">&quot;wenn man einem Double-Wert zur Laufzeit 0.015 eines Extended-Wertes zuweist wird ja auch RICHTIG gerechnet&quot;</font> trifft nicht zu. Dazu ein kleines Beispiel in Assembler:
            <p>
            <pre>procedure TForm1.Button1Click(Sender: TObject);
            var
            a: double;
            b: extended;
            begin
            b := 0.015;
            asm
            fld tbyte ptr [b] <font color="#FF0000">{Inhalt von b in oberste FPU-Register laden}</font>
            fstp qword ptr [a] <font color="#FF0000">{Inhalt des obersten FPU-Registers der Double-Variable a speichern}</font>
            wait
            end;
            end;</pre>

            <p>Wenn Du Dir nach der erfolgten Speicherung die Variable a als
            überwachten Ausdruck im Gleitkommaformat anschaust, steht dann
            wieder der von Dir bereits genannte Wert 0.0149...94.</p>

            <p>Tschüß</p>

            <p>Torsten</p>
            </body>
            </html&gt

            Comment


            • #21
              Hallo Michael,

              vielleich habe ich ja doch noch eine Lösung für Dich. Die function von Carsten habe ich ein klein wenig abgewandelt.

              <pre>
              Function ru ( value : double; _nachkommastellen : integer ) : double;
              Var
              m : integer;
              i : integer;
              const
              korrektur: double = 0.50000000000001;
              Begin
              If _nachkommastellen = 0 Then
              result := int (value + korrektur)
              Else
              Begin
              m := 1;
              For i := 1 To _nachkommastellen Do
              m := m * 10;
              result := int(value * m + korrektur) / m;
              End;
              End;
              </pre>

              Die Anzahl der Nullen zwischen 5 und 1 habe ich jetzt nur "per Daumen" eingefügt (sie ist abhängig davon, wieviel signifikante Stellen der Wert VALUE max. haben soll.

              Auch diese abgewandelte function wird nicht alle Zahlen korrekt runden, solange aber die Anzahl der signifikanten Stellen zumindest im ein- vielleich auch im niedrigen 2stelligen Bereich liegt müßte sie funktionieren.

              Tschüß

              Torste

              Comment


              • #22
                Hi Torsten

                Hast'e mal ohne Assembler getestet ? Einfach nur Double = Extended(0.015) ? bei klappts, Ich vermute das dort interne "gerundet" wird ?

                Gruß Hage

                Comment


                • #23
                  Hi Torsten

                  Hast'e mal ohne Assembler getestet ? Einfach nur Double = Extended(0.015) ? bei mir klappts, Ich vermute das dort interne "gerundet" wird ?

                  Gruß Hage

                  Comment


                  • #24
                    Hallo Hagen,

                    wenn Du als Anzeigentyp Gleitkommawert eingibst bekommst Du den exakten Zahlenwert und der ist nun mal 0.0149...94 bei Double.

                    Tschüß

                    Torste

                    Comment


                    • #25
                      "jungs jungs" !!!

                      ihr wolltet nicht zufällig ein buch darüber schreiben??<br>
                      und welche funktion nimmt man nun am besten???

                      mfg

                      marku
                      Herzliche Grüße

                      Markus Lemcke
                      barrierefreies Webdesign

                      Comment


                      • #26
                        Hi,
                        <br>
                        <br>ich will ja kein Querulant sein, aber das überzeugt mich alles noch nicht.
                        <br>Wenn dem so wäre, das dieses Verhalten eine Eigenart von Double Werten
                        ist, wieso gibt es dann Programmiersprachen, in denen dieses Verhalten nicht
                        auftritt?
                        <br>Bsp.:
                        <br>Auf der Basis von Hagens Code aus #13 habe ich folgende zwingende if
                        bedingung aufgebaut:
                        <pre>
                        D := C * 100;
                        If d = (c*100) then
                        Writeln('d = (c*100) TRUE')
                        Else
                        Writeln('d = (c*100) FALSE');
                        </pre>
                        <br>
                        <br>Von der mathematischen Seite her betrachtet sollte immer die If Bedingung
                        erfüllt werden.
                        <br>
                        <br>Umgesetzt wurde dieser Code in die folgenden Sprachen:
                        <br>1) Delphi 5
                        <br>2) Borland Pascal 7
                        <br>3) MS Access 97 (Basic)
                        <br>4) PHP 4.??
                        <br>5) gcc (irgend ein Gnu Compiler auf BeOS 5, leider kenne ich die
                        Versionsnummer nicht.)
                        <br>
                        <br>Die Tests in 1 und 2 waren wie schon hier diskutiert negativ.
                        <br>Da die Tests in 3 und 4 positiv waren habe ich mich schon fast dazu
                        hinreißen lassen zu Behaupten, das sei ein Borland Problem (Phänomen). Jedoch
                        zeigte der Test in 5 auch ein falsches Ergebnis.
                        <br>Damit ist das wohl kein Borland Problem.
                        <br>Liegt es vieleicht daran das 3 und 4 Interpreter sind?
                        <br>Hat man in 3 und 4 den Double Typen nun richtig(er) oder falsch
                        implementiert?
                        <br>Ich hatte jetzt leider kein c# zur Hand. Wenn jemand mal schnell diesen
                        Code in C# ausführen könnte wäre ich ihm sehr dankbar.
                        <br>In den folgenden Code Beispielen habe ich mich hin und wieder mal darum
                        gedrückt den zweiten If Vergleich zu machen, in dem die Double Werte in Strings
                        umgewandelt werden und erst dann verglichen werden. Dieses If Statement ist
                        meiner Meinung nach nicht so gravierend, soll es ja nur im negativen Falle
                        zeigen, das die Zahlen nach Konvertierung doch gleich sind.
                        <br>Es folgen die Code Beispiele

                        Comment


                        • #27
                          <br>Die Code Beispiele:
                          <br>Delphi 5
                          <pre>
                          program DoubleTest;

                          {$APPTYPE CONSOLE}

                          uses

                          sysutils;



                          const

                          C: Double = 0.015;

                          var

                          D: Double;



                          begin

                          D := C * 100;

                          If d = (c*100) then

                          Writeln('d = (c*100) TRUE')

                          Else

                          Writeln('d = (c*100) FALSE');

                          If FloatToStr(d) = FloatTostr((c*100)) then

                          Writeln('FloatToStr(d) = FloatTostr((c*100)) TRUE')

                          Else

                          Writeln('FloatToStr(d) = FloatTostr((c*100)) FALSE');

                          Writeln('C: ',C);

                          Writeln('C*100: ',C*100);

                          Writeln('D: ',D);

                          Readln;

                          end.



                          Ergebnis:

                          d = (c*100) FALSE

                          FloatToStr(d) = FloatTostr((c*100)) TRUE

                          C: 1.50000000000000E-0002

                          C*100: 1.50000000000000E+0000

                          D: 1.50000000000000E+0000

                          </pre>
                          <br>
                          <br>Borland Pascal 7
                          <pre>
                          program DoubleT;

                          {$N+}

                          uses crt;

                          const

                          C: Double = 0.015;

                          var

                          D: Double;



                          begin

                          D := C * 100;

                          If d = (c*100) then

                          Writeln('d = (c*100) TRUE')

                          Else

                          Writeln('d = (c*100) FALSE');

                          (* If Str(d) = Str((c*100)) then

                          Writeln('Str(d) = Str((c*100)) TRUE')

                          Else

                          Writeln('FloatToStr(d) = FloatTostr((c*100)) FALSE');*)

                          Writeln('C: ',C);

                          Writeln('C*100: ',C*100);

                          Writeln('D: ',D);

                          Readln;

                          end.



                          Ergebnis:

                          d = (c*100) FALSE

                          C: 1.50000000000000E-0002

                          C*100: 1.50000000000000E+0000

                          D: 1.50000000000000E+0000

                          </pre>
                          <br>
                          <br>Access 97
                          <pre>
                          Option Compare Database

                          Option Explicit



                          Private Const C As Double = 0.015

                          Public Sub x()

                          Dim D As Double

                          D = C * 100

                          If D = (C * 100) Then

                          Debug.Print "d = (c*100) TRUE"

                          Else

                          Debug.Print "d = (c*100) FALSE"

                          End If

                          If Str(D) = Str((C * 100)) Then

                          Debug.Print "Str(d) = Str((c*100)) TRUE"

                          Else

                          Debug.Print "Str(d) = Str((c*100)) FALSE"

                          End If

                          Debug.Print "C: " & C

                          Debug.Print "C*100: " & C * 100

                          Debug.Print "D: " & D

                          End Sub



                          Ergebnis:

                          d = (c*100) TRUE

                          Str(d) = Str((c*100)) TRUE

                          C: 0,015

                          C*100: 1,5

                          D: 1,5

                          </pre>
                          <br>
                          <br>PHP 4.??
                          <pre>
                          $C = 0.015;

                          $D = $C * 100;

                          if ($D == ($C*100))

                          echo 'br $D == ($C*100) TRUE';

                          else

                          echo 'br $D == ($C*100) FALSE';

                          if ((string)$D == (string)($C*100))

                          echo ' br ((string)$D == (string)($C*100)) TRUE';

                          else

                          echo ' br ((string)$D == (string)($C*100)) FALSE';

                          echo ' br $C: '."$C";

                          echo ' br $C*100: '.(string)($C*100);

                          echo ' br $D: '."$D";



                          Ergebnis:

                          $D == ($C*100) TRUE

                          ((string)$D == (string)($C*100)) TRUE

                          $C: 0.015

                          $C*100: 1.5

                          $D: 1.5

                          </pre>
                          <br>
                          <br>Gnu C Compiler der auf meinem BeOS liegt. Leider weiß ich die Version
                          nicht.
                          <pre>
                          #include stdio.h
                          main()
                          {
                          double C = 0.015;
                          double D = C * 100;
                          if (D == (C*100))
                          printf("D == (C*100) TRUE\n");
                          else
                          printf("D == (C*100) FALSE\n");
                          /* if ((string)D == (string)(C*100))
                          printf("((string)D == (string)(C*100)) TRUE\n");
                          else
                          printf("((string)D == (string)(C*100)) FALSE\n");*/
                          printf("C: %f\n",C);
                          printf("C*100: %f\n",(C*100));
                          printf("D: %f\n",D);
                          }
                          Ergebnis:
                          D == (C*100) FALSE
                          C: 0.015000
                          C*100: 1.500000
                          D: 1.500000
                          </pre&gt

                          Comment


                          • #28
                            Hallo Patrick,

                            meiner Meinung nach ist es in Deinem Testcode sinnvoller die Variable D gleich mit 1.5 zu initialisieren.

                            Bei den Interpeter-Sprachen vermute ich mal das die intern runden.

                            Tschau

                            Torsten

                            PS: wenn man das Programm einfach mal mit dem Debugger im "Maschinencode" beobachtet und sich die Ergebinsse der FPU anschaut wird einem vieles klarer

                            Comment


                            • #29
                              Hallo Torsten,
                              <br>
                              <br>>meiner Meinung nach ist es in Deinem Testcode sinnvoller die Variable D gleich mit 1.5 zu initialisieren.
                              <br>
                              <br>Das habe ich extra gemacht, da ich ja den Compiler etwas unter Streß setzen wollte.
                              <br>
                              <br>>Bei den Interpeter-Sprachen vermute ich mal das die intern runden.
                              <br>
                              <br>Wenn dem so wäre, was ist dann daran verwerflich? Denn rein mathematich gesehen, kommt beim Runden das richtige Ergebnis heraus. Was ist nun falsch? Das Runden (das den Schwachpunkt der FPU beseitigt) oder zu behaupten die Werte wären nicht gleich (was ja auch stimmt, da die FPU die Werte nicht korrekt speichert). Was mich interessieren würde ist, ob auch die Programmiersprachen die den Test mit 1.5 bestanden haben bei irgend welchen Zahlen ins schleudern kommen.
                              <br>
                              <br>>...und sich die Ergebinsse der FPU anschaut wird einem vieles klarer.
                              <br>
                              <br>Die Ergenisse sind unbestreitbar, aber warum gibt es Programmiersprachen die anscheinend nicht dafür anfällig sind?
                              <br>In C# habe ich es auch mal getestet (siehe Code weiter unten) und hier tritt der Fehler auch nicht auf (ich wahr angenehm überrascht (ein Pluspunkt für C#)).
                              <br>(Es würde mich sehr interessieren, wie sich das alte Visual C++ verhält bei dem Beispiel Code und wie sich der selbe c++ code im .Net Framework verhält.)
                              <br>Der Typ Double ist in allen Sprachen 64 Bit groß (ich habe jetzt nur mal in der Delphi, C# und Access Hilfe nachgeschaut (daraus folgere ich das es auch bei den anderen Sprachen so ist)). Damit ist die Voraussetzung gleich, jedoch rechnen die einen so, das "mathematisch" kein Fehler auftritt. Und daraus folgere ich, wenn man es nicht als Bug bezeichnen kann (siehe Hagen), so ist es zumindest nicht mehr zeitgemäß.
                              <br>
                              <br>Spätestens wenn jemand den nahezu identischen Delphi Code in Delphi.net und Delphi 7 ausführt und unterschiedliche Ergebnisse bekommt wird das ganze sehr fragwürdig. Ich denke (hoffe), daß das in irgend einer Delphi Version geändert wird.
                              <br>
                              <br>Aber was kann man nun als Lösung für diese Problem anbieten?
                              <br>1)
                              <br>Wie Hagen schon sagte man könnte Extended verwenden. Aber wie sieht es dann mit der Kommunikation mit anderen Komponenten aus?
                              Was passiert wenn man MYDBField.asFloat := MyExtended; oder umgekehrt schreibt. Wie genau rechnet der IB mit Dialect 3 bei Double Preccision (ist ja auch ein Borland Produkt). Das werde ich mal bei Zeiten testen. (Wenn jemand das zufällig weiß, ob es hier ebenfalls zu Problemen kommen kann bitte melden. Dann erspare ich mir Arbeit. (Danke!))) Wie sieht es im Vergleich dazu mit einem MS Server aus?
                              <br>2)
                              <br>Wenn man Double Werte vergleicht muß man diese vorher in einen String konvertieren und dann vergleichen (Gäähhn).
                              (Wenn jemand noch eine bessere Idee hat, bitte melden. (Danke!)))
                              <br>
                              <br>mfg
                              <br>P

                              Comment


                              • #30
                                Der C# Code (siehe vorherige Message):
                                <pre>
                                using System;

                                namespace ConsoleApplication1
                                {
                                /// <summary>
                                /// Zusammendfassende Beschreibung für Class1.
                                /// </summary>
                                class Class1
                                {
                                /// <summary>
                                /// Der Haupteinstiegspunkt für die Anwendung.
                                /// </summary>
                                [STAThread]
                                static void Main(string[] args)
                                {
                                //
                                // TODO: Fügen Sie hier Code hinzu, um die Anwendung zu starten
                                //
                                double C = 0.015;
                                double D = C * 100;
                                if (D == (C*100))
                                System.Console.Write("D == (C*100) TRUE\n");
                                else
                                System.Console.Write("D == (C*100) FALSE\n");
                                if (Convert.ToString(D) == Convert.ToString(C*100))
                                System.Console.Write("(Convert.ToString(D) == Convert.ToString(C*100)) TRUE\n");
                                else
                                System.Console.Write("(Convert.ToString(D) == Convert.ToString(C*100)) FALSE\n");
                                System.Console.WriteLine("C: " + Convert.ToString(C));
                                System.Console.WriteLine("C*100: " + Convert.ToString(C*100));
                                System.Console.WriteLine("D: " + Convert.ToString(D));
                                System.Console.ReadLine();
                                }
                                }
                                }

                                Ergenis:
                                D == (C*100) TRUE
                                (Convert.ToString(D) == Convert.ToString(C*100)) TRUE
                                C: 0,015
                                C*100: 1,5
                                D: 1,5
                                </pre&gt

                                Comment

                                Working...
                                X