Vra

Wat is die beste metode vir die vergelyking van IEEE dryf en dubbelspel vir gelykheid? Ek het gehoor van 'n paar metodes, maar ek wou om te sien wat die gemeenskap dink.

Was dit nuttig?

Oplossing

Die beste benadering ek dink is om te vergelyk ULPs .

bool is_nan(float f)
{
    return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) == 0x7f800000 && (*reinterpret_cast<unsigned __int32*>(&f) & 0x007fffff) != 0;
}

bool is_finite(float f)
{
    return (*reinterpret_cast<unsigned __int32*>(&f) & 0x7f800000) != 0x7f800000;
}

// if this symbol is defined, NaNs are never equal to anything (as is normal in IEEE floating point)
// if this symbol is not defined, NaNs are hugely different from regular numbers, but might be equal to each other
#define UNEQUAL_NANS 1
// if this symbol is defined, infinites are never equal to finite numbers (as they're unimaginably greater)
// if this symbol is not defined, infinities are 1 ULP away from +/- FLT_MAX
#define INFINITE_INFINITIES 1

// test whether two IEEE floats are within a specified number of representable values of each other
// This depends on the fact that IEEE floats are properly ordered when treated as signed magnitude integers
bool equal_float(float lhs, float rhs, unsigned __int32 max_ulp_difference)
{
#ifdef UNEQUAL_NANS
    if(is_nan(lhs) || is_nan(rhs))
    {
        return false;
    }
#endif
#ifdef INFINITE_INFINITIES
    if((is_finite(lhs) && !is_finite(rhs)) || (!is_finite(lhs) && is_finite(rhs)))
    {
        return false;
    }
#endif
    signed __int32 left(*reinterpret_cast<signed __int32*>(&lhs));
    // transform signed magnitude ints into 2s complement signed ints
    if(left < 0)
    {
        left = 0x80000000 - left;
    }
    signed __int32 right(*reinterpret_cast<signed __int32*>(&rhs));
    // transform signed magnitude ints into 2s complement signed ints
    if(right < 0)
    {
        right = 0x80000000 - right;
    }
    if(static_cast<unsigned __int32>(std::abs(left - right)) <= max_ulp_difference)
    {
        return true;
    }
    return false;
}

'n soortgelyke tegniek kan gebruik word vir die dubbelspel. Die geheim is om die vlotte te omskep sodat hulle beveel (asof heelgetalle) en dan net te sien hoe verskillende dit is.

Ek het geen idee hoekom hierdie damn ding is skroefwerk my onderstreping. Edit: O, miskien is dit net 'n artefak van die voorskou. Dit is dan OK.

Ander wenke

Die huidige weergawe ek gebruik is hierdie

bool is_equals(float A, float B,
               float maxRelativeError, float maxAbsoluteError)
{

  if (fabs(A - B) < maxAbsoluteError)
    return true;

  float relativeError;
  if (fabs(B) > fabs(A))
    relativeError = fabs((A - B) / B);
  else
    relativeError = fabs((A - B) / A);

  if (relativeError <= maxRelativeError)
    return true;

  return false;
}

Dit lyk om te sorg vir die meeste probleme te neem deur die kombinasie van relatiewe en absolute fout verdraagsaamheid. Is die ULP benadering beter? Indien wel, hoekom?

  

@DrPizza. Ek is geen prestasie guru, maar ek sou verwag vaste punt bedrywighede te vinniger as drywende punt bedrywighede (in die meeste gevalle) wees

Dit eerder hang af van wat jy doen met hulle. 'N Vaste-punt tipe met dieselfde reeks as 'n IEEE float sou baie baie keer stadiger (en baie keer groter) wees.

  

Dinge wat geskik is vir dryf:

3D-beelde, fisika / ingenieurswese, simulasie, klimaat simulasie ....

In numeriese sagteware dikwels wil jy om te toets of twee drywende punt getalle is presies gelyk. LAPACK is vol voorbeelde vir sulke gevalle. Seker, die mees algemene geval is waar jy wil om te toets of 'n drywende punt nommer is gelyk aan "Zero", " 'n Mens", "Twee", "Die helfte". As iemand belangstel kan ek 'n paar algoritmes haal en gaan meer in detail.

Ook in BLAS dikwels wil jy om te kyk of 'n drywende punt nommer is presies nul of een. Byvoorbeeld, kan die roetine dgemv bedrywighede van die vorm te bereken

  • y = beta * y + alfa * A * x
  • y = beta * y + alfa * A ^ T * x
  • y = beta * y + alfa * A ^ H * x

As beta gelyk Een jy 'n "plus opdrag" en vir beta gelyk Zero 'n "eenvoudige opdrag". Sodat jy seker kan die computational koste te sny as jy hierdie (algemene) gevalle 'n spesiale behandeling te gee.

Seker, kan jy die BLAS roetines te ontwerp in so 'n manier dat jy presies vergelykings kan vermy (bv die gebruik van 'n paar vlae). Maar die LAPACK is vol voorbeelde waar dit nie moontlik is.

post scriptum.

  • Daar is beslis baie gevalle waar jy nie wil gaan vir "is presies gelyk". Vir baie mense dit selfs dalk die enigste geval hulle ooit hoef te hanteer. Al wat ek wil uitwys is dat daar ander gevalle ook.

  • Hoewel LAPACK in Fortran is geskryf die logika is dieselfde as jy die gebruik van ander programmeertale vir numeriese sagteware.

  

O liewe Here moet asseblief nie die float stukkies as SY tensy jy hardloop is op 'n P6 of interpreteer vroeër.

Selfs al is dit veroorsaak dat dit tot die kopiëring van vektor registreer om registers in heel getal via geheue, en selfs al is dit die pyplyn stalletjies, dit is die beste manier om dit te doen wat ek teëgekom het, vir sover dit die mees omvattende vergelykings selfs in die gesig van drywende punt foute.

d.w.z. dit is 'n prys werd.

  

Dit lyk om te sorg vir die meeste probleme te neem deur die kombinasie van relatiewe en absolute fout verdraagsaamheid. Is die ULP benadering beter? Indien wel, hoekom?

ULPs is 'n direkte maatstaf van die "afstand" tussen twee drywende punt nommers. Dit beteken dat hulle jou nie vereis om optower die relatiewe en absolute fout waardes, nie doen jy om seker te maak om daardie waardes te kry "oor reg". Met ULPs, kan jy direk uit te druk hoe naby jy wil die getalle te wees, en dieselfde drumpel werk net so goed vir klein waardes as vir grotes.

  

As jy punt foute swaai jy selfs meer probleme as dit. Alhoewel ek dink dit is tot persoonlike perspektief.

Selfs as ons doen die numeriese analise om opeenhoping van foute te minimaliseer, kan ons nie uit te skakel dit en kan ons gelaat met resultate wat behoort identies te wees (as ons die berekening met reals) maar verskil (want ons kan nie bereken met reals).

As jy op soek is na twee dryf gelyk te wees, dan moet hulle identies gelyk in my opinie. As jy in die gesig staar 'n drywende punt afronding probleem, miskien 'n vaste punt verteenwoordiging sou jou probleem te pas beter.

  

As jy op soek is na twee dryf gelyk te wees, dan moet hulle identies gelyk in my opinie. As jy in die gesig staar 'n drywende punt afronding probleem, miskien 'n vaste punt verteenwoordiging sou jou probleem te pas beter.

Miskien kan ons nie bekostig om die verlies van omvang of prestasie wat so 'n benadering sou aandoen.

@DrPizza. Ek is geen prestasie guru, maar ek sou verwag vaste punt bedrywighede te vinniger as drywende punt bedrywighede (in die meeste gevalle) wees

@Craig H: Seker. Ek is heeltemal okay met dit druk nie. As 'n of b winkel geld dan moet hulle verteenwoordig in vaste punt. Ek sukkel om te dink aan 'n werklike wêreld voorbeeld waar sulke logika behoort te word gekoppel aan dryf. Dinge wat geskik is vir dryf:

  • gewigte
  • geledere
  • afstande
  • werklike wêreld waardes (soos uit 'n ADC)

Vir al hierdie dinge, óf jy baie daarna getalle en net die resultate aan die gebruiker vir menslike interpretasie, of jy 'n vergelykende verklaring (selfs al is so 'n stelling is, "het hierdie saak binne 0,001 van hierdie ander ding" maak ). 'N Vergelykende stelling soos myne is net nuttig in die konteks van die algoritme: die "binne 0.001" deel hang af van wat fisiese vraag wat jy vra. Dat my 0,02. Of moet ek sê 2 / 100ths?

  

Dit eerder hang af van wat jy   doen met hulle. 'N Vaste-punt tipe   met dieselfde reeks as 'n IEEE float   sou baie baie keer stadiger wees (en   baie keer groter).

Goed, maar as ek wil 'n infinitesimale klein bietjie-resolusie dan is dit terug na my oorspronklike punt:. == en = het geen betekenis in die konteks van so 'n probleem

'n int laat my uit te druk ~ 10 ^ 9 waardes (ongeag van die omvang) wat lyk soos genoeg vir enige situasie waar ek sou omgee twee van hulle gelyk. En as dit is nie genoeg nie, gebruik 'n 64-bit OS en jy het ongeveer 10 ^ 19 verskillende waardes.

Ek kan waardes 'n reeks van 0 tot 10 ^ 200 (byvoorbeeld) in 'n int, dit is net die bietjie-resolusie wat ly (resolusie sou groter as 1 wees, maar, weer uit te druk, geen aansoek het dat die soort van wissel asook dié soort van besluit).

Om op te som, ek dink in alle gevalle een óf as verteenwoordiger van 'n kontinuum van waardes, in welke geval! = En == is irrelevant, of 'n mens wat 'n vaste stel waardes, wat gekarteer kan word om 'n int (of 'n ander vaste-presisie tipe).

  

'n int laat my uit te druk ~ 10 ^ 9 waardes   (Ongeag van die omvang) wat lyk   soos genoeg vir enige situasie waar ek   sou omgee twee van hulle om   gelyk. En as dit is nie genoeg nie, gebruik 'n   64-bit OS en jy het ongeveer 10 ^ 19   duidelike waardes.

Ek het eintlik getref dat perk ... Ek het probeer om tye in ps en tyd jongleren in klok siklusse in 'n simulasie waar jy maklik getref 10 ^ 10 siklusse. Maak nie saak wat ek gedoen het ek baie vinnig oorstroom die nietige verskeidenheid van 64-bit heelgetalle ... 10 ^ 19 is nie soveel as wat jy dink dit is, Gimme 128 stukkies nou berekening!

dryf my toegelaat om 'n oplossing vir die wiskundige probleme te kry, as die waardes oorstroom met baie nulle by die lae einde. Sodat jy basies 'n desimale punt swaai aronud in die aantal met geen verlies van presisie (Ek kon hou met die meer beperkte duidelike aantal waardes toegelaat in die MANTISSA van 'n float in vergelyking met 'n 64-bit int, maar dringend nodig ste reeks! ).

En dan dinge omgeskakel terug na heelgetalle te vergelyk ens

irriterende, en op die ou end het ek geskrap die hele poging en net staatgemaak op dryf en om ontslae die werk wat gedoen is. Nie volmaak nie, maar werk vir die gebruik geval beoog.

  

As jy op soek is na twee dryf gelyk te wees, dan moet hulle identies gelyk in my opinie. As jy in die gesig staar 'n drywende punt afronding probleem, miskien 'n vaste punt verteenwoordiging sou jou probleem te pas beter.

Miskien moes ek die probleem beter te verduidelik. In C ++, die volgende kode:

#include <iostream>

using namespace std;


int main()
{
  float a = 1.0;
  float b = 0.0;

  for(int i=0;i<10;++i)
  {
    b+=0.1;
  }

  if(a != b)
  {
    cout << "Something is wrong" << endl;
  }

  return 1;
}

druk die frase "Iets is verkeerd". Is jy sê dat dit moet?

O liewe Here moet asseblief nie die float stukkies as SY tensy jy hardloop is op 'n P6 of interpreteer vroeër.

  

Dit is die beste manier om dit te doen wat ek teëgekom het, vir sover dit die mees omvattende vergelykings selfs in die aangesig van drywende punt foute.

As jy punt foute swaai jy selfs meer probleme as dit. Alhoewel ek dink dit is tot persoonlike perspektief.

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top