procedure TForm1.Button1Click(Sender: TObject); var a,b:real; begin a:=1; b:=2.34; if round(b)=b then begin label2.caption:='b'; end; if round(a)=a then begin label1.caption:='a'; end; end;
Det kan du meget nemt finde ud af ved at prøve og konvertere den. Se nedenstående eksempel:
procedure TForm1.Button1Click(Sender: TObject); var tmpInt: Integer; begin Try tmpInt := StrToInt(Edit1.Text); Showmessage('Det er et heltal'); Except Showmessage('Det er IKKE et heltal'); End; end;
Hvis det der står i Edit1.Text ikke er et heltal, så bliver Showmessage('Det er et heltal'); aldrig udført og den hopper ned i Except delen af koden. (Kører du dette eksempel inde fra Delphi, vil du få en fejlmeddelelse når den hopper ned i Except delen, men den fejl bliver ikke vist hvis du bare kører .exe filen uden for Delphi!)
Og inden folk konkluderer at jeg er kuleskør, så prøv lige følgende lille program:
program testisint;
{$APPTYPE CONSOLE}
uses SysUtils;
function isint1(x : real) : boolean;
begin isint1 := (round(x)=x); end;
function isint2(x : real) : boolean;
begin isint2 := (abs(round(x)-x) < 0.00001); end;
function isint3(x : real) : boolean;
begin isint3 := (frac(x)=0); end;
procedure test(x : real);
begin writeln(x:7:5,' ',isint1(x):5,' ',isint2(x):5,' ',isint3(x):5); end;
var i : integer; x : real;
begin test(123); test(123.456); test(122.9999); test(123.0001); test(122.999999); test(123.000001); for i:=1 to 1000 do begin x := 1/i; x := i*x; if (isint1(x)<>isint2(x)) or (isint2(x)<>isint3(x)) then begin writeln(i:3,' ',x:9:7,' ',isint1(x):5,' ',isint2(x):5,' ',isint3(x):5); end; end; end.
arne_v: Hør her, hvis du vil se om et tal er helt eller ej, så behøves der altså ikke den store udvikling af den "dybe tallerken" til. Jeg arbejder normalt med 3D geometry og i den forbindelse bruger jeg altid Single - og jeg har AlDRIG haft problemer med dette. Jeg afviser bestemt ikke at din metode IKKE er korrekt, men hvorfor dælen bruge så meget hjerne-vridning for et så simpelt en procedure som denne, hvis det kun skal bruges til et eller andet simpelt!!! Jeg holder stadig på at, hvis et tal skal være helt, må alt der er til højre for kommaet være lig nul og ABSOLUT NUL - det behøves der nu ikke meget professor-viden til for at finde ud af - selv med floats! Frac afrunder desuden ikke!
if Frac(Tal) <> 0 then Result := False //Ikke Helt else Result := True;
ZeroHero (Måske har jeg misforstået noget) ;)
PS: I fleste sammenhænge (afhængigt af projektets kunnen - præcision) foretrækker jeg så simpelt kode som muligt!!! Overblik er mere værd end overkill!!! ;))
Hvis du prøver at køre mit eksempel vil du se, at man kan få problemer - endda at det er rimeligt nemt at få problemet.
Jeg er også til simple løsninger. Men til simple korrekte løsninger. En simpel løsning der kun virker i 99% af tilfældende er ikke god kode.
Og min løsning er faktisk ikke så kompleks. Mindre end 20 bogstaver mere end din løsning. 20 bogstaver er ikke slemt.
Jeg har aldrig påstået at frac runder af, men round(x)=x og frac(x)=0 er reelt samme test.
Floating point tal på computer har nogle egenskaber som afviger en del fra almindelige matematiske regler og derfor skal man være meget forsigtig med at bruge almindelig matematisk logik på dem.
Du kan godt definere at en floating point value kun er 1 hvis den har et ganske betsmt bit mønster.
Det er tanke-gangen bag både round og frac metoderne.
Men det har som mit program demonstrerer den kosekvens at (1/49)*49 ikke er 1.
Det kan give problemer - store problemer.
Mit test bygger på at en floating point value er 1 hvis den ligger i intervallet fra 1 - meget lidt til 1 + meget lidt.
Det virker umiddelbart sjusket, men lige pludselg er (1/49)*49 igen 1 og det passer altså bedre.
begin isint4 := iszero((round(x)-x, 0.00001); end;
procedure test(x : real);
begin writeln(x:7:5,' ',isint1(x):5,' ',isint2(x):5,' ',isint3(x):5,' ',isint4(x):5); end;
var i : integer; x : real;
begin test(123); test(123.456); test(122.9999); test(123.0001); test(122.999999); test(123.000001); for i:=1 to 1000 do begin x := 1/i; x := i*x; if (isint1(x)<>isint2(x)) or (isint2(x)<>isint3(x)) then begin writeln(i:3,' ',x:9:7,' ',isint1(x):5,' ',isint2(x):5,' ',isint3(x):5,' ',isint4(x):5); end; end; end.
Har lige testet dit eksempel, og jaaahhhh det virker som om at der er noget om snakken. Så snart der mere end 5 decimaler efter kommaet, så begynder det at gå knap så godt for mit eksempel ;(( Tjaaah der er jo ikke andet for end at jeg må bøje mig i støvet (snøft) - Men så blev jeg da lidt klogere - takker ;))
Det var faktisk ikke den første del jeg klandrede frac metoden for - det var den sidste del med (1/i)*i.
Skiftet fra real til single ændrer lidt på hvor fejlene kommer,men de kommer stadig.
Prøv og kør:
program testisint;
{$APPTYPE CONSOLE}
uses SysUtils;
function isint1(x : single) : boolean;
begin isint1 := (round(x)=x); end;
function isint2(x : single) : boolean;
begin isint2 := (abs(round(x)-x) < 0.00001); end;
function isint3(x : single) : boolean;
begin isint3 := (frac(x)=0); end;
procedure test(x : single);
begin writeln(x:7:5,' ',isint1(x):5,' ',isint2(x):5,' ',isint3(x):5); end;
var i : integer; x : single;
begin test(123); test(123.456); test(122.9999); test(123.0001); test(122.999999); test(123.000001); for i:=1 to 1000 do begin x := 1/i; x := i*x; if (isint1(x)<>isint2(x)) or (isint2(x)<>isint3(x)) then begin writeln(i:3,' ',x:9:7,' ',isint1(x):5,' ',isint2(x):5,' ',isint3(x):5); end; end; end.
Floating point tal på computere er repræsenteret som:
(b1*1/2 + b2*1/4 + b3*1/8 + ... + bn*1/2^n) * 2^m
Hvor b1..bn er bits (0 eller 1) i den del man kalder manissen og m er exponenten.
Og allerede nu begynder man jo at få en mistanke om at alle tal ikke nødvendigvis kan repræsenteres i den form (meget lig problem stillingen med at 1/3 ikke kan repræsenteres som et endeligt antal decimaler 0.3333333333333 og i det uendelige).
En anden måde at måde at analyser det på er at der jo åbenlyst kun 2^32 mulige floating point tal for 32 bit floating point og 2^64 for 64 bit. Smamtidift ved vi jo at der er et uendeligt antal reelle tal (i den matematiske betydning) mellem hvilket som helst 2 forskellige tal. Igen konkluderer vi at ikke alle tal kan repræsenteres eksakt som floating point på en computer.
1 kan repræsentres eksakt som 1*1/2*2^1.
49 kan repræsenteres eksakt som (1*1/2+1*1/4+1*1/64)*2^6
Men 1/49 kan ikke repræsenteres eksakt der skr en afrundings fejl og når vi ganger op med 49 igen ender vi ikke nødvendigvis på 1*1/2*2^1 men kun på noget som er tæt på.
Men hvis noget er tilpas tæt på så giver det mening at definere det som værende det samme.
Det matcher også udmærker til mange problem-stillinger i den virkelige verden.
Hvsi jeg spørger dig om du har et helt antal kilometer mellem hjem og arbejde, så måler du ikke op i millimeter. Kilometer tælleren i din bil tager kun 0.1 km og derfor bliver alt mellem 49.95 og 50.05 lig med 50.0.
Andre problem-stillinger gælder ikke det samme for. Penge f.eks.. Bogholdere og revisorer har en opfattelse af at regnskaber skal stemme absolut - små afrundings fejl er ikke acceptable. Derfor må man aldrig bruge floating point til beløb. Eller for igen at vende tilbage til problem-stillingen. Et beløb er et helt antal kroner eller ej - 50.00 kr. er ikke 49.995-50.005 kr..
>> Kommentar til arne_v 04/07-2003 23:27:25 Ifølge IEEE standarden, som nok er den mest benyttede, lægges 1 underforstået til mantissens brøk, så 1 burde reprænsenteres med lutter nuller, altså: 1,00000...0 * 2^0.
Dog benytter exponenten biased notation, hvorved den unsigned værdi fratrækkes 127 (ved 8 bit), så exponenten er derfor 01111111.
For at gøre tingene endnu mere mudret arbejder standarden med denormaliserede numre for meget små værdier, hvor det foranstillet 1-tal fjernes ved at sætte exponenten til 00000000.
Og for resten har vi helt glemt at nævne at der også er en sign-bit forrest. ;-)
(ps: jeg synes spørgeren glimrer lidt med sit fravær!*S*)
Jeg har osse erfaret at det kan være besværligt at arbejde med reel eller extended som jeg har brugt i forbindelse med nogle input felter som jeg validerer og konvereterer, helt på danskt, her vil 999,99 fx blive 999,99800021 (eller der omkring) når man looper chars igennem og ganger op, før og efter kommaet, så dethe.....
Det har arne_v da ret i men der er (6.0 ihvertilfælde) nogle funktioner i unit'en "math" til netop dette formål: samevalue, comparevalue og iszero som opererer med en epsilon værdi ligesom "megetlidt" i arne_v's eksempler. Kig i hjælp for yderlige oplysninger. Du har dog stadig brug for noget a la: if iszero(frac(i),0.00001) then //tallet er "helt" OG det var så en ny måde arne_v (som jo nok er den bedste bemærk fx at "abs" ikke er nødvendig).
Den gør nok (jeg ved ikke om der evt er nogle x87 instruktioner som kompinerer en/flere af arbejdsgangene) det samme, man kan så (nok) være sikker på at mellemresultaterne holdes i x87-registerne. Man kunne forestille sig at hvis man brugte iszero (og de andre) så ville man være sikker på at det gik hurtigst muligt. Vi skal heller ikke diskutere her det er ikke vores tråd, vi må håbe at thecokeguy har fået noget ud af vores indlæg. Jeg synes det var en meget pædagogisk måde arne_v anskuliggjorde problemmet omkring fragmenter på, det er en metode man med held kan bruge til at blive fortrolig med andre funktioner på, fx strenge (copy,leftstr,rightstr,pos,val mv), arrays og osse fil-funktioner/procedure, altså stil en masse beregninger og showmessage's op med den/de funktioner som man er usikker(e) på, start simpelt og udvid langsomt hen mod det der er ens problem, og prøv evt at komme ud til krogene og opstil så nogle generalier for dig selv som du kan huske. Venlig hilsen NOP.
Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.