12. maj 2007 - 17:25Der er
17 kommentarer og 1 løsning
Fysikens love bryder sammen :-D
Jeg skulle lige lave en hurtig test på Number() funktionen og skrev et simpelt script der lyder:
var txt = "11.05"; var floatval = Number( txt ); window.alert( floatval - 1 );
vinduet der popper op siger 'selvfølgelig' "10.05", for dét er trods alt regnestykkets resultat. MEN så var det at jeg rettede scriptet til at sige:
var txt = "11.05"; var floatval = Number( txt ); window.alert( floatval - 4 );
Og nu kan den pludselig ikke regne .. hér er hvad den påstår 11.05 - 4 er ..: 7.050000000000001 !! Beder jeg den om at trække 8 fra bliver resultatet 3.0500000000000007 ... værre og værre. Jeg vil hermed gerne have fysik-lærebøgerne omskrevet, for tilsyneladende KAN man få noget ud af intet ;-) Nogen der kan sige noget om hvad der foregår siden så simpel matematik slår fejl?
Ja det er ikke fordi det er et problem som sådan at omgåes, jeg undrer mig bare over at min computer -eller JSP parseren- ikke kan regne :-D Men betyder det at jeg generelt skal lave én beregning på heltallene, én på decimalerne, og ud fra de to beregne det faktiske resultat, frem for at lave beregninger af den type direkte i javascript? Hvor svært kan 11.05-4 være? ;-)
Det er faktisk svært, for du kan ikke skrive 0.05 som en sum af 0 eller 1 halv, 0 eller 1 kvart, 0 eller 1 ottendel, osv, svarende til de bits der står i din computer for kommadelen (og med et endeligt antal bits).
De 2 anbefalinger du har fået er at regne på heltal 1105-400 og skrive ud med komma det rette sted. Eller regne som nu, men udskrive afrundet.
Det hele bunder i hvordan maskinen (hardwaren) repræsenterer de reelle og rationale tal. De fleste maskiner i dag implementerer IEEE 754 standarden hvor en float eller double repræsenteres ud fra en sign-bit, en significand og en exponent:
(-1)^sign * (significand) * 2^exponent
en float har 8 bits til exponenten, 23 bits til significanden og 1 bit til sign'et.
Fordi der kun er et begrænset antal bits til at gemme de forskellige reelle og rationalle tal er det langt fra alle tal som kan repræsenteres på maskinen som en float eller double. Og det er netop her sådanne fejl som du får ovenover popper frem.
For hver aritmetisk operation du udfører mellem to floats eller doubles kan der opstå afrundingsfejl hvis resultatet (eller mellem-resultaterne) ikke kan repræsenteres ved hjælp af de begrænsede antal bits og den formel ovenover. Det er så dog muligt at implementere en arbitrær præcisions datatype som giver eksakte resultater, datatypen bliver så desværre ca. 100 gange langsommere til at udføre aritmetiske operationer end ved normale hardware operationer.
De her datatyper er selvfølgelig ikke helt trivielle at implementere men der eksiterer mange biblioteker derude som indeholder datatyperne, f.eks. GMP, LEDA, CGAL eller CORE. Java har også en BigDouble datatype med arbitrær præcision.
Så det betyder det intet med javascript har at gøre, du kan bare ikke regne med at dine resultater bliver helt eksakte når du bruger en floating-point datatype. Det er derfor også helt vitalt at alt software til banker osv bruger datatyper med arbitrær præcision, ellers kunne vi ikke være sikre på at beløbet på vores konti var det eksakte beløb. ;)
Så hvis du vil have eksakte resultater uden brug af arbitrær præcisions datatyper må du udføre heltalsberegninger (int). :)
jaja, så blev man da dét klogere :) Syntes stadig der er lidt overraskende at en sådan ekstra implementering af biblioteker for præcise beregninger er påkrævet. Sådan i betragtning af at beregninger li'som er hvad en computer gør :-D
Ja exponenten er bias'ed, dvs ved float skal der trækkes en bias på 127 fra eksponenten (1023 for doubles) og dermed har du dine negative eksponenter og dermed dine tal under 1. :)
Hvad endian angår er det forskelligt fra hardware til hardware om floaten er repræsenteret ved little eller big endian formen. :)
Jeg skal lige have på det rene ... det hér JSP er jo kun på clientsiden, og hér skal der ikke foregå nogen nævneværdig beregning, omend ".05" problemet sagtens kan opstå .. altså hvis jeg ikke deler beregningen op, hvilket jeg selvfølgelig gør ;) Men hvad så når tallene er sendt til serveren, lagt i database og sidenhen skal bruges i beregninger .. skal jeg så også i PHP være opmærksom på dette? Syntes stadig det er lidt spøjst at man skal tage særskilt hensyn til det i JSP.
JSP er helt misforstået, for det betyder Java Server Pages og er en arkitektur, der benytter Java til at lave dynamiske websider fra en server, så det har ikke det fjerneste med klienten at gøre ...
Typisk vil man benytte javascript på klienten for at udføre noget dynamisk, men javascript og Java har cirka 4 ting til fælles: j - a - v - a !-)
-- og php bruger præcis den samme måde at beregne på, men har så en hel del flere funktioner indbygget, så der kan du finde en round-funktion, der direkte kan afrunde et tal til et bestemt antal decimaler, hvis du skal bruge en sådan i javascript, skal du skrive eller hente den selv !o]
Som jeg også skrev tidligere så har afrundingsfejlene ved floating-point aritmetik intet med script eller programmeringssprog at gøre. Fejlene opstår i hardwaren når der afrundens til et tal maskinen kan repræsentere. Det betyder at når du gemmer en eksakt værdi (udregnet i heltal i javascript) i databasen som en float eller double sker der allerede en afrundingsfejl i konverteringen (for at tallet kan gemmes). Så næste gang du hiver talet ud af databasen kan du ikke regne med det er eksakt mere.
Dette fænomen er noget alle programmører må døje med i ALLE sprog, men i de fleste tilfælde har vi ikke brug for eksakte resultater (fejlene er jo meget små), det er kun når vi involverer penge eller f.eks. EGC (exact geometry computation) det bliver et problem. Altså når man f.eks. skal finde skæringspunkter mellem to eller flere trekanter, i sådanne tilfælde kan der nemlig opstå tvetydigheder mellem trekanternes skæringspunkter der i værste tilfælde kan få algoritmen til at fejle.
Jeg vil da råde alle programmører som ikke kender til afrundingsproblemet for floating points til at læse mere om emnet så man ihvertfald er klar over begrænsningerne. :) Et godt sted at starte er:
Eller den gamle udgave af documentet skrevet af David Goldberg (jeg har ikke læst den nye udgave som jeg refererer til ovenover, men læste den gamle under studietiden).
Og denser80, for at sikre at dine resultater forbliver eksakte (hvis det er meget vigtigt) kan du f.eks. gemme din tal som opsplittede integers i databasen? Eller som roenving foreslår, "gemme" fejlen lidt ved at afrunde til f.eks. 2 decimaler. :)
roenvig: "JSP er helt misforstået, for det betyder Java Server Pages og er ... " det var jeg egentlig godt klar over :) det er bare blevet en 'dårlig' vane med P'et til sidst ;)
crazysnap: Som sådan er jeg godt klar over .. nu :) hvor 'fejlen' er, og jeg kan til dels forstå det med beregninger på polygoner osv. som du siger, men ... ja jeg er stadig bare lidt overrasket over at sådan fejl er så nem at fremprovokere. Selvom javascript ikke er tænkt som et highlevel techno sprog. Men ja .. så har jeg da lært dét ;-D Så mangler der bare at javascript kan manipulere tyngdeaccelerationen .. jeg mener ... når nu man kan få noget for intet ;-)
Ja, her har du et svar og held og lykke med det! :)
Mvh.
- Snap
Synes godt om
Ny brugerNybegynder
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.