A lebegÅ‘pontos számok felbontása véges, ezért nem minden szám egyforma pontossággal ábrázolható. Ez a pontatlanság akkor okoz nekünk problémát, ha a == operátorral szeretnénk két lebegÅ‘pontos számot összehasonlÃtani. Nézzünk erre egy példát:
using System;
namespace pelda_double_compare
{
public class Program
{
public static void Main(string[] args)
{
double doubleX = 0d;
for (int i = 0; i < 10; i++)
{
doubleX += 0.1d;
}
doubleX *= 8d;
double doubleY = 8d;
Console.WriteLine("Double:");
Console.WriteLine(doubleX);
Console.WriteLine(doubleY);
Console.WriteLine("doubleX == doubleY: {0}", doubleX == doubleY);
float floatX = 0f;
for (int i = 0; i < 10; i++)
{
floatX += 0.1f;
}
floatX *= 8f;
float floatY = 8f;
Console.WriteLine();
Console.WriteLine("Float:");
Console.WriteLine(floatX);
Console.WriteLine(floatY);
Console.WriteLine("floatX == floatY: {0}", floatX == floatY);
}
}
}
A program kimenete:
Double:
7,999999999999999
8
doubleX == doubleY: False
Float:
8,000001
8
floatX == floatY: False
A példaprogram a (0.1 * 10) * 8 számÃtást végzi el az egyik változó esetén, a másik esetén pedig fixen 8 érték kerül allokálásra. A meglepÅ‘ az, hogy bár azonos eredményt kellene kapnunk, mégsem ez történik. Ennek az oka az, hogy a számÃtógép belsÅ‘leg ugye kettes számrendszerrel dolgozik és lebegÅ‘pontos számok esetén nem tizedes törteket tárol, hanem kettedes törteket, ami nem képes pontosan ábrázolni a 0.1 tizedes tört értéket. Ezen a pontatlanságon erÅ‘sÃt a ciklikus összeadás és végeredményben jól látható, hogy a float tÃpus felülbecsli egy picit az értéket, a double tÃpus pedig alulbecsli. Illetve az is jól látható, ha == operátorral hasonlÃtunk össze, akkor false eredményt kapunk mindkét esetben.
Ennek az oka az, hogy a == operátor csak akkor ad igazat, ha bitre pontosan ugyan az a két szám tartalma. Ez pedig a lebegőpontos számábrázolás pontatlansága miatt szinte sosem fog bekövetkezni a való életben.
Éppen ezért lebegÅ‘pontos számokat sose hasonlÃtsunk össze a == operátorral, mert több, mint valószÃnű nem az fog történni, amire számÃtunk.
Kérdés már csak az, hogy akkor mégis hogyan lehetne megvizsgálni, hogy két szám nagyjából egyenlÅ‘-e? A kulcs a nagyjából egyenlÅ‘n van. Két lebegÅ‘pontos szám összehasonlÃtásakor mindig egy adott tűréshez kell hasonlÃtani:
bool equal = Math.Abs(x -y) < diff;
Az Abs hÃvás azért kell, mivel a két szám esetén nem tudhatjuk elÅ‘re, hogy melyik lesz a nagyobb, Ãgy kivédjük a negatÃv számokat. Ha pedig a kapott eredmény kisebb, mint az elÅ‘re meghatározott tűrés (diff), akkor a két szám egyenlÅ‘nek tekinthetÅ‘.