Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - 1 != 1 - Problem beim Vergleich mit Double


freedy - Fr 26.01.07 17:41
Titel: 1 != 1 - Problem beim Vergleich mit Double
Hallo Leute!

Ich habe folgendes Problem. Es soll eine logarithmische Skala gezeichnet werden. Als Test und zum Probieren habe ich auf eine Form einen TButton und ein TImage gesetzt.

Hier mein Code (leider nicht Kommentiert):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function log10(Value : Double) : Double;
var
  res : Double;
begin
  Res := (ln(Value) / ln(10));
  if IsZero(Res, 0.00000001then
    Result := 0
  else
    Result := Res;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Erg : Double;
  I : Double;
  r : TRect;
  pix : Integer;
  Start : Double;
  Ende : Double;
  Step : Double;
  big : Integer;
  a, b : Extended;
begin
  Start := 0.1;
  Ende := 100;
  R.Right := Image1.Width;
  R.Bottom := Image1.Height;
  Image1.Picture.Bitmap.SetSize(Image1.Width, Image1.Height);
  with Image1.Picture.Bitmap.Canvas do
  begin
    FillRect(R);
    MoveTo(0, Image1.Height div 2);
    LineTo(Image1.Width, Image1.Height div 2);
  end;

  I := Start;
  Step := Start;

  big := Trunc((Image1.Width - 10) / (log10(Ende) - log10(Start)));

  while (I <= Ende) do
  begin
    Erg := log10(I);
    ListBox1.Items.Add(FloatToStr(Erg));

    pix := Trunc(big * (Erg - log10(Start)));
    with Image1.Picture.Bitmap.Canvas do
    begin
      MoveTo(pix + 5, Image1.Height div 2 - 5);
      LineTo(pix + 5, Image1.Height div 2 + 5);


      a := log10(I);
      b := Trunc(a);
      if (a = b) then
      begin
        TextOut(pix + 2, Image1.Height div 2 + 8, FloatToStr(I));
        Step := I;
      end;
      I := I + Step;
    end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Image1.Picture := TPicture.Create;
end;



Damit kann man beliebig rumspielen. Das tolle ist, er zeichnet alle Werte an die Skala, solange der Startwert nicht unter 1 geht. Warum?
Mit diesem Source erkennt er nicht, dass 1 = 1 sein soll, wenn die 10 gezeichnet werden soll. Ich weiß echt nicht weiter.

Gruß


Gausi - Fr 26.01.07 17:45

Real-Typen sollten aufgrund der ungenauen Darstellung im Rechner niemals mit = verglichen werden. Du hast in deinem Code eine Funktion IsZero drin. Ich würde mal den Vergleich a=b durch IsZero(a-b,0.000001) ersetzen, dann sollte es gehen.


freedy - Fr 26.01.07 17:50

Zitat:
Real-Typen sollten aufgrund der ungenauen Darstellung im Rechner niemals mit = verglichen werden. Du hast in deinem Code eine Funktion IsZero drin. Ich würde mal den Vergleich a=b durch IsZero(a-b,0.000001) ersetzen, dann sollte es gehen.


Schon ausprobiert. Klappt nicht, da b den Wert 0 annimmt, während a näherungsweise 1 ist. IsZero war also keine Lösung.

Die Zeilen, wo a und b "berechnet" werden, sollten ja verständlich sein. Ich prüfe, ob der Log-Wert eine Zahl ohne Nachkommastellen ist.

Kennt da vielleicht noch jemand eine andere Lösung?


Gausi - Fr 26.01.07 17:58

Dann probier mal, ob du mit Frac weiterkommst. Das liefert die Nachkommastellen. Wenn die fast 0 sind, oder 1-frac fast 0, dann bist du sehr nahe an einer ganzen Zahl dran.


freedy - Fr 26.01.07 18:04

Das kann nicht funktionieren.

Das Problem sind doch die Nachkommastellen, die Trunc() abschneidet. Das Programm rechnet ja richtig. log(10) ist 1. Jetzt schneide ich von diesem Wert aber die Nachkommastellen ab und weise ihn b zu. Da der Wert aber nicht genau 1 war, ist b = 0. Er rechnet aber mit a = 1 weiter. Natürlich sind die beiden nicht gleich, so dass er logischerweise nicht in meine if-Schleife läuft. Frac() liefert mir leider aber genau 0 zurück.



Edit:

Damit klappt es jetzt doch:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
  //....

  if (frac(log10(I)) = 0or IsZero(1 - frac(log10(I)), 1e-10then
  begin
    TextOut(pix + 2, Image1.Height div 2 + 8, FloatToStr(I));
    Step := I;
  end;

  //....


Gruß


Gausi - Fr 26.01.07 18:09

Deswegen hab ich ja Frac vorgeschlagen. Wenn die Nachkommastellen .0000001 sind oder .999999, dann ist man sehr nahe bei einer ganzen Zahl und geht in die Schleife rein. Oder verstehe ich dich immer noch nicht richtig?

Edit: Hab dein Edit nicht gelesen ;-)


BenBE - So 28.01.07 16:58

user profile iconfreedy hat folgendes geschrieben:
Das kann nicht funktionieren.

Das Problem sind doch die Nachkommastellen, die Trunc() abschneidet. Das Programm rechnet ja richtig. log(10) ist 1. Jetzt schneide ich von diesem Wert aber die Nachkommastellen ab und weise ihn b zu. Da der Wert aber nicht genau 1 war, ist b = 0. Er rechnet aber mit a = 1 weiter. Natürlich sind die beiden nicht gleich, so dass er logischerweise nicht in meine if-Schleife läuft. Frac() liefert mir leider aber genau 0 zurück.


Es gibt keine If-Schleifen [http://www.if-schleife.de]!!!

Zu deinem Problem: Probier mal Round statt Trunc.


freedy - Mo 29.01.07 01:23

user profile iconBenBE hat folgendes geschrieben:


Es gibt keine [url=http://www.if-schleife.de]If-Schleifen[/url]!!!


Nein, natürlich nicht. "verschrieben"! Soll jetzt keine Ausrede sein. Fehler ist natürlich Fehler. Das "if" habe ich nachträglich zugefügt.

user profile iconBenBE hat folgendes geschrieben:

Zu deinem Problem: Probier mal Round statt Trunc.


Hat sich alles schon erledigt. Mit Round klappte es übrigens gar nicht.

Gruß