Announcement

Collapse
No announcement yet.

Suche eine allgemein funktionierende Rundungsfunktion

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

  • Suche eine allgemein funktionierende Rundungsfunktion

    Ich habe ziemliche Probleme mit runden von Double-Werten.
    Allgemein ist die Formel folgende y = Int( x * ( 10 ^ Nk ) + 0.5 ) / ( 10 ^ Nk ). Wenn x = 0.015 und Nk = 2, dann kommt y = 0.01 statt y = 0.02 heraus.
    Mit Extended würde ich diesen Problemfall lösen, bin aber immer noch
    skeptisch, ob es unter den Extended-Typen ganz andere Problemfälle gibt!
    Kann mir jemand eine eleganten Lösungsansatz (am liebsten mit Double-Felder) verraten? Apropos: Kennt jemand den internen Aufbau von Double-Feldern und kann diesen mir beschreiben?

  • #2
    Hallo Michael,

    hier ist ein allgemein gültige Funktion zum Runden von Doublewerten:

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

    mf

    Comment


    • #3
      Ich bedanke mich vorab für Deine Antwort. Leider mußte ich jedoch feststellen, daß der Fall 0.015 immer noch das Rundungsergebnis 0.01 bringt. Grass wird es, wenn man Zahlen mit 4 Nk auf 3 Nk rundet! Anbei habe ich mal meine Routine mitgeschickt, mit der ich die Fälle mal simuliere:

      function WertRunden( aZahl : Double; aNk : Integer ) : Double;<BR>
      var<BR>
      wMult : Integer;<BR>
      wAdd : Double;<BR>
      wIdx : Integer;<BR>
      begin<BR>
      if aNk = 0 then<BR>
      begin<BR>
      result := int( aZahl + 0.5 );<BR>
      end else<BR>
      begin<BR>
      wMult := 1;<BR>
      for wIdx := 1 to aNk do<BR>
      begin<BR>
      wMult := wMult * 10;<BR>
      wAdd := 5.0 / ( wMult * 10 );<BR>
      Result := Int( ( aZahl + wAdd ) * wMult ) / wMult;<BR>
      end;<BR>
      end;<BR>
      end;<BR>
      <BR>
      procedure TForm1.Button1Click(Sender: TObject);<BR>
      var<BR>
      wFloat1, wFloat2, wFloat3 : Double;<BR>
      wIdx : Longint;<BR>
      wStr : String;<BR>
      begin<BR>
      wFloat1 := 0.0; // Wert, der gerundet werden soll<BR>
      wFloat2 := 0.0; // Wert, der als Ergebis herauskommen soll<BR>
      for wIdx := 1 to 1000 do<BR>
      begin<BR>
      wFloat1 := wFloat1 + 0.001; // 3-stellige Doublezahl erzeugen<BR>
      if ( wIdx mod 10 ) = 5 then<BR>
      begin<BR>
      wFloat2 := wFloat2 + 0.01; // aufrunden<BR>
      end;<BR>
      wFloat3 := WertRunden( wFloat1, 2 );<BR>
      if Format( '%.2f', [ wFloat3 ] ) <> Format( '%.2f', [ wFloat2 ] ) <BR>then // Wert ungleich?<BR>
      begin<BR>
      Memo1.Lines.Add( Format( '%d: %.3f: %.2f <> %.2f', [ wIdx, wFloat1, wFloat2, wFloat3 ] ) );<BR>
      end;<BR>
      end;<BR>
      Memo1.Lines.Add( '---' );<BR>
      end;<BR&gt

      Comment


      • #4
        Zum Aufbau soviel: Gib einmal in der Delphi-Hilfe im Index "Double" ein. Falls sich mit deiner Version nichts geändert hat, erscheint Double (Datentype), dieser Punkt führt direkt zu dem Aufbau mit Skizze über den Internen Aufbau. Falls es nicht klappt, kann ich es dir ja zuschicken (ich verwende Delphi 3).

        Gruss And

        Comment


        • #5
          Könntest Du mir (wenn es geht) die Hilfeseite in den Forum reinlegen (unter der Delphi4-Hilfe habe ich nichts gefunden)?
          Es Reicht mir schon textuell.

          mfg

          Comment


          • #6
            Hallo Michael, hier meine Lösung:

            function Potenz ( Basis, Exponent: double ): double;
            begin
            try
            result:=exp(ln(abs(basis))*exponent)
            except { TODO : Exception für ungültige Potenzwerte }
            result:=0
            end
            end;

            function Runde ( Value: double; Precision:integer ): double;
            var temp: double;
            const Korrektur = 0.5; // zum Runden in die "richtige" Richtung
            begin
            temp:=Potenz(10,abs(Precision));
            if Value>=0
            then result:=Trunc(Value)+Trunc(Frac(Value)*temp+Korrek tur)/temp
            else result:=Trunc(Value)+Trunc(Frac(Value)*temp-Korrektur)/temp
            end;

            Ich fahre hiermit eigentlich ganz gut. Fairerweise muß ich zugeben, daß ich sie ursprünglich mit EXTENDED zusammengebastelt und getestet habe. Allerdings sind mir mit DOUBLE noch keine Ungereimtheiten aufgefallen.
            Gruß Jürge

            Comment


            • #7
              Hallo Jürgen,

              wie auch die eine Rundungs-Routine habe ich auch mal Deine Routine gechecked. mit den Wert 1,15 und der Rundung auf eine Nk-Stelle kommt diese Routine auch nicht klar (weder Double noch Extended)! Anbei mein Versuchs-Source:
              <BR>
              function Potenz( Basis, Exponent: Extended ): Extended;<BR>
              begin<BR>
              try<BR>
              result:=exp(ln(abs(basis))*exponent)<BR>
              except<BR>
              { TODO : Exception für ungültige<BR>
              Potenzwerte }<BR>
              result:=0<BR>
              end;<BR>
              end;<BR>
              <BR>
              function Runde( Value: Extended; Precision:integer ): Extended;<BR>
              var<BR>
              temp: Extended;<BR>
              const Korrektur = 0.5; // zum Runden in die "richtige" Richtung<BR>
              begin<BR>
              temp:=Potenz(10,abs(Precision));<BR>
              if Value>=0 then<BR>
              result:=Trunc(Value)+Trunc(Frac(Value)*temp+Korrek tur)/temp<BR>
              else<BR>
              result:=Trunc(Value)+Trunc(Frac(Value)*temp-Korrektur)/temp<BR>
              end;<BR>
              <BR>
              procedure TForm1.Button1Click(Sender: TObject);<BR>
              const<BR>
              cWert : Extended = 1.15;<BR>
              begin<BR>
              label1.caption := FloatToStr( Runde( cWert, 1 ) );<BR>
              end;<BR>
              <BR&gt

              Comment


              • #8
                Hallo Michael, ich untersuche die Sache mal bei mir. So oder so melde ich mich dann noch mal.

                Gruß Jürge

                Comment


                • #9
                  Hallo Michael, also ich habe das ausprobiert, und meiner Meinung nach funktioniert es. D.h., es wird bei ,5-Werten immer in die ungerade Richtung gerundet. So kenne ich es, und so ist es auch z.B. in der Vermessung allgemein üblich. Interessanterweise lernen es aber z.B. meine Kinder in der Grundschule und am Gymi genau umgekehrt, sprich z.B. 1,15 sollen sie zu 1,2 werden lassen, während ich es 1,1 werden lasse. Könnte es sein, daß unsere Meinung hier differiert?

                  Gruß Jürge

                  Comment


                  • #10
                    Hi

                    Hm, ich verstehe nicht warum Du Int() benutzt ? Diese arbeitet genauso wie Trunc(), liefert also den abgeschnittenen Ganzzahlanteil zurück, egal ob Double,Real,Extended, der datentyp spielt keine Rolle.

                    Mit:

                    X := Round(0.015 * 100) / 100 bekommst Du 0.02 raus !

                    X := Round(0.0015 * 1000) / 1000 = 0.002 usw.

                    Round, rundet bei 0.5 auf.

                    Allgemein also:

                    Result := Round(X * 10^E) / 10^E

                    10^E wird am besten mit IntPower() berechnet.

                    Gruß Hage

                    Comment


                    • #11
                      Hallo Hagen, Deinen Vorschlag habe ich einmal überprüft. Es funktioniert zwar mit dem Extended-Wert (mit ziemlicher Sicherheit werde ich auch dort Zahlen finden, die Probleme machen!) jedch nicht mit dem Double-Wert 0.015 !
                      <BR>
                      procedure TForm1.Button1Click(Sender: TObject);<BR>
                      const<BR>
                      &nbsp;&nbsp;cWert : Double = 0.015;<BR>
                      begin<BR>
                      &nbsp;&nbsp;label1.caption := FloatToStr( Round( cWert * 100 ) / 100 );<BR>
                      end;<BR>
                      <BR>
                      Resultat: 0.01<BR>
                      <BR>
                      zu Jürgen:<BR>
                      Danke für Deine Antwort, jedoch kannst Du mal bei Dir den Wert 0.025 oder 0.035 ausprobieren? Kaufmännisch wird bei 5 aufgerundet

                      Comment


                      • #12
                        <p>Hallo Michael,<br>
                        <br>
                        dein letztes Beispiel kann nicht das gewünschte Ergebnis liefern, da
                        Round(0.015 *100) = <b>1</b>.<br>
                        Wenn Du Round weglässt, kommt 0.015 heraus.<br>
                        <br>
                        Thomas<br></p&gt

                        Comment


                        • #13
                          <p>Hallo Michael,<br>
                          <br>
                          den Aufbau der Datentypen findest Du in der Delphi-Hilfe folgendermaßen:<br>
                          In der Hilfe den Reiter "Suchen" aktivieren und als Suchbegriff "<b>Reelle</b>" eingeben.<br>
                          In der Thema-Liste dann den 1. Eintrag "<b>Reelle Typen</b>" auswählen.<br>
                          <br>
                          Gruß Thomas<br></p&gt

                          Comment


                          • #14
                            Hi Michael

                            Gratulation, wie's aussieht haste einen "BUG" ?! im Delphi gefunden.
                            Also, eine initilaisierte Double Konstante mit dem Wert 0.015 wird initialisiert mit 0.01499999994, nun wird damit gerechnet MUSS natürlich 0.01 rauskommen, vergleich mal folgendes:

                            <pre>

                            const
                            C: Double = 0.015;
                            var
                            D: Double;
                            S1,S2: String;
                            begin
                            S1 := FloatToStr(Round(C * 100) / 100);

                            D := C * 100;
                            S2 := FloatToStr(Round(D) / 100);
                            end;

                            </pre>

                            wieder erwartens unterscheiden sich S1 und S2, S2 enthält korrekter weise 0.02 !!
                            Im Debugger kann man gut erkennen das die Konstante C: Double = 0.015 mit 0.014999994 initialisiert ist !!! Wird nun C als Extended definiert wird sie korrekt mit 0.015 initilisiert !!! Nun, da ich von Hause aus nur mit Extended arbeite stört mich das nicht, ABER normal kann so ein Verhalten von Double NICHT sein, konkret: die math. Formel von mir zum Runden, ist und bleibt korrekt, nur die Double-Konstanten spinnen :-)

                            Gruß Hage

                            Comment


                            • #15
                              <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">

                              <pre>Hallo Hagen,
                              </pre>

                              <p>das Verhalten von Double ist normal. Dieser &quot;ungewöhnliche&quot;
                              Effekt liegt an der Art und Weise wie Real-Zahlen gespeichert
                              werden. </p>

                              <p>An die genauen Einzelheiten kann ich mich nur noch sehr dunkel
                              erinnern (es ist schon viele Jahre her, dass ich dazu etwas
                              gelesen habe). </p>

                              <p>Auf jeden Fall wird eine Real-Zahl im wissenschaftlichen
                              Format gespeichert (z.B. 0,015 als 1.15E-2), wobei Exponent und
                              Mantisse in seperaten Teilen der Speicheradresse der Variablen
                              abgelegt werden. Die Mantisse wird immer mit einer Vorkommastelle
                              und den notwendigen Nachkommastellen dargestellt. Der
                              Nachkommateil ergibt sich aus der Addition von Bruchteilen aus
                              der Potenz von 2 (1/2 + 1/4 + 1/8 usw.). </p>

                              <p>Bei dieser Darstellungsart können natürlich nicht alle
                              Zahlen exakt widergegeben werden, dazu ein kleines Beispiel: </p>

                              <p>0,015 wird als 1.15E-2 gespeichert -&gt; Exponent wird mit -2
                              gespeichert (das ist noch einfach) </p>

                              <p>der Vorkommateil der Mantisse ist 1 (das kann ebenfalls noch
                              einfach gespeichert werden),</p>

                              <p>schwieriger wird es beim Nachkommateil 0,15. Die 0,15 ergeben
                              sich aus der Addition von 1/8 (0,125) + 1/64 (0,015625) + 1/128 (0,0078125)
                              ... jetzt habe ich aber keine Lust mehr </p>

                              <p>Auf Bit-Ebene ergibt sich daraus folgendes Bit-Muster, wobei
                              Bit 1 für 1/2, Bit 2 für 1/4 usw. steht </p>

                              <p><font color="#0080C0"><b>Bit-Muster = 0010011... </b></font></p>

                              <p>Die Genauigkeit der Annäherung hängt jetzt natürlich von
                              der Anzahl der zur Verfügung stehen Bits ab. </p>

                              <p>Ich hoffe, dass ich mich halbwegs verständlich ausgedrückt
                              und dazu auch noch richtig erinnert habe. </p>

                              <p>&nbsp;</p>

                              <pre><font face="Times New Roman">Tschüß</font></pre>

                              <pre><font face="Times New Roman">Torsten</font></pre>
                              </body>
                              </html&gt

                              Comment

                              Working...
                              X