🔥 Darmowa Roadmapa .NET — kompletny plan nauki do pierwszej pracy. Pobierz i dołącz do Listy VIP →

Dzielenie przez zero w C# — double, nieskończoność i NaN

Dzielenie przez zero w C# — komputer pokładowy pokazuje zasięg: ∞ km przy 120 litrach paliwa

Statek ma 120 litrów paliwa. Komputer pokładowy liczy, jak daleko na tym zajedzie, i odpowiada: nieskończenie daleko. Program się nie wywrócił, nie było błędu ani ostrzeżenia. A to jeszcze nie najgorsze, co potrafi ta liczba — jest wartość, przy której alarm „mało paliwa” nie włączy się nawet przy trzech litrach w zbiorniku.

Ten wpis to wersja tekstowa odcinka Program .NET Developer #3 na YouTube. Kod jest ten sam co w filmie, a każdy wynik uruchomiłem na .NET 10. Jeśli w twoim programie coś się dzieli — średnia, procent, zasięg, cena za sztukę — to dotyczy także ciebie. Zakładam, że znasz zmienne i typy w C# i instrukcję if; resztę wyjaśniam po drodze.

Dzielenie przez zero w double: zasięg ∞ km

Komputer pokładowy statku Zorza dzieli paliwo przez spalanie. Czujnik spalania jeszcze nic nie zmierzył, więc na razie jest tam zero:

double paliwo = 120;   // litry w zbiorniku
double spalanie = 0;   // litry na kilometr

double zasieg = paliwo / spalanie;

Console.WriteLine($"Paliwo: {paliwo} l");
Console.WriteLine($"Zasięg: {zasieg} km");

Słowo double przed nazwą zmiennej to jej typ: liczba, która może mieć część po przecinku, na przykład 0,05. Ukośnik / to dzielenie. Wynik:

Paliwo: 120 l
Zasięg: ∞ km

Dzielenie przez zero, którego „nie wolno” robić, przeszło bez słowa. C# wpisał do zmiennej nieskończoność i program poszedł dalej. Tak działa typ double: ma kilka specjalnych wartości, które nie są zwykłymi liczbami, a jedna z nich to właśnie nieskończoność.

Zasada: dzielenie double przez zero nie jest błędem. Daje nieskończoność i program liczy dalej.

Te same liczby w int: DivideByZeroException

Zmieńmy jedno słowo — zamiast double piszemy int, czyli liczbę całkowitą:

int paliwo = 120;
int spalanie = 0;

int zasieg = paliwo / spalanie;

Console.WriteLine($"Paliwo: {paliwo} l");
Console.WriteLine($"Zasięg: {zasieg} km");
Unhandled exception. System.DivideByZeroException: Attempted to divide by zero.
   at Program.<Main>$(String[] args) in …\02-Int.cs:line 10

To jest wyjątek — sygnał, że program trafił na coś, czego nie umie zrobić. DivideByZeroException znaczy „wyjątek dzielenia przez zero”. Nikt go nie obsłużył, więc program się zatrzymał. Nie wypisał nawet paliwa, ale powiedział dokładnie, w której linijce był problem. Jak taki wyjątek złapać, zamiast pozwolić programowi się wywrócić, opisuje wpis o obsłudze błędów: try, catch, finally.

Skąd ta różnica? Liczby double zapisuje się według normy IEEE 754, która przewiduje specjalne wartości: plus i minus nieskończoność oraz NaN. Typ int takich wartości nie ma — dla wyniku dzielenia przez zero nie ma czego wpisać, więc zostaje tylko zatrzymać program. Tak samo zachowuje się decimal (sprawdziłem: 120 / 0 w decimal też kończy się DivideByZeroException).

Typ120 / 0Co się dzieje dalej
intDivideByZeroExceptionprogram staje i pokazuje linijkę
decimalDivideByZeroExceptionprogram staje i pokazuje linijkę
double∞program liczy dalej, po cichu

Zasada: program, który się wywraca, jest uczciwy. Program, który liczy dalej ze złą wartością, może cię okłamać.

Pułapka #1: 0 / 0 to NaN — alarm, który milczy

Prawdziwy komputer pokładowy sam liczy spalanie: dzieli litry spalone od startu przez przebyte kilometry. Wyobraź sobie, że właśnie się zrestartował. Liczniki są wyzerowane, a w zbiorniku zostały trzy litry. Niżej jest alarm: jeżeli (if) zasięg jest mniejszy niż 50 km, wracamy do bazy; w przeciwnym razie (else) lecimy dalej. Więcej o tej konstrukcji — we wpisie o instrukcjach warunkowych if i else.

double paliwo = 3;     // litry w zbiorniku
double zuzyte = 0;     // litry spalone od restartu komputera
double przebyte = 0;   // kilometry od restartu komputera

double spalanie = zuzyte / przebyte;
double zasieg = paliwo / spalanie;

Console.WriteLine($"Spalanie: {spalanie} l/km");
Console.WriteLine($"Zasięg: {zasieg} km");

if (zasieg < 50)
{
    Console.WriteLine("ALARM: mało paliwa! Wracaj do bazy.");
}
else
{
    Console.WriteLine("Paliwa wystarczy. Lecimy dalej.");
}
Spalanie: NaN l/km
Zasięg: NaN km
Paliwa wystarczy. Lecimy dalej.

NaN to skrót od *Not a Number* — „to nie jest liczba”. Zero podzielone przez zero w double nie daje nieskończoności, tylko właśnie NaN. Zasięg też wyszedł NaN, bo każde działanie z NaN daje znowu NaN. A ostatnia linijka mówi: paliwa wystarczy. Przy trzech litrach. Alarm był w kodzie i się nie włączył.

Pułapka #2: NaN przegrywa każde porównanie — nawet z samym sobą

Żeby zobaczyć dlaczego, bierzemy NaN i zadajemy mu cztery pytania:

double zasieg = 0.0 / 0.0;

Console.WriteLine($"zasieg < 50: {zasieg < 50}");
Console.WriteLine($"zasieg >= 50: {zasieg >= 50}");
Console.WriteLine($"zasieg == double.NaN: {zasieg == double.NaN}");
Console.WriteLine($"double.IsNaN(zasieg): {double.IsNaN(zasieg)}");

Zanim przewiniesz dalej, odpowiedz sam:

Zgadnij, zanim klikniesz

04-NaN.cs — cztery pytania do NaN

Zmienna zasieg ma wartość 0.0 / 0.0, czyli NaN. Co wypisze każda linijka?

zasieg < 50
zasieg >= 50
zasieg == double.NaN
double.IsNaN(zasieg)
Uruchomione na .NET 10: trzy pierwsze to False, ostatnie True. Porównanie z NaN zawsze przegrywa — nawet z samym NaN. Prawdę mówi tylko double.IsNaN.
zasieg < 50: False
zasieg >= 50: False
zasieg == double.NaN: False
double.IsNaN(zasieg): True

Mniejszy niż 50? Fałsz. Większy albo równy 50? Też fałsz. Równy NaN? Fałsz. Porównania <, >, <=, >= i == z NaN zawsze dają false — nawet porównanie NaN z NaN (jedynie != daje true). Dlatego nasz if dostał odpowiedź „nie, zasięg nie jest mniejszy niż 50” i poszedł do else. Kompilator przy zasieg == double.NaN nie ostrzega, więc ten błąd łatwo przepuścić. Prawdę mówi tylko metoda double.IsNaN. Wszystkie operatory porównania znajdziesz we wpisie o operatorach w C#.

Zasada: NaN nie jest ani mniejszy, ani większy, ani równy niczemu. Nigdy nie sprawdzaj go znakiem == — tylko metodą double.IsNaN.

Poprawka: double.IsFinite przed porównaniem

Zanim porównamy zasięg z pięćdziesiątką, sprawdzamy, czy to w ogóle zwykła liczba:

double paliwo = 3;     // litry w zbiorniku
double zuzyte = 0;     // litry spalone od restartu komputera
double przebyte = 0;   // kilometry od restartu komputera

double spalanie = zuzyte / przebyte;
double zasieg = paliwo / spalanie;

if (!double.IsFinite(zasieg))
{
    Console.WriteLine("Zasięg: brak danych. Wracaj do bazy.");
}
else if (zasieg < 50)
{
    Console.WriteLine("ALARM: mało paliwa! Wracaj do bazy.");
}
else
{
    Console.WriteLine($"Zasięg: {zasieg} km. Lecimy dalej.");
}
Zasięg: brak danych. Wracaj do bazy.

IsFinite znaczy „czy skończona”: zwraca true dla zwykłej liczby, a false dla nieskończoności i dla NaN. Wykrzyknik przed nią to zaprzeczenie — „jeżeli zasięg nie jest zwykłą liczbą”. Wtedy mówimy wprost: brak danych, wracaj do bazy. Ta sama poprawka łapie też nieskończoność z pierwszego przykładu (sprawdziłem: przy spalaniu 0 l/km wersja finalna też wypisuje „brak danych”).

To nie jedyny sposób. Trzy podejścia z ich kosztami:

PodejściePlusyMinusy
sprawdzić dzielnik przed dzieleniem (if (przebyte == 0))błąd nie powstaje wcaletrzeba pamiętać przy każdym dzieleniu; nie łapie NaN, który przyszedł z innego miejsca
double.IsFinite(wynik) przed porównaniemjedno sprawdzenie łapie ∞ i NaN, skądkolwiek przyszływynik już jest zepsuty — sprawdzasz skutek, nie przyczynę
liczyć w int albo decimaldzielenie przez zero kończy się wyjątkiem, którego nie da się przeoczyćtracisz część ułamkową (int) albo zakres i szybkość (decimal); wyjątek trzeba obsłużyć

Zasada: gdy program nie wie, ile wynosi wynik, ma zachować się ostrożnie — „brak danych” to też odpowiedź, i to lepsza niż „lecimy dalej”.

Sprawdź sam: komputer pokładowy przed i po poprawce

Wybierz stan liczników i wersję kodu. Wszystkie dziesięć wyników uruchomiłem na .NET 10.

Sprawdź sam

Komputer pokładowy Zorzy

1 · stan liczników (paliwo / spalone / przebyte)

2 · wersja kodu

if (zasieg < 50) { Console.WriteLine("ALARM: mało paliwa! Wracaj do bazy."); } else { Console.WriteLine("Paliwa wystarczy. Lecimy dalej."); }

Ściągawka na code review

DziałanieWynikPorównanie z liczbą
int / 0, decimal / 0DivideByZeroException—
double (≠ 0) / 0∞∞ < 50 → False
double 0 / 0NaNkażde <, >=, == → False
cokolwiek z NaNNaNjw.
double.IsFinite(x)False dla ∞ i NaN—

Przeglądając kod, szukaj dzielenia zmiennych double, których mianownik może być zerem (liczniki, sumy, „przebyte od restartu”), porównań == double.NaN i warunków, których gałąź else jest „bezpieczna” tylko pod warunkiem, że liczba jest prawdziwa. Kiedy wynik i tak wyjdzie dziwny, przejdź program krok po kroku debugerem — NaN widać w podglądzie zmiennych od pierwszej linijki, w której powstał.

Zadanie: osłony 0 z 0

Nowy statek, generator osłon jeszcze niezamontowany. Program liczy procent osłon:

double oslony = 0;
double maxOslony = 0;

double procent = oslony / maxOslony * 100;

Console.WriteLine($"Osłony: {procent}%");

if (procent < 20)
{
    Console.WriteLine("UWAGA: osłony słabe!");
}
  1. Zanim uruchomisz program, zapisz, co wypisze.
  2. Popraw go tak, żeby uczciwie mówił „brak danych”.
  3. Celowo zepsuj: zmień double na int i przeczytaj komunikat.

Pytanie dodatkowe do komentarza pod filmem: co wypisze -120.0 / 0? Najpierw w głowie, dopiero potem w kodzie.

Podsumowanie

  • **int i decimal przez zero → wyjątek DivideByZeroException.** Program staje i pokazuje linijkę.
  • **double przez zero → ∞, a 0 / 0 → NaN.** Bez błędu, bez ostrzeżenia — program liczy dalej.
  • Każde działanie z NaN daje NaN, a każde porównanie z NaN (<, >=, ==) to false — dlatego if po cichu przechodzi do else.
  • **Sprawdzaj wynik metodą double.IsFinite** (albo dzielnik przed dzieleniem), zanim podejmiesz na jego podstawie decyzję.

Dzielenie przez zero wraca też w zadaniu rekrutacyjnym „Kalkulator w C#” — tam celowo zamieniamy je na wyjątek.


🎥 Wersja wideo: ten sam przykład krok po kroku — Program .NET Developer #3 — Zasięg: ∞ km
🔴 Zorza leci dalej — na żywo: „Bitwa statków”, gra w C# od zera, w której czat dowodzi statkiem — transmisja 2.11.2026 o 19:00, potem nagranie pod tym samym linkiem
🎓 Skąd ten temat: kurs C# Podstawy Programowania — lekcje o typach wbudowanych, operatorach i metodach typów takich jak double. Kurs to poziom 1 Programu .NET Developer.
📘 Darmowa roadmapa .NET Developera: dev-hobby.pl/lista-vip

Zobacz

Mariusz Jurczenko
Mariusz Jurczenko
Senior .NET Developer · 10+ lat doświadczenia komercyjnego

Programista .NET z doświadczeniem komercyjnym w firmach takich jak NFZ, Kamsoft, Diagnostyka, Hermes Reply Polska czy Etisoft Smart Solutions. Twórca kursów, z których skorzystało już ponad 11 000 osób w Strefie Kursów i ponad 1 000 kursantów na dev-hobby.pl.

Specjalizacja: Clean Code, Clean Architecture i uczenie programowania tak, żeby dało się je naprawdę zrozumieć — nie wykuć.

🚀 Co dalej?

Zobacz to w praktyce na wideo i pobierz darmową roadmapę, żeby ułożyć naukę w spójną ścieżkę do pierwszej pracy.

Dodaj komentarz

czytanie to początek

Zamień wiedzę w umiejętności

Pobierz darmową Roadmapę .NET i ułóż takie tematy jak ten w spójną ścieżkę do pierwszej pracy.

Pobieram roadmapę →