.net och jämförelser av double och float

Permalänk
Medlem

.net och jämförelser av double och float

Tjo
Jag installerade resharper igår och började gå igenom ett projekten på mitt jobb och fick varningar bland annat på hur jag jämför doubles, vilket oftast är "if(result == expectedResult){ ... }". Det är flyttal vilket datorn inte gillar och att man istället borde göra "if(result - expectedResult < 0.0001) { ... }" eftersom två flyttal i princip inte går att jämföra. Men, min tanke är att tar inte .net-ramverket hand om detta själv i ==operatorn för doubles och floats? Jag har inte lyckats hitta någon information om detta och tänker att det kanske borde vara bra att veta innan jag sätter mig och ändrar en massa kod.

Visa signatur

"Say unto thine own heart, I am mine own redeemer"
Don't touch me when I'm crazy of that airplane glue

Permalänk
Medlem

tveksamt, en == operator kommer nog inte fungera på två flyttal om dem inte faktiskt blir EXAKT lika.

Provade just i debugmiljön i visual studio och 0.00000001 == 0.0000000001 blev .FALSE.

edit; nu har jag inget projekt med C-kod atm men varken fortran eller vb.net gillade det, inte ens 0.1e-33 = 0.11e-33 i vb.net blev lika

Permalänk
Medlem

Okej, då verkar det som att man får ta hand om det själv. Då är nästa fråga hur stor skillnad man bör acceptera för att anse talen lika?

Visa signatur

"Say unto thine own heart, I am mine own redeemer"
Don't touch me when I'm crazy of that airplane glue

Permalänk
Medlem

Beror helt på vilka toleranser du jobbar med och vad koden ska göra, men 1-5% avvikelse är väl inte orimligt

Permalänk
Medlem

Som sägs så är det ju ofta inget problem så länge du har tal som "5.2" och "1.1" osv. Men säg att du skall jämnföra tal som är 25*10^-30 osv. Då blir det svårt att garantera att de stämmer på decimalen :).

Du skulle kanske kunna överlagra == operatorn, men då hade du fått skapa en egen typ vilket är flera gånger jobbet att gå över dina if-satser.

Visa signatur

Gigabyte Aorus Master | 32gb DDR4 3466MHZ CL14 | Ryzen 3950X | 3080Ti
En lång rad Intel system som barnen fått som speldatorer, VR-dator, massa bärbara, servrar, RPi's och andra boxar :P

Permalänk
Medlem

Jag har väl 3 decimaler eller nåt sånt, så 0.00001 i noggrannhet borde räcka för mig då gissar jag?

Visa signatur

"Say unto thine own heart, I am mine own redeemer"
Don't touch me when I'm crazy of that airplane glue

Permalänk
Medlem

inquam, problemet ligger inte i svårigheten att hantera stora eller små tal. Enligt IEEE specifikationen för floats (se t.ex. http://steve.hollasch.net/cgindex/coding/ieeefloat.html) så räcker en vanligt 32bits float till för tal mellan +-10^-44.85 till 10^38.53

Det problemet grundar sig i är den mappning man gör från klassen av reella tal (R) till en samling av bitar (Z^32). Man kan kalla det för en diskretisering som gör att man tappar information.

Om man jämför informationsinnehållet i en float som är 32 bitar med t.ex. en 32 bitars heltal Om man ser en float som är 32 bitar som håller tal mellan 0 och 2^32 - 1 = 4294967295 så inser vi att det bara finns ~4.3 miljarder unika flyttal.
Därför kommer den "diskretisering" man gör klassa flera olika tal (inom R) till samma flyttal (i Z^32). t.ex. med följande kod:

#include <stdio.h> int main(int argc, char **argv) { int iv, i; float *fp, fv; fv = 100.0f; iv = *((int*)&fv); fp = &iv; printf("Size of; int: %d, float: %d\n", sizeof(int), sizeof(float)); for(i=0; i<100; ++i) { printf("int: %d ==> float: %20.20f\n", iv, *fp); iv+=1; } return 0; }

så finner man följande:
int: 1120403456 ==> float: 100.00000000000000000000
int: 1120403457 ==> float: 100.00000762939453125000
int: 1120403458 ==> float: 100.00001525878906250000
int: 1120403459 ==> float: 100.00002288818359375000
Upplösningen eller steget mellan varje flyttal kring 100 är 0.00000762939453125
Alltså kommer det inte gå att skilja på tal mellan 100 och 100.00000762939453125

Vidare så blir det problem redan då man försöker återskapa talet 0.1 som inte kan representeras exakt men blir: 0.1000000014901161193847656250000000000000
int: 1036831949 ==> float: 0,1000000014901161193847656250000000000000
int: 1036831950 ==> float: 0,1000000089406967163085937500000000000000
kring detta område runt 0.1 så är upplösningen: 0.000000007450580596923828125

och till sist talet
10^-33 = 0.000000000000000000000000000000001
int: 145106764 ==> float: 0.00000000000000000000000000000000100000002374222799
int: 145106765 ==> float: 0.00000000000000000000000000000000100000011557772415
int: 145106766 ==> float: 0.00000000000000000000000000000000100000020741322031

Ursäkta det långa inlägget men frågan är en sådan som kan få ett enkelt svar av karaktären ja/nej men jag anser att det kan vara god idé att försöka belysa problemet för att bygga förståelse.

För mer utförlig läsning hänvisar jag till följande sidor:
http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.htm...
http://www.cygnus-software.com/papers/comparingfloats/compari...

Och så till det korta svaret du redan fått: .NET hanterar inte float-jämförelser så det gör livet enkelt för dig. Det bästa är om du vet applikationens syfte och själv kan sätta trösklar för att jämföra om abs( a-b) < epsilon. Alternativt att du jämför hur många ULPs (Units in last place) som skiljer. Kika t.ex. på svaret du får här:
http://stackoverflow.com/questions/3874627/floating-point-com...
(det svaret Andrew Wang ger)

Visa signatur

weeeee

Permalänk
Medlem
Skrivet av DarkBob:

Tjo
Jag installerade resharper igår och började gå igenom ett projekten på mitt jobb och fick varningar bland annat på hur jag jämför doubles, vilket oftast är "if(result == expectedResult){ ... }". Det är flyttal vilket datorn inte gillar och att man istället borde göra "if(result - expectedResult < 0.0001) { ... }" eftersom två flyttal i princip inte går att jämföra. Men, min tanke är att tar inte .net-ramverket hand om detta själv i ==operatorn för doubles och floats? Jag har inte lyckats hitta någon information om detta och tänker att det kanske borde vara bra att veta innan jag sätter mig och ändrar en massa kod.

Du har redan fått många bra svar, så jag tänkte bara bidra med lite relaterad allmän visdom. Det är oftast dålig programmeringsvana att förlita sig på att "något annat" ska ta hand om tillkortakommanden i koden, även om det skulle råka fungera för tillfället. Detta "något annat" kan ändras i framtiden, vilket kan medföra att koden beter sig otillförlitligt och det kan vara mycket svårt att se varför (för dig, och särskilt för andra). Bäst är nog att hålla sig till ANSI standard.

En viktig detalj dock, i ditt fall skulle du även behöva ta absolutbeloppet på skillnaden:

if( fabs(result - expectedResult) < 0.0001) { ... }

Annars kan skillnaden vara exempelvis -100000.0 och det skulle uppfylla villkoret i if-satsen.