Uspoređivanje brojeva s pomičnom točkom

Vrijedi li da je ? Da, naravno!

Što ispisuje slijedeći programski isječak?

if (0.7 + 0.2 + 0.1 == 1) {
  cout << "da, naravno!" << endl;
} else {
  cout << "wait." << endl;
}

Objašnjenje

Operator == će u programu provjeriti jesu li lijeva i desna strana jednake, odnosno sadržavaju li (privremeni) objekti s lijeve i desne strane istu vrijednost u memoriji.

Pogledajmo na kratko kako izgledaju gore uspoređeni brojevi u memoriji (hexadecimalno) i kada se ispišu na 20 decimalnih mjesta.

union broj {
  long long ll;
  double lf;
} A, B;

A.lf = 1;
B.lf = 0.7 + 0.2 + 0.1;

printf("%lx %.20lf\n", A.ll, A.lf);
// ispisuje 3ff0000000000000 1.00000000000000000000

printf("%lx %.20lf\n", B.ll, B.lf);
// ispisuje 3fefffffffffffff 0.99999999999999988898

Ovdje je union iskorišten da bi pogledali zapis broja s pomičnom točkom kako je zapisan u memoriji.

Razlog ovom hazardu je ograničena preciznost zapisa brojeva koji se koristi. Odnosno, broj 0.7 kada se zapiše u binarnom zapisu je periodičan broj, slično kao što je periodičan broj u dekadskom zapisu. Kada bi u takvom ograničenom zapisu pokušali izračunati također bi dobili broj koji nije točno već približno. Za one koji žele više - numerička matematika.

Kako rukovati s decimalnim brojevima

Stoga, uvijek kada je potrebno u zadatku koristiti i uspoređivati decimalne brojeve potrebno je to raditi imajući na umu preciznost:

typedef double lf;

const lf EPSILON = 1e-12;

inline bool lt(const lf& a, const lf& b) { return b - a > EPSILON; }

inline bool gt(const lf& a, const lf& b) { return lt(b, a); }
inline bool eq(const lf& a, const lf& b) { return !lt(a, b) && !lt(b, a); }
inline bool le(const lf& a, const lf& b) { return !gt(a, b); }
inline bool ge(const lf& a, const lf& b) { return !lt(a, b); }

Definirani operatori redom predstavljaju C operatore <, >, ==, <=, >=.

Uz ovako definirane operatore usporedbe, nemamo problema jer je eq(0.7 + 0.2 + 0.1, 1) - da, naravno!

© 2014. Anton Grbin Creative Commons License

Ovaj članak objavljen je pod
Creative Commons Attribution-ShareAlike 3.0 Croatia License