🧐 PHP, Liczby zmiennoprzecinkowe i Tajemnica -0.0 Przegląd PHP #5
Tak, serio. -0.0 i 0.0 to dwie różne reprezentacje bajtowe w standardzie IEEE 754 – ale w praktyce… PHP i tak je traktuje tak samo.
Miałem taki kod:
public function getPercentage(): int
{
    // epsilon to avoid division by zero
    if (abs($this->limitPrice) < 1e-8) {
        return 0;
    }
    return min(100, (int) round(($this->totalCartPrice / $this->limitPrice) * 100));
}
I zacząłem się zastanawiać:
- Czy mogę po prostu porównać $this->limitPrice == 0.0?
- A co jeśli limitPrice=-0.0? 🤯
🔬 Analiza
1️⃣ IEEE 754 i -0.0
W standardzie liczb zmiennoprzecinkowych istnieje dodatnie zero (0.0) i ujemne zero (-0.0).
 Na poziomie bajtów to dwie różne wartości:
-  0.0→0x0000000000000000
-  -0.0→0x8000000000000000
Czyli tak, binarnie to nie to samo.
2️⃣ Co na to PHP?
Porównajmy:
var_dump(0.0 == -0.0);   // true
var_dump(0.0 === -0.0);  // true
✅ PHP traktuje te dwie wartości jako równe zarówno w porównaniu luźnym (==), jak i ścisłym (===).
Ale uwaga:
var_dump(1 / 0.0);  // float(INF)
var_dump(1 / -0.0); // float(-INF)
➡️ Przy dzieleniu widać różnicę – znak zera wpływa na wynik (+∞ vs -∞).
3️⃣ Czy to ma znaczenie w moim przypadku?
W moim kodzie limitPrice ma maksymalnie 4 miejsca po przecinku, pochodzi z bazy danych lub prostych operacji.
 Nie mam tu ujemnych zer z kosmosu.
 W praktyce mogę spokojnie napisać:
if ($this->limitPrice == 0.0) {
    return 0;
}
I wszystko działa jak należy.
 Epsilon (1e-8) nie jest potrzebny, bo nie mam problemów z błędami zaokrągleń na poziomie 17 miejsc po przecinku.
🛠 Bonus: Jak wykryć -0.0 (dla nerdów)
Jeżeli jednak z jakiegoś powodu musisz wiedzieć, czy to -0.0, oto trik:
function isNegativeZero(float $x): bool {
    return $x === 0.0 && 1 / $x === -INF;
}
var_dump(isNegativeZero(0.0));  // false
var_dump(isNegativeZero(-0.0)); // true
Tak, trzeba podzielić przez zero, żeby to wykryć. 🧪
✅ Wniosek
- Tak, -0.0 i 0.0 to różne bajty.
- PHP traktuje je jako równe (==,===).
- Możesz śmiało pisać == 0.0w porównaniach.
- Jedynie dzielenie przez 0.0 i -0.0 zwraca różne nieskończoności (INF vs -INF) – więc jak dzielisz, warto używać abs().
🎉 Podsumowanie
Udało mi się uprościć kod i zamiast epsilonów mam po prostu:
public function getPercentage(): int
{
    return $this->limitPrice ? min(100, (int) round(($this->totalCartPrice / $this->limitPrice) * 100)) : 0;
}
Czytelniej, prościej i dalej bezpiecznie.
 A fakt, że -0.0 istnieje, zostawiam jako ciekawostkę do impressowania znajomych programistów na kawie. ☕️😎
PS: Jeżeli zależy Ci na użyciu epislona to chyba znacznie lepiej użyć tej stałej.
PHP_FLOAT_EPSILON
Podoba Ci się ten artykuł?
Oto kilka następnych artykułów, które mogą Ci się również spodobać: