Løjerlig regnefejl i lommeregneren til Android 5.0 Lollipop

Illustration: Android Logo
Den indbyggede lommeregner til den seneste version af Googles mobile styresystem giver forkerte resultater i nogle enkelte, spøjse tilfælde.

Der er ikke tale om en stor regnefejl, men alligevel. Den seneste opdatering til Googles Android styresystem burde kunne gengive selv små decimaler korrekt, men i nogle tilfælde begår den indbyggede lommeregner en mindre fejl.

Giver man den regnestykket 30,7 - 30,6, bliver resultatet ifølge lommeregneren 0,0999999999, hvor de fleste andre lommeregnere ville have givet svaret 0,1. Fejlen er beskrevet af mobilbrugeren Leandro Paratella i et blogindlæg.

Det kan diskuteres, hvor forkert resultatet er, eftersom det også ville blive 0,1, hvis man runder op til en decimal, men det løjerlige er, at hvis man eksempelvis beder lommeregneren om at udregne 40,7 - 40,6, så bliver resultatet 0,1.

På samme måde er der også mindre og spøjse afvigelser i andre regnestykker i lommeregneren. Mens den får 90,7 - 90,5 til at blive 0,2, bliver resultatet af 90,7-90,6 gengivet som 0,1000000000.

En bruger har i en kommentar til den norske netavis Digi.no pointeret, at Android lommeregneren ikke afrunder korrekt. Som andre lommeregnere benytter den sig af det binære talsystem til udregningerne, mens resultatet bliver gengivet i 10-talssystemet. Det giver nogle enkelte situationer, hvor resultatet ikke kan omregnes korrekt mellem talsystemerne på samme måde, som ⅓ i brøker ikke kan gengives præcist med decimaler, men bliver til en uendelig lang talrække af 0,33333. Hans argument er, at det er umuligt at skrive 30,7 og 30,6 i 2-talssystemet. Fejlen i Android lommeregneren består derfor i, at den ikke afrunder decimalerne korrekt.

Tips og korrekturforslag til denne historie sendes til tip@version2.dk
Følg forløbet
Kommentarer (36)
sortSortér kommentarer
  • Ældste først
  • Nyeste først
  • Bedste først
Torben Mogensen Blogger

Så vidt jeg kan forstå, regner lommeregneren rigtigt nok (indenfor den præcision, kommatal i IEEE-standarden har). At vise resultatet af 30.7 - 30.6 som 0.09999999... er formentligt mere eksakt i forhold til det beregnede binære kommatal end at vise det som 0.1, så "fejlen" er, at der afrundes til det nærmeste tal, der kan repræsenteres som en decimalbrøk, og ikke til et tal længere væk, der tilfældigvis kan vises med færre cifre. Det er vel ikke egentlig en regnefejl?

Subtraktion af to næsten-ens tal er altid et problem for nøjagtigheden af flydende-kommatal, så det er her man mest tydeligt ser unøjagtigheder. Ved regnestykket 30.7-30.6 "forsvinder" 8-9 bit af præcisionen, så med et udgangspunkt i en 52-bits mantisse (double precision IEEE standarden), ender man med "kun" 43-44 bits nøjagtighed svarende til ca. 13 decimale cifre.

Ideelt set bør lommeregnerapplikationer (ligesom "rigtige" lommeregnere) regne i decimalbrøker i stedet for i binære talbrøker. Præcisionen bliver generelt set ikke bedre, men fastkommatal (som f.eks. kroner og øre) bliver adderet og subtraheret eksakt.

Ask Hjorth Larsen

30.7 - 30.6 med dobbeltpræcision bliver 0.09999999999999787 som Googles lommeregner derefter trunkerer når den viser resultatet. De fleste lommeregnere afrunder i stedet for at trunkere, hvilket giver det mere læsevenlige og tilfældigvis eksakte resultat 0.1.

40.7 - 40.6 bliver 0.10000000000000142, og hvadenten man trunkerer eller afrunder, vil man dermed få vist 0.1.

Så det kan koges ned til at Googles lommeregner trunkerer i stedet for at afrunde sine 64-bit flydende kommetal.

Man kan kalde det en fejl fordi det ikke er matematisk eksakt, men det er med vilje, at programmerne opfører sig som de gør.

"Fejlen" kan ses pâ en normal lommeregner ved at fratrække 0.1.

Nikolaj Reibke

Det ikke en fejl det er blot en feature til at lære nyt, Så begynder folk jo at undersøge hvordan binære tal virker, og så blev folket lige pludselig lidt klogere på noget de ellers ikke ville tænke over.

Jn Madsen

Hvis man lige tager sølvpapir - "det er jo næsten rigtig, og det er jo et udslag af de geniale binære algoritmer, som folk virkeligt bude interessere sig for"- hatten af ..... så er det forkert.
Og det er noget møg.

Om x antal år,- så er der en eller anden der løser problematikken på en eller anden smart måde. Derefter vil alle grine af "dengang folk ikke tændte lyset på 1'ste sal,- og levede med sådan noget pjat".

Torben Mogensen Blogger

Eller '%'. Prøv f.eks '100+20,25%' i spotlight. Resultatet bliver '100,2025'.

Er det egentlig en fejl? Procent betyder "per hundrede" og angiver antallet af hundrededele. Så 20,25% er 20,25 hundrededele eller 0,2025.

Det er dog rigtigt, at visse lommeregnere regner X+Y% ud som X + (XY/100) for at efterligne dagligdags tale: "234 kr + 25% moms er 292,50 kr". Om det er mere rigtigt er et definitionsspørgsmål. Jeg ganger altid med 1,25, når jeg skal lægge moms til et beløb. Så er jeg sikker på, at jeg får det rigtige.

Theis Blickfeldt

Man kunne jo evt. også tilføje en funktionalitet i det library man laver sin lommeregner i, som ved + og - automatisk afrunder til den største præcision, som indgår i de to operander. I eksemplet her benyttes tallene 30.7 og 30.6, hvor den største præcision er 1 decimal -> så afrunder man også til 1 decimal. Det burde give et mere fornuftigt resultat.

Jakob Damkjær

Prøv f.eks '100+20,25%' i spotlight. Resultatet bliver '100,2025'.

men hvis du taster "100%+20,25%" får du det korrekt. Mixed types er noget der kræver en ret stor indsats at få til at virke så alle er glade for alle adfærd.

På Wolfram Alpha har de lavet den indsats http://www.wolframalpha.com/input/?i=100%2B20.25%25

iøvrigt en ret fed app de har (som ikke falder i den der mixed type problemstilling som søgefeltet gør)

https://itunes.apple.com/us/app/wolframalpha/id334989259?mt=8

Per Erik Rønne

Finn Aldring,

Når du har slået 'dansk' til i iOS eller OSX, så markerer ',' decimalbrøkens begyndelse, mens '.' ignoreres i input; den skal jo blot markere bundter af 3 cifre. På AmE er det omvendt.

Så på dansk har du at:

30.7 - 30.6 = 307 - 306 = 1,

hvilket er helt korrekt.

Troels Henriksen

Hvis man lige tager sølvpapir - "det er jo næsten rigtig, og det er jo et udslag af de geniale binære algoritmer, som folk virkeligt bude interessere sig for"- hatten af ..... så er det forkert.
Og det er noget møg.

Om x antal år,- så er der en eller anden der løser problematikken på en eller anden smart måde. Derefter vil alle grine af "dengang folk ikke tændte lyset på 1'ste sal,- og levede med sådan noget pjat".

Man kan allerede løse dette på mange enkle måder. Den enkleste ville nok være at udføre beregninger i decimal fixed point, da det giver resultater der for de fleste brugere nok er mere intuitive, omend de strengt taget ikke er mere "korrekte", og væsentligt langsommere at udføre for en maskine. Da lommeregneren på en telefon ikke just er et ydelseskritisk beregningsprogram, så kan det måske godt være besværet værd.

Janus Knudsen

Stor fejl, at så nogen finder det morsomt at påstå det er korrekt og kommer med en lang udredning af datatyperne gør det kun endnu mere latterligt forkert.

Det er sjovt som vi mennesker altid forsøger at bøje omstændighederne sådan at de passer bedst muligt til situationen.

At Martin og Torben så kommer med forklaringer om standarder er det meget sjovt, men altså stadigvæk skrupforkert.

Et som fremkommer ved et tal fratrukket et andet tal skal altid udtrykkes i tallet med den højeste præcision.

Så eks.vis 90,7-90,6 skal naturligvis gengives som 0,1 og 30,5 - 30,5000 som 0,5000

At jeg så selv personligt, ligesom mange andre udviklere nogen gange kan få en mindre hjerneblødning over tossede regnefejl som CPU'en er belastet af, gør fejlen endnu mere irriterende.
Jeg har spildt oceaner af tid på at sidde med datatyper og "regnefejl", indtil jeg tilsidst fik nok og lavede mit eget objekt hvori der kun findes heltal og lidt andet smart :)

Magnus Jørgensen

I Excel giver 30.6-30.7: -0.099999999999997900000

"Fejlen" er ikke spor løjerlig, da den er resultatet af double præcesions datatypen der er brugt. Som også er beskrevet oven over.
Fejlen i android lommeregneren ligger i at resultatet i android lommeregneren ikke er afrundet korrekt. Resultatet i Android lomme regneren vises som 0.099999999. Dette er en fejl. Med den præcision skal tallet afrundes korrekt til: 0.01.
Med det resultat kunne man tro at der var tale om: 0.09999999900000000
Det ved vi tilfældig vis er forkert, men hvis der var tale om et mere kompliceret regnestykke kunne man komme i tvivl.

Derfor er der altså ikke tale om en regnefejl, men en fejl i tal repræsentationen i GUI implementeringen.

Kai Birger Nielsen

Enig med Boris. Det datalogisk interessante er da at finde ud af hvad brugeren ville forvente af resultat og så sørge for at programmet leverer det. Fx i javascript er det slet ikke noget trivielt problem og afrunding/trunkering af IEEE floating point formater er ret sikkert det helt forkerte sted at lede efter løsningen.

o * x^3 + 1 * x^2 + (-2) * x - 0 er helt fint som debug output, men en bruger vil med rette forvente at få x^2 - 2x at se i stedet for.

Man kan også sagtens få rigtige lommeregnere til at vise grænserne for deres præcision, men lommeregneren i Android 5.0 Lollipop har forhåbentlig lidt flere resourcer at trække på, så den burde faktisk kunne programmeres til at lave færre af den slags underligheder end en rigtig lommeregner. På den anden side kan jeg måske godt forstå den projektleder, der foretrækker at et tryk på + bliver til noget med at lave kald af + i stedet for et kald af et eller andet hjemmestrikket og indviklet kode med de muligheder for at gemme på superobskure fejl, det giver.

Jeg tror ikke at IEEE floats er lavet med lommeregnerapplikationer som mål, så det undrer ikke at det ikke er godt til det. Det interessante spørgsmål er om der findes tilsvarende veldokumenterede og velprogrammerede standarder for "lommeregnertal" eller hvad vi nu skal kalde det.

Torben Mogensen Blogger

Man kan også sagtens få rigtige lommeregnere til at vise grænserne for deres præcision, men lommeregneren i Android 5.0 Lollipop har forhåbentlig lidt flere resourcer at trække på, så den burde faktisk kunne programmeres til at lave færre af den slags underligheder end en rigtig lommeregner.

Med lige netop de viste regnestykker vil en almindelig lommeregner (selv med ringe præcision) vise det rigtige svar, da lommeregnere i reglen regner med decimaltal. Til gengæld er der en del andre regnestykker, der ikke regnes eller vises eksakt, for eksempel (1/3)*3-1.

Ideelt set burde man regne med ubegrænset præcision. Der findes måder at regne med præcision, der kun er begrænset af computerens lager, men den slags beregninger er overordentligt langsomme. Groft sagt regner man med en liste af cifre, der kan udvides efter behov, både før og efter kommaet. Når man skal vise et resultat, beregnes præcis så mange cifre i mellemresultaterne, så man har garanti for, at de viste cifre er korrekte.

Man kan komme langt med brøker, men kun så længe man ikke har brug for kvadratrødder, logaritmer, pi, osv. Og man kommer meget hurtigt til at have overordentligt store tællere og nævnere i brøkerne, selv om man kun bruger de fire regnearter. Periodiske kædebrøker kan håndtere kvadratrødder eksakt, men ikke pi eller logaritmer. Symbolsk beregning kan klare lidt flere beregninger eksakt, men man vil en gang imellem ende med udtryk, der ikke kan reduceres symbolsk, og som derfor må udregnes.

Pragmatisk set bør en god lommeregnerapplikation "nøjes" med at regne med stor præcision i enten decimal eller binær repræsentation og bruge fornuftige afrundingsregler ved visning af resultater.

Magnus Jørgensen

Du forholder dig ikke til essensen, men forklarer om datatyper og GUI-præsentation

Jo, det er jo netop det jeg gør. Jeg forholder mig til essensen. Den er at udregning er gjort 100% korrekt, men at der er en fejl i GUI på appen.

Hvordan vil du have at GUI'en skal præsentere et tal som beregnes med den "af lommeregneren antagede" korrekte datatype?

Hvis GUI kun har 9 decimalers nøjagtighed skal der rundes korrekt af.... Det er næsten for simpelt Janus. Hvad er det du mener med :

"af lommeregneren antagede" korrekte datatype?

Mener du dermed at en ordentligt implementere lommeregner skal bruge en datatype end en Double?
Hvad så med Excel?

En 64-bits float er rigeligt i denne henseende i min optik.

Kai Birger Nielsen

Enig med Torben langt hen ad vejen. Min pointe var blot at det kunne være at det nogle gange var smart at have mere end en taktik. Hvis jeg husker ret, så har perls virtuelle maskine det med at gemme mere end en repræsentation af et objekt internt, så hvis nu man både regner med begrænsede decimaltal mindst lige så godt som en lommeregner ville gøre det og med fx også en brøk og en kædebrøksrepræsentation, så kunne det være at man endte med noget fiksere. Fx at 1/3 * 3 faktisk gav 1.

Et af problemerne er at de første, der prøver med den slags, garanteret render i endnu flere bugs end dem, der bare laver truncate til 13 cifre af (1/3 * 3).

Men hvis man kan putte en lille del af Mathematica ind under motorhjelmen, så er der mange sjove ting, man kan, Fx:

Recognize[3.65028153987288474520, 4, x]
9 - 14 x^2 + x^4

Dvs at Mathematica kan genkende 3.65028153987288474520... som en rod i et pænt fjerdegradspolynomium bare man giver den et hint om at det max er en fjerdegradsligning, man har med at gøre.

Når man har set den slags, så gør det ondt at se 1/3 * 3 - 1 = 1.03 * 10^-15

Lasse Lasse

At fremvise 30,7 - 30,6 som 0,0999999999 er det mest informative fordi det synliggører at applikationen ikke regner præcist.

At fremvise 0,1 via afrunding er et meget dårligt forsøg på at skjule, at applikationen regner upræcist. Det skjuler, at (30,7 - 30,6) * 1234567890123456 - 123456789012345.6 = -2.625 er meget forkert (dvs. langt mere forkert end præcisionen af displayet).

Hvad skal man så gøre? Man kunne starte med at se på målgruppen af ens applikation. Det er almindelige mennesker, hvor læsbarheden af 0.1 nok er vigtigere end alt det ovenstående. Applikationen er jo ikke til mission-critical ting.

Man kunne så tilføje et "ca. " prefix til displayet, som bare tændte hvis et brugerinput eller et mellemresultat var meget stort eller meget småt.

Det er så skørt, når især dygtige fagfolk råber "i min verden går det hele op i en højere helhed og er det enete rigtige!" og glemmer brugerne. Det kommer der Rejsekortet ud af :)

Torben Mogensen Blogger

At fremvise 0,1 via afrunding er et meget dårligt forsøg på at skjule, at applikationen regner upræcist. Det skjuler, at (30,7 - 30,6) * 1234567890123456 - 123456789012345.6 = -2.625 er meget forkert (dvs. langt mere forkert end præcisionen af displayet).

En ide kunne være at inkludere et estimat af unøjagtigheder, og vise de cifre, der med stor sandsynlighed er upræcise, i en anden farve, evt. gradueret efter konfidens (grøn = stor konfidens, gul og orange = middel konfidens og rød = lav konfidens). Når man trækker to næsten ens tal fra hinanden, så vil resultatet f.eks. være omfattet af stor unøjagtighed, så i eksemplet ovenfor burde resultatet -2.625 vises i røde cifre, mens f.eks. 30,7 - 30,6 kan vises som 0,100000000 med grønne cifre, der dog for det sidste ciffers vedkommende er en anelse gulligt.

Michael Cederberg

Googles programmører har valgt at bruge en base 2 datatype til at repræsentere tal (en double). Problemet er at tallet vises som base 10 (decimalt). Googles programmører skulle have valgt en base 10 floating point decimal type (BigDecimal) som intern repræsentation.

Dette er en klassisk fejl hvor programmører tror at resultatet bliver mere rigtigt ved at hæve præcisionen af datatyperne. I praksis kommer de aldrig til at regne helt rigtigt hvis de ikke have samme repræsentation internt som i brugerinterfacet.

Sune Marcher

Wow, kan anvendelsen af IEEE floating-point datatyper i en ikke-ingeniør lommeregner stadig skabe overskrifter? :-)

Det er dog lidt sjovt at det stadig sker - selv Microsoft fixede calc.exe til arbitrary precision arithmetic tilbage i Win98.

Log ind eller Opret konto for at kommentere
Jobfinder Logo
Job fra Jobfinder