10 Nisan 2019 Çarşamba

IEEE 754 ve İki Sayının Eşitliği

Giriş
Yöntemler şöyle
1. Her sayı belli bir küsurat hanesine yuvarlanır ve >, < işlemine sokulur.
2. Epsilon veya diğer ismiyle tolerans değeri kullanılarak karşılaştırma yapılır.

3. Belirtilen ULP (x ve y değeri arassındaki kaç tane double olduğu) kadar yakın olup olmadığının karşılaştırması yapılır.

Mutlak Epsilon'a Göre Karşılaştırmak
Açıklaması şöyle. x ve y küçük sayılar iken kullanışlı. Burada sadece tek bir epsilon aralığına bakılıyor.
For numbers between 1.0 and 2.0 FLT_EPSILON represents the difference between adjacent floats. For numbers smaller than 1.0 an epsilon of FLT_EPSILON quickly becomes too large, and with small enough numbers FLT_EPSILON may be bigger than the numbers you are comparing (a variant of this led to a flaky Chromium test)!

For numbers larger than 2.0 the gap between floats grows larger and if you compare floats using FLT_EPSILON then you are just doing a more-expensive and less-obvious equality check. That is, if two floats greater than 2.0 are not the same then their difference is guaranteed to be greater than FLT_EPSILON. For numbers above 16777216 the appropriate epsilon to use for floats is actually greater than one, and a comparison using FLT_EPSILON just makes you look foolish. We don’t want that.
Örnek
Şöyle yaparız.
bool absoluteToleranceCompare(double x, double y)
{
  return std::fabs(x - y) <= std::numeric_limits<double>::epsilon() ;
}
Relative Epsilon'a Göre Karşılaştırmak
Açıklaması şöyle. x ve y büyük sayılar ilen kullanışlı. Burada daha fazla büyük epsilon aralığına bakılıyor.
The idea of a relative epsilon comparison is to find the difference between the two numbers, and see how big it is compared  to their magnitudes. In order to get consistent results you should always compare the difference to the larger of the two numbers. In English:

To compare f1 and f2 calculate diff = fabs(f1-f2). If diff is smaller than n% of max(abs(f1),abs(f2)) then f1 and f2 can be considered equal.
Epsilon şöyle hesaplanır.
double Epsilon = std::max(x, y) * std::numeric_limits<double>::epsilon();
Örnek
Şöyle yaparız. Yukarıdaki Epsilon hesaplamadan farklı olarak x ve y'nin mutlak değerinin max()'ı alınıyor. Fark yaratır mı bilmiyorum.
bool relativeToleranceCompare(double x, double y)
{
  double maxXY = std::max( std::fabs(x) , std::fabs(y) ) ;
  return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXY ;
}
Eğer her iki örneği birleştirmek istersek şöyle yaparız
bool combinedToleranceCompare(double x, double y)
{
  double maxXYOne = std::max( { 1.0, std::fabs(x) , std::fabs(y) } ) ;

  return std::fabs(x - y) <= std::numeric_limits<double>::epsilon()*maxXYOne ;
}
Örnek
The art of computer programming by Knuth kitabındaki örnekler şöyle. approximatelyEqual() metodu relativeToleranceCompare() metodu ile aynı. essentiallyEqual() metodunu anlamadım.
bool approximatelyEqual(float a, float b, float epsilon)
{
  return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool essentiallyEqual(float a, float b, float epsilon)
{
  return fabs(a - b) <= ( (fabs(a) > fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyGreaterThan(float a, float b, float epsilon)
{
  return (a - b) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}

bool definitelyLessThan(float a, float b, float epsilon)
{
  return (b - a) > ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon);
}
ULP'ye Göre Karşılaştırmak
Açıklaması şöyle.
We already know that adjacent floats have integer representations that are adjacent. This means that if we subtract the integer representations of two numbers then the difference tells us how far apart the numbers are in float space. 
Yani
In other words, if you subtract the integer representations and get one, then the two floats are as close as they can be without being equal. If you get two then they are still really close, with just one float between them. The difference between the integer representations tells us how many Units in the Last Place the numbers differ by. This is usually shortened to ULP, as in “these two floats differ by two ULPs.”
Şöyle yaparız.
// Usable AlmostEqual function    
bool AlmostEqual2sComplement(float A, float B, int maxUlps)    
{    
    // Make sure maxUlps is non-negative and small enough that the    
    // default NAN won't compare as equal to anything.    
    assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024);    
    int aInt = *(int*)&A;    
    // Make aInt lexicographically ordered as a twos-complement int    
    if (aInt < 0)    
        aInt = 0x80000000 - aInt;    
    // Make bInt lexicographically ordered as a twos-complement int    
    int bInt = *(int*)&B;    
    if (bInt < 0)    
        bInt = 0x80000000 - bInt;    
    int intDiff = abs(aInt - bInt);    
    if (intDiff <= maxUlps)    
        return true;    
    return false;    
}

Hiç yorum yok:

Yorum Gönder