Avatar billede thomas_theis Nybegynder
14. juni 2004 - 12:02 Der er 25 kommentarer og
1 løsning

floattostr, real og single

Kan nogen forklare mig hvorfor følgende lille konvertering tilsyneladende regner forkert. Mig giver det ingen mening.

procedure TForm1.Button1Click(Sender: TObject);
var x : real; y : single;
begin
  randomize;
  x:=random(1000)/1000;
  edit1.Text:=floattostr(x);
  y:=strtofloat(edit1.text);
  edit2.Text:=floattostr(y);
end;

Jeg har blot lavet en form med 2 editfields og en enkelt knap. Sagen er den at jeg er ved at lave et (andet) program hvor jeg skal kunne indtaste en værdi i et editfield. Dette svar SKAL oversættes til en SINGLE! Men hver gang jeg forsøger at gøre det sker der det at der bliver hæftet en masse decimaler på mit resultat - decimaler som jeg ikke vil have der. Det hele fungerer fint hvos jeg lader både x og y være real, men ikke hvis, som i eksemplet y er en single. Hvorfor opfører den sig dog som den gør....?
Avatar billede arne_v Ekspert
14. juni 2004 - 12:11 #1
Kan du give et eksempel på værdier ?
Avatar billede thomas_theis Nybegynder
14. juni 2004 - 12:31 #2
Ja da. 0,041 bliver til 0,0410000011324883. 0,814 bliver til 0,814000010490417 osv.
Avatar billede hreiff Nybegynder
14. juni 2004 - 12:51 #3
Når du arbejder med Single har du kun 7-8 betydende cifre, dvs alt efter ciffer 7-8 kan du ikke bruge til noget.
Hvis du ændrer Single til Double har du 15-16 betydende cifre, og så får du samme tal i dine to edit-box'e.
Avatar billede stefmeister Nybegynder
14. juni 2004 - 12:53 #4
prøv med

trunc(floattostr(y));
Avatar billede arne_v Ekspert
14. juni 2004 - 12:56 #5
Prøv:

editn.Text := FloatToStrF(amount1, ffFixed, 5, 3);
Avatar billede hreiff Nybegynder
14. juni 2004 - 12:57 #6
fortsat:
StrToFloat arbejder med double og skriver derfor 16 cifre ud.
Hvis du vil have en 'pæn' udskrift skal du bruge FloatToStrF(y,ffFixed,8,7);
(8 betydende cifre, 7 tal efter komma)
(Så får du også nogle pæne tal i dit eksemple, f.eks 0.0410000)
Avatar billede thomas_theis Nybegynder
14. juni 2004 - 13:00 #7
Alle tiders! Det ser ud til at fungere fint! Tak for det.
Avatar billede arne_v Ekspert
14. juni 2004 - 13:03 #8
Så vil jeg ligge et svar
Avatar billede arne_v Ekspert
14. juni 2004 - 13:03 #9
Øh ????
Avatar billede thomas_theis Nybegynder
14. juni 2004 - 13:04 #10
Nu er jeg ikke med....
Avatar billede arne_v Ekspert
14. juni 2004 - 13:06 #11
Er det ikke lidt usædvaneligt at give point til nummer to som har foreslået
en løsning ?
Avatar billede thomas_theis Nybegynder
14. juni 2004 - 13:09 #12
Tjoh, men nu var det jo hans udgave som jeg endte med at bruge. Derfor var det vel fair nok at det var ham der fik pointene. Er det i modstrid med god tone her på forummet? Hvis ja, så vil jeg da lave det om. Det var ikke min mening at træde nogen over tæerne.
Avatar billede arne_v Ekspert
14. juni 2004 - 13:12 #13
Det var mit indtryk at du brugte FloatToStrF ??
Avatar billede hreiff Nybegynder
14. juni 2004 - 13:25 #14
arne v: Di kan da godt få nogle af pointene, men så vidt jeg kan se, så svarede du ikke på spørgsmålet (Hvorfor virker det ikke ?)
Avatar billede arne_v Ekspert
14. juni 2004 - 13:32 #15
Der er jo ikke nogen som har givet en forklaring på det !

Forklaringen er vel at at der med strtofloat konverteres fra single
til extended og at 0.041 ikke kan repræsenteres eksakt i floating point
typer og derfor er single 0.041 det samme tal som extended (eller double)
0.0410000011324883 mens extneded (elle rdouble) 0.041 er et andet tal.
Avatar billede arne_v Ekspert
14. juni 2004 - 13:35 #16
Og hvis du mener at forklaringen er vigtigere end koden må du meget gerne
tage konsekvensen her:
  http://www.eksperten.dk/spm/505009
Avatar billede hreiff Nybegynder
14. juni 2004 - 14:34 #17
arne v:
Jeps, eller at du har 64 tilfældige bit hvor du kun overskriver de 32 første, og derfor ikke kan regne med mere end de 32 første, hvilket jo ikke er så underligt.
Det var sådan set det jeg svarede kl. 12:51 altså før dit svar.

I spg. 505009, var det et svar og ikke en forklaring der blev bedt om. Men vi kan godt blive enige om at det ikke skulle have været 200p men nok 15 eller 30, så hvis du er sur over det, må jeg jo give dem retur til debushatta.
Iøvrigt virker swap fint - resultatet er fuldstændig det samme som den funktion jeg først lavede, både på positive og negative tal. Prøv selv at læse bits.
Og tror du iøvrigt ikke at swap-funktionen er der som standard fordi det er noget man bruger tit ? f.eks i forbindelse med kommunikation mellem windows og unix ?

Kom gerne med nogle kommentarer - jeg er villig til at dele ud af pointene til de rette personer.
Avatar billede arne_v Ekspert
14. juni 2004 - 14:52 #18
Du gav ikke den rigtige forklaring 12:51 og du har lige fortalt i din 14:34
kommentar at du ikke engang har forstået svaret efter at jeg har skrevet det.
Avatar billede hreiff Nybegynder
14. juni 2004 - 15:37 #19
Afvigelsen er for stor til at den kan stamme fra forkert tolkning - det må være fordi bittene ikke bliver sat.
Og iøvrigt er fejlen som jeg skrev, at man kun kan regne med 7-8 betydende cifre på single variablen.
Avatar billede arne_v Ekspert
14. juni 2004 - 21:14 #20
Konverteringen fra single til double virker perfekt. I Delphi og
i andre sprog. Ingen tab af præcision.

Hvis man flytter de 32 bit i en single over i en double og ligger nul bytes ind for
resten så får man slet ikke noget der ligner 0.041, så den hypotese er noget vrøvl.

Problemet er som jeg skrev at 0.041 ikke kan repræsenteres eksakt i
floating point. En single kan ikke indeholde værdien 0.041 eksakt. Den
indeholder 0.0410000011324883 og et uendeligt antal cifre derefter.

Derfor virker konverteringen perfekt. Men man skal forstå floating point
for at kunne se logikken i det.

Så din forklaringen i din kommentar 14:34 er forkert - og dit svar 12:51 bemærker jo
bare det som der allerede står i spørgsmålet nemlig at single ikke virker men at
double gør det og at man ikke kan have mere end 7-8 betydende cifre i en
single, hvilket ikke forklarer hvorfor 0.041 giver problemer (da der kun er
2 betydende cifre).

Og jeg kan stadigvæk ikke se logikken i at få point for løsnings forslag
man ikke er først med. Og heller ikke for forkert forklaring.
Avatar billede hreiff Nybegynder
14. juni 2004 - 21:51 #21
Man kan sagtens repræsentere en singleværdi i en double - Det er kun et spørgsmål om at runde af det rigtige sted. Prøv bare at se (via en string):

function singleTodouble(s: single):double;
begin
  result := strtofloat(floattostrF(s,ffExponent,7,3));
end;

var
  s: single;
  d: double

s := 0.041;
d := singletodouble(s);
edit1.text := floattostr(d);

som giver: 0.041 !!!

Altså kan man godt repræsentere 0.041 i en double.
Der er bare ikke en nem måde at gøre det på.
Egentlig burde man jo kunne typecaste, men det er der altså ikke mulighed for.
Avatar billede arne_v Ekspert
14. juni 2004 - 22:19 #22
1)  Jeg har hele tiden sagt at man kan konvertere perfekt fra en single til en double.

2)  Den kode konverterer usædvaneligt dårligt fra single til double. Den virker
    kun for tal med 3 decimaler og ændrer tallets numeriske værdi.

3)  Hvis du i et sprog der kan typecaste assigner en single med 0.041 til en
    double, så får du lige præcis 0.0410000011324883 også selvom du explicit
    type caster. Fordi det er det korrekte resultat.
Avatar billede hreiff Nybegynder
15. juni 2004 - 07:56 #23
Ok, det er rigtigt at du ikke kan repræsentere 0.041 eksakt med en single, men fejlen ligger i tolkningen. Fordi nærmeste brugbare værdi hedder 0.0410000011324883 er det ikke ensbetydende med at man skal overføre samtlige 16 cifre når man konverterer til double, når man nu ved at man højest kan regne med de første 8.
Altså bør 0.041 repræsenteret i single som overføres til double vises som 0.041000000000000001 og ikke som 0.0410000011324883 (selvom der reelt foretages en afrunding).
I virkeligheden betyder det bare at FloatToStr er ubrugelig og man er nødt til at benytte FloatToStrF, fordi man så er tvunget til at overveje nøjagtigheden.
Det kom vi jo også begge to frem til, og der var du rigtig nok hurtigst.
PS. Du kender vist ikke FloattostrF - Det viste eksempel giver 7 decimaler, hvilket er hvad man ihvertfald kan være sikker på passer (3 tallet er eksponenten)
Men jeg skal nok oprette et spørgsmål, så du kan få de point.
Avatar billede arne_v Ekspert
15. juni 2004 - 08:58 #24
Floating points er ikke decimale men binære.

Den har taget præcis de binære cifre der er i singlen og flytttet til doublen.

Helt svarende til 0.041 -> 0.041000 decimalt.

Men fordi 0.041 ikke kan repræsenteres eksakt og fordi floating point er binære og
fordi binære og decimale tal systemer kører meget skævt i forhold til hinanden,
så ser man det man ser.

Og computeren kan altså ikke tillade sig at begynde at gætte på hvad
programmøren mener. Den laver en eksakt konvertering fra single til double.
Den kan ikke begynde at tænke "hmm - den her single ligger meget tæt på det her
meget pæne runde tal - lad mig prøve at finde en double som matcher det bedre".
Avatar billede arne_v Ekspert
15. juni 2004 - 09:00 #25
Floating point tal er designet til videnskabelige beregninger - og de er ikke
optimale til ret meget ud over det.

Hvis man synes at der er forskel på 0.041 og 0.0410000011324883 så skal
man nok overveje en anden data type.
Avatar billede arne_v Ekspert
15. juni 2004 - 09:04 #26
3 tallet er antal decimaler og jeg valgte det fordi de 2 angivne eksempler
havde netop 3 decimaler (jeg fik så ikke rettet variabel navnet fra hvor
jeg havde copy pastet koden fra ...).

Man kan ikke være sikker på 7 decimaler med single. Man kan være sikker på 7
betydende cifre, hvilket er noget helt andet.

Jeg programmerer normalt slet ikke i Delphi.
Avatar billede Ny bruger Nybegynder

Din løsning...

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.

Loading billede Opret Preview
Kategori
Kurser inden for grundlæggende programmering

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester