Precyzja liczb zmiennoprzecinkowych i dziwny problem.

0

Witam wszystkich.

Mam pewien kłopocik odnośnie precyzji liczb typu Real. Zaznacze na wstępie że używam Delphi 4 Standard. W helpie jest informacja że liczy te są zapisywane z precyzją 15-16 cyfr ... co to tak naprawde znaczy ? Że raz jest 15 a raz 16 - ale kiedy ? Pisze pewien większy program i ostatnio nie mogłem wyjaśnić pewnej sprawy. Wynik ścisłego obliczenia był różny z zależności od tego czy wszceśniej uruchomiłem przeglądarkę czy nie. Obliczenia są proste, chodzi o pole powierzchni metodą Gauss'a - jest to bardzo szybka metoda - dlatego chciałem jej użyć. Problem w tym że operuje na współrzędnych punktów takich jak: 5683234,123 a we wzorze występuje iloczyn współrzędnych : ). Ale do rzeczy, dołączam źródło programu napisanego na potrzeby demonstracji problemu. U mnie na komputerze program działa następująco:

  • po uruchomieniu naciskam przycisk Button1 i otrzymuje wynik:
    26.702977180481
  • następnie naciskam Button2 i znowu Button1 i otrzymuje wynik:
    26.697265625

(Button2 uruchamia przeglądarke)

Ma to być pole trójkąta liczone ze współrzędnych. Pierwszy wynik jest poprawny drugi jest taki jakby liczyć go ze zmnieszoną precyzją (do 15 cyfr). Ale dlaczego tak się dzieje ???

Pozdrawiam i z góry dziękuje za odpowiedzi (szczególnie za wyniki jakie otrzymacie).

Kod programu:

unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Edit1: TEdit;
	Button1: TButton;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
	procedure FormCreate(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
	{ Private declarations }
  public
	{ Public declarations }
  end;

var
  Form1: TForm1;
  X1, X2, X3, Y1, Y2, Y3 : Real;

implementation

{$R *.DFM}


procedure TForm1.Button1Click(Sender: TObject);
begin
Edit1.Text := Floattostr(0.5*Abs(X2*Y3 + Y2*X1 + X3*Y1 - Y3*X1 - Y2*X3 - Y1*X2));
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
X1 := 5682743.89;
Y1 := 3812665.525;
X2 := 5682758.442;
Y2 := 3812668.981;
X3 := 5682744.589;
Y3 := 3812662.021;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
ShellExecute(Application.Handle,'open','http://4programmers.net',nil,nil,SW_MAXIMIZE);
end;

end.
0

a jak dasz extended to tak samo?

0

Jak dam extended to jest drugi wynik też zły ale inny : ) :
26.69921875

No i co to sie dzieje ?

Pomyślałem że może dołącze exe'ca jak sie komus nie chce kompilować kodu.
http://ge0dezja.republika.pl/files/project1.exe

0

Spróbuj posprawdzać wyniki cząstkowe i zobaczyć gdzie wyniki zaczynają się różnić od oczekiwanych. Masz dużo mnożeń liczbco może owocować błędami zaokrągleń które się zsumują. Przejście na większe rozmiarowo typy zmiennych może być przydatny. Pamiętaj że generalnei nie uzyskasz idealnego wyniku (w specyficznej syuacji tak). Problem leży w sposobie reprezentacji liczby - nie może ona przyjmować wszystkich stanów - a ma pewien skok. Jeśli dałoby radę spróbuj pomnożyć przez tysiąc współrzędne i liczyć na longintach, albo int64. Jak nie przekroczysz zakresu nie powinno być zniekształceń.

0

Witam

Dzięki za odpowiedzi ale nie do końca o to pytałem. Jak się liczy tak w zaokrągleniu to niech będzie. Ja sprawdzałem, zły wynik jest taki jakby liczyć z dokłądknością 15 cyfr znaczących. Ale moje pytania są następujące: Co oznacza że typ Real ma precyzje 15 - 16 cyfr ? I dlaczego raz liczy dobrze a raz liczy źle ???

Pozdrawiam

0

Hmm .. dość ciekawy temat. Może ma to związek z błędem przybliżenia i że nie zawsze prostą liczbę zmiennoprzecinkową da się zapisać w Real czy Extended, np. 0,1 nie da się umieścić jednoznacznie w 32 bitach, a niektóre inne da - może ma to właśnie związek z tą dokładnością do 15- 16 cyfr po przecinku.. ale to są tylko moje spekulacje, tak naprawde nie wiem czemu tak jest - bym musiał poznać dokładną budowę typu Real i jego upakowanie.

A z tą zmianą wyników po wywołaniu funkcji ShellApi to naprawdę nie mam pojęcia..

0

Taki banał, a wy się głowicie.

Daj przed operacją:

Set8087CW(Default8087CW);

Będzie po sprawie. Zachęcam do zapoznania się szczegółowego z budową jednoski matematycznej.

0

A mógłbyśpowiedzieć co to za polecenie, co ono robi i czemu mi nie działa (nie pomaga)?

0

typ real w paskalu jest be!
używać double.

0
beeeeee napisał(a)

typ real w paskalu jest be!
używać double.

real w Delphi to to samo co double.

0

Kto nie wie, niech poczyta (a warto na przyszłość) ;)

http://www.delphiqa.org.pl/techniques/qa067.html

1 użytkowników online, w tym zalogowanych: 0, gości: 1