JavaRush /Java Blog /Random-TK /Hakyky sanlaryň enjamy

Hakyky sanlaryň enjamy

Toparda çap edildi
Salam! Şu günki leksiýamyzda Java-daky sanlar we esasanam hakyky sanlar barada gürleşeris. Hakyky sanlaryň enjamy - 1Aljyrama! :) Leksiýada matematiki kynçylyklar bolmaz. Hakyky sanlar hakda diňe “programmist” nukdaýnazaryndan gürleşeris. Onda “hakyky sanlar” näme? Hakyky sanlar fraksiýa bölegi bolan sanlardyr (nol bolup biler). Oňyn ýa-da otrisatel bolup biler. Ine käbir mysallar: 15 56.22 0.0 1242342343445246 -232336.11 Hakyky san nähili işleýär? Örän ýönekeý: bitewi bölekden, bölek bölekden we alamatdan ybarat. Oňyn sanlar üçin alamat adatça aç-açan görkezilmeýär, ýöne otrisatel sanlar üçin görkezilýär. Mundan ozal, Java-da sanlar boýunça haýsy amallary ýerine ýetirip boljakdygyny jikme-jik gözden geçirdik . Olaryň arasynda köp sanly matematiki amallar - goşmak, aýyrmak we ş.m. Siziň üçin käbir täzeleri hem bardy: mysal üçin bölünişigiň galan bölegi. Numbersöne sanlar bilen işlemek kompýuteriň içinde nädip işleýär? Haýsy görnüşde ýatda saklanýar?

Hakyky sanlary ýatda saklamak

Sanlaryň uly we kiçi bolup biljekdigi siziň üçin açyş bolmaz diýip pikir edýärin :) Olary biri-biri bilen deňeşdirip bolar. Mysal üçin, 100 sany 423324 belgiden az. Bu kompýuteriň we programmamyzyň işleýşine täsir edýärmi? Aslynda - hawa . Her san Java-da belli bir bahalar bilen görkezilýär :
Görnüşi Oryadyň ululygy (bitler) Gymmatlyklaryň diapazony
byte 8 bit -128-den 127-e çenli
short 16 bit -32768-den 32767-e çenli
char 16 bit UTF-16 nyşany (harplar we sanlar) aňladýan gol çekilmedik bitewi san
int 32 bit -2147483648-den 2147483647-e çenli
long 64 bit -9223372036854775808-den 9223372036854775807-e çenli
float 32 bit 2 -149- dan (2-2 -23 ) * 2 127-e çenli
double 64 bit 2 -1074 -den (2-2 -52 ) * 2 1023-e çenli
Bu gün soňky iki görnüş hakda gürleşeris - floatwe double. Ikisi-de birmeňzeş işi ýerine ýetirýärler - fraksiýa sanlaryny görkezýärler. Olara köplenç “ ýüzýän nokat sanlary” diýilýär . Geljek üçin bu termini ýadyňyzda saklaň :) Mysal üçin, 2.3333 ýa-da 134.1212121212 belgisi. Gaty geň. Galyberse-de, şol bir işi ýerine ýetirýändikleri üçin bu iki görnüşiň arasynda hiç hili tapawut ýokmy? Emma tapawut bar. Aboveokardaky tablisadaky “ýadyň ululygy” sütünine üns beriň. Numbershli sanlar (we diňe sanlar däl - umuman ähli maglumatlar) kompýuter ýadynda bit görnüşinde saklanýar. Biraz maglumatyň iň kiçi birligi. Bu gaty ýönekeý. Islendik bit 0 ýa-da 1-e deňdir we “ bit ” sözüniň özi iňlis dilindäki “ ikilik san ” - ikilik belgiden gelýär. Meniň pikirimçe, matematikada ikilik san ulgamynyň barlygy hakda eşiden bolsaňyz gerek. Biziň bilen tanyş bolan islendik onluk san sanlaryň we nollaryň toplumy hökmünde görkezilip bilner. Mysal üçin, ikilikdäki 584.32 belgisi şeýle bolar: 100100100001010001111 . Bu sanyň her biri we noly aýratyn. Indi maglumat görnüşleriniň arasyndaky tapawut barada has aýdyň bolmaly. Mysal üçin, birnäçe görnüşi döreden bolsak float, diňe 32 bit bar. San döredilende, floatkompýuteriň ýadynda onuň üçin näçeräk ýer bölünjekdigi hut şu. 123456789.6565656565656565 belgisini döretmek islesek, ikilik görnüşinde şeýle bolar : 11101011011110011010001010110101000000 38 we noldan ybarat, ýagny ýatda saklamak üçin 38 bit gerek. Bu san görnüşine float“laýyk gelmez” ! Şonuň üçin 123456789 belgisini bir görnüş hökmünde görkezip bolýar double. Saklamak üçin 64 bit bölünip berilýär: bu bize laýyk gelýär! Elbetde, gymmatlyklaryň diapazony hem amatly bolar. Amatly bolmak üçin, sanlary öýjükler bilen kiçijik guty diýip pikir edip bilersiňiz. Her biti saklamak üçin ýeterlik öýjük bar bolsa, maglumatlaryň görnüşi dogry saýlanýar :) Hakyky sanlaryň enjamy - 2Elbetde, bölünen ýadyň dürli mukdary hem sanyň özüne täsir edýär. Görnüşleriň dürli bahalaryň floatbardygyny ýadyňyzdan çykarmaň. doubleIş ýüzünde bu nämäni aňladýar? San doublesanlardan has takyklygy görkezip biler float. 32 bitli ýüzýän nokat sanlary (Java-da bu görnüşiň görnüşi float) takmynan 24 bit, ýagny 7 onluk ýer takyklygy bar. 64 bitli sanlar (Java-da bu görnüş double) takmynan 53 bit, ýagny takmynan 16 onluk ýer bar. Ine, bu tapawudy gowy görkezýän mysal:
public class Main {

   public static void main(String[] args)  {

       float f = 0.0f;
       for (int i=1; i <= 7; i++) {
           f += 0.1111111111111111;
       }

       System.out.println(f);
   }
}
Netijede bu ýere näme almaly? Hemme zat gaty ýönekeý ýaly. Bizde 0.0 belgisi bar we oňa yzly-yzyna 7 gezek 0.111111111111111111 goşýarys. Netije 0.7777777777777777 bolmaly. Emma san döretdik float. Onuň ululygy 32 bit bilen çäklenýär we öňem aýdyşymyz ýaly, takmynan 7-nji onluk ýere çenli san görkezmäge ukyply. Şonuň üçin ahyrynda konsolda alýan netijämiz garaşyşymyzdan tapawutly bolar:

0.7777778
Bu san “kesilen” ýalydy. Maglumatlaryň ýatda nähili saklanýandygyny eýýäm bilýärsiňiz - bit görnüşinde, bu sizi geň galdyrmaly däldir. Munuň näme üçin bolup geçendigi düşnüklidir: 0.7777777777777777 netijesi diňe bize bölünen 32 bit bilen gabat gelmedi, şonuň üçin görnüş üýtgeýjisine gabat gelmek üçin kesildi float:) Üýtgeýjiniň görnüşini doublemysalymyzda üýtgedip bileris, soň bolsa jemleýji netije kesilmez:
public class Main {

   public static void main(String[] args)  {

       double f = 0.0;
       for (int i=1; i <= 7; i++) {
           f += 0.1111111111111111;
       }

       System.out.println(f);
   }
}

0.7777777777777779
Eýýäm 16 onluk ýer bar, netijesi 64 bite “laýyk gelýär”. Theeri gelende aýtsak, iki ýagdaýda-da netijeleriň düýbünden dogry däldigine göz ýetirdiňiz? Hasap ownuk ýalňyşlyklar bilen geçirildi. Munuň sebäpleri barada aşakda gürleşeris :) Indi sanlary biri-biri bilen nädip deňeşdirip boljakdygy barada birnäçe söz aýdalyň.

Hakyky sanlary deňeşdirmek

Geçen leksiýada deňeşdirme amallary barada aýdanymyzda, bu meselä bölekleýin degip geçdik. >,,, operationsaly amallary täzeden seljeremzok <. Munuň ýerine has gyzykly mysala seredeliň: >=<=
public class Main {

   public static void main(String[] args)  {

       double f = 0.0;
       for (int i=1; i <= 10; i++) {
           f += 0.1;
       }

       System.out.println(f);
   }
}
Siziň pikiriňizçe ekranda haýsy nomer görkeziler? Mantykly jogap jogap bolar: 1-nji san. 0.0 belgisinden sanap başlaýarys we yzly-yzyna on gezek 0,1 goşýarys. Hemme zat dogry ýaly, bir bolmaly. Bu kody işledip görüň, jogap sizi gaty geň galdyrar :) Konsol çykyşy:

0.9999999999999999
Emma näme üçin beýle ýönekeý mysalda ýalňyşlyk ýüze çykdy? O_o Bu ýerde hatda bäşinji synp okuwçysy hem aňsatlyk bilen dogry jogap berip bilerdi, ýöne Java programmasy nädogry netije berdi. “Nädogry” bu ýerde “nädogry” sözlerden has gowy söz. Diňe bir tötänleýin baha däl-de, birine gaty ýakyn bir san aldyk :) Dogrysyndan millimetr bilen tapawutlanýar. Emma näme üçin? Belki, bu diňe bir gezeklik ýalňyşlykdyr. Belki, kompýuter ýykyldy? Başga bir mysal ýazmaga synanyşalyň.
public class Main {

   public static void main(String[] args)  {

       //add 0.1 to zero eleven times in a row
       double f1 = 0.0;
       for (int i = 1; i <= 11; i++) {
           f1 += .1;
       }

       // Multiply 0.1 by 11
       double f2 = 0.1 * 11;

       //should be the same - 1.1 in both cases
       System.out.println("f1 = " + f1);
       System.out.println("f2 = " + f2);

       // Let's check!
       if (f1 == f2)
           System.out.println("f1 and f2 are equal!");
       else
           System.out.println("f1 and f2 are not equal!");
   }
}
Konsol çykyşy:

f1 = 1.0999999999999999
f2 = 1.1
f1 и f2 не равны!
Diýmek, bu kompýuteriň ýalňyşlygy däl. Näme bolýar? Rorsalňyşlyklaryň bu görnüşleri, sanlaryň kompýuteriň ýadynda ikilik görnüşde görkezilişi bilen baglanyşyklydyr. Hakykat, ikilik ulgamynda 0,1 belgini takyk görkezmek mümkin däl . Theeri gelende aýtsak, onluk ulgamyň hem şuňa meňzeş meselesi bar: fraksiýalary dogry görkezmek mümkin däl (we instead ýerine 0.3333333333333333 alarys ... bu hem dogry netije däl). Bu ownuk-uşak ýaly bolup görünýär: şeýle hasaplamalar bilen tapawut ýüz müňden bir bölegi (0,00001) ýa-da ondanam az bolup biler. Veryöne gaty çynlakaý programmaňyzyň netijesi bu deňeşdirmä bagly bolsa näme etmeli?
if (f1 == f2)
   System.out.println("Rocket flies into space");
else
   System.out.println("The launch is canceled, everyone goes home");
Iki sanyň deň boljakdygyna aç-açan garaşýardyk, ýöne içerki ýat dizaýny sebäpli raketa atyşyny ýatyrdyk. Hakyky sanlaryň enjamy - 3Şeýle bolsa, deňeşdirmegiň netijesi has ... ummm ... öňünden aýdyp bolar ýaly iki ýüzýän nokat sanyny nädip deňeşdirmelidigini çözmeli. Şeýlelik bilen, hakyky sanlary deňeşdirenimizde eýýäm 1-nji düzgüni öwrendik: hakyky sanlar bilen deňeşdirilende hiç haçan ýüzýän nokat sanlaryny ulanmaň . == Bolýar, bu ýeterlik erbet mysallardyr öýdýän :) Gowy mysal göreliň!
public class Main {

   public static void main(String[] args)  {

       final double threshold = 0.0001;

       //add 0.1 to zero eleven times in a row
       double f1 = .0;
       for (int i = 1; i <= 11; i++) {
           f1 += .1;
       }

       // Multiply 0.1 by 11
       double f2 = .1 * 11;

       System.out.println("f1 = " + f1);
       System.out.println("f2 = " + f2);

       if (Math.abs(f1 - f2) < threshold)
           System.out.println("f1 and f2 are equal");
       else
           System.out.println("f1 and f2 are not equal");
   }
}
Bu ýerde aslynda şol bir zady edýäris, ýöne sanlary deňeşdirmegiň usulyny üýtgedýäris. Bizde ýörite “bosagada” san bar - 0.0001, on müňden bir. Başgaça bolup biler. Belli bir ýagdaýda deňeşdirmegiň näderejede takykdygyna baglydyr. Uly ýa-da kiçi edip bilersiňiz. Usuly ulanyp, Math.abs()sanyň modulyny alýarys. Modul, belgä garamazdan sanyň bahasydyr. Mysal üçin, -5 we 5 sanlaryň birmeňzeş moduly bolar we 5-e deň bolar. Ikinji belgini birinjisinden aýyrarys, eger netijä, belgä garamazdan, bellenen çäkden az bolsa, onda sanlarymyz deňdir. Her niçigem bolsa, olar “çäk belgimizi” ulanyp kesgitlän takyklyk derejämize deňdir , ýagny iň bolmanda on müňden birine deňdir. Deňeşdirmegiň bu usuly sizi gören garaşylmadyk hereketlerden halas eder ==. Hakyky sanlary deňeşdirmegiň ýene bir gowy usuly, ýörite synp ulanmakdyr BigDecimal. Bu synp bölekleýin bölegi bilen gaty köp sanly saklamak üçin ýörite döredildi. doubleGoşmaçadan tapawutlylykda float, BigDecimalaýyrmak we beýleki matematiki amallar operatorlary (we ş.m.) ulanman +-, usullary ulanmak arkaly ýerine ýetirilýär. Bu biziň ýagdaýymyzda nähili bolar:
import java.math.BigDecimal;

public class Main {

   public static void main(String[] args)  {

       /*Create two BigDecimal objects - zero and 0.1.
       We do the same thing as before - add 0.1 to zero 11 times in a row
       In the BigDecimal class, addition is done using the add () method */
       BigDecimal f1 = new BigDecimal(0.0);
       BigDecimal pointOne = new BigDecimal(0.1);
       for (int i = 1; i <= 11; i++) {
           f1 = f1.add(pointOne);
       }

       /*Nothing has changed here either: create two BigDecimal objects
       and multiply 0.1 by 11
       In the BigDecimal class, multiplication is done using the multiply() method*/
       BigDecimal f2 = new BigDecimal(0.1);
       BigDecimal eleven = new BigDecimal(11);
       f2 = f2.multiply(eleven);

       System.out.println("f1 = " + f1);
       System.out.println("f2 = " + f2);

       /*Another feature of BigDecimal is that number objects need to be compared with each other
       using the special compareTo() method*/
       if (f1.compareTo(f2) == 0)
           System.out.println("f1 and f2 are equal");
       else
           System.out.println("f1 and f2 are not equal");
   }
}
Haýsy konsol çykyşyny alarys?

f1 = 1.1000000000000000610622663543836097232997417449951171875
f2 = 1.1000000000000000610622663543836097232997417449951171875
f1 и f2 равны
Biz garaşýan netijämizi aldyk. Sanlarymyzyň näderejede dogry çykandygyna we näçe onluk ýerlere laýyk gelýändigine üns beriň! floatIçinden we hatda has köp zat double! BigDecimalGeljek üçin synpy ýadyňyzda saklaň , hökman size gerek bolar :) Phew! Leksiýa gaty uzyn, ýöne muny etdiň: gowy! :) Geljekki programmist, indiki sapakda görüşeris!
Teswirler
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION