1. Giriş
Elə bilirsən ki, kompüter "ağıllıdır", onda 0.1 + 0.2 sadəcə 0.3 olmalıdır. Amma bu tam belə deyil. Gəlin bunu sadə bir nümunədə yoxlayaq.
double x = 0.1;
double y = 0.2;
double sum = x + y;
Console.WriteLine(sum); // Proqram nə çap edəcək?
İndi isə müqayisə et 0.3 ilə:
Console.WriteLine(sum == 0.3); // Burada true olacaq, yoxsa false?
Əgər False görsən, təəccüblənmə!
Səbəb ədədlərin yaddaşda necə saxlanmasındadır
Kompüterlər ədədlərlə ikilik sistemdə işləyir. Amma heç də bütün onluq kəsrləri sonlu ikilik kəsr kimi yazmaq olmur, elə 1/3 kimi onluq kəsrdə dəqiq yazmaq olmur (0.333...). Məsələn, 0.1 ikilikdə sonsuz kəsrdir və onu yaddaşda saxlamaq üçün "yuvarlaqlaşdırmaq" lazımdır.
Sadə dillə desək, double bəzən "elə göstərir" ki, sənin ədədini tam saxlayır, amma əslində çox yaxın bir təxmini dəyəri saxlayır.
2. double ilə arifmetikada hansı "gülməli şeylər" baş verir?
Gəlin praktik nümunələrə baxaq.
Nümunə 1. Klassik "sehr" 0.1 + 0.2
double a = 0.1;
double b = 0.2;
double sum = a + b;
Console.WriteLine(sum); // 0.30000000000000004
Console.WriteLine(sum == 0.3); // False
Kompüter 0.3 yox, 0.30000000000000004 çap etdi. Fərq kiçikdir, amma məsələn, maliyyə ilə məşğulsansa — bu artıq ciddi məsələdir.
Nümunə 2. İterativ toplama
double result = 0;
for (int i = 0; i < 10; i++)
{
result += 0.1;
}
Console.WriteLine(result); // 0.9999999999999999
1.0 istədin — bir az az aldın. Yenə də səbəb double daxilindəki yuvarlaqlaşdırmadır.
Bu real məsələlərdə niyə vacibdir
Çoxları düşünür: "Nə fərqi var, kiçik səhvdir, nə olsun!" Gəlin ödənişlər dünyasından bir nümunəyə baxaq.
Tutaq ki, sənin internet-bankında 100 əməliyyat 0.1 avro ilə toplanır. Əgər proqramın hər iterasiyada bir yüz mininci avronu "itirirsə", bank səviyyəsində artıq real pul "itirmiş" olacaqsan. Bax, burada mühasib dərhal gəlib soruşacaq: "Bizim pullar hanı?!"
3. Həqiqi ədədləri düzgün necə müqayisə etmək lazımdır
double çox vaxt sənin gözlədiyin dəyəri tam saxlaya bilmir deyə, birbaşa == ilə müqayisə "aldada" bilər. Bunun əvəzinə adətən ədədlərin fərqinin modulunu müəyyən çox kiçik bir ədəd (epsilon) ilə müqayisə edirlər.
Yaxınlıqla müqayisə nümunəsi
double a = 0.1 + 0.2;
double b = 0.3;
double epsilon = 0.000001;
if (Math.Abs(a - b) < epsilon)
{
Console.WriteLine("Demək olar ki, bərabərdir!"); // Belə müqayisə daha təhlükəsizdir
}
Burada deyirik: "Əgər ədədlər arasındakı fərq bir milyonda birindən azdırsa, onları bərabər sayırıq".
4. double üçün xüsusi dəyərlər: Infinity, NaN, -Infinity
double tipi təkcə ədədləri yox, həm də xüsusi dəyərləri saxlayır. Onlar elə vəziyyətlərdə yaranır ki, riyaziyyat müəllimi adi tələbəyə qəti qadağan edərdi.
Sonsuzluq (Infinity)
Bəs 1 bölünsə 0-a nə olar?
double result = 1.0 / 0.0;
Console.WriteLine(result); // Infinity
C#-da (və bir çox dillərdə) double üçün 0-a bölmə istisna atmır! Əvəzində nəticə xüsusi "müsbət sonsuzluq" dəyəri olur.
Minus sonsuzluq (-Infinity)
Əgər mənfi ədəd 0-a bölsən, mənfi sonsuzluq alırsan:
double result = -1.0 / 0.0;
Console.WriteLine(result); // -Infinity
"Ədəd deyil" (NaN — Not a Number)
Əgər tam qəribə bir şey etsən, məsələn, mənfi ədədin kvadrat kökünü hesablamağa çalışsan:
double result = Math.Sqrt(-1);
Console.WriteLine(result); // NaN
Və ya 0.0 / 0.0 bölməsinin nəticəsi:
double result = 0.0 / 0.0;
Console.WriteLine(result); // NaN
NaN — "real həyatda ədəd olmayan hər şey" deməkdir.
Xüsusi dəyərləri yoxlamaq
C#-da xüsusi dəyərləri yoxlamaq üçün funksiyalar var:
Console.WriteLine(double.IsInfinity(result)); // true əgər sonsuzluqsa
Console.WriteLine(double.IsNaN(result)); // true əgər NaN-dırsa
Cədvəl: double qeyri-adi əməliyyatlara necə reaksiya verir
| Əməliyyat | Nəticə | double-da nə saxlanır |
|---|---|---|
|
Infinity | +∞ |
|
-Infinity | -∞ |
|
NaN | Ədəd deyil |
|
NaN | Ədəd deyil |
GO TO FULL VERSION