JavaRush /Java блогу /Random-KY /Чыныгы сандар аппараты

Чыныгы сандар аппараты

Группада жарыяланган
Салам! Бүгүнкү лекцияда биз Java тorндеги сандар, тагыраак айтканда реалдуу сандар жөнүндө сүйлөшөбүз. Чыныгы сандар аппараты - 1Дүрбөлөң түшпөө! :) Лекцияда математикалык кыйынчылыктар болбойт. Биз чыныгы сандар жөнүндө биздин "программист" көз карашыбыздан гана сүйлөшөбүз. Ошентип, "чыныгы сандар" деген эмне? Чыныгы сандар бөлчөк бөлүгү бар сандар (нөл болушу мүмкүн). Алар оң же терс болушу мүмкүн. Бул жерде кээ бир мисалдар: 15 56,22 0,0 1242342343445246 -232336,11 Чыныгы сан кандай иштейт? Абдан жөнөкөй: ал бүтүн бөлүктөн, бөлчөк бөлүктөн жана белгиден турат. Оң сандар үчүн белги адатта ачык көрсөтүлбөйт, ал эми терс сандар үчүн ал көрсөтүлөт. Буга чейин биз Java тorнде сандар боюнча кандай операцияларды жасоого болорун майда-чүйдөсүнө чейин карап чыкканбыз . Алардын арасында көптөгөн стандарттуу математикалык операциялар бар эле - кошуу, кемитүү ж. Бирок сандар менен иштөө компьютердин ичинде кантип иштейт? Алар эс тутумда кандай формада сакталат?

Эстутумда реалдуу сандарды сактоо

Сандардын чоң жана кичине болушу сиз үчүн ачылыш болбойт деп ойлойм :) Аларды бири-бири менен салыштырууга болот. Мисалы, 100 саны 423324 санынан кичине. Бул компьютердин жана биздин программанын иштешине таасирин тийгизеби? Чынында - ооба . Ар бир сан Java тorнде белгилүү бир маани диапазону менен көрсөтүлөт :
Type Эстутум өлчөмү (бит) Маанилердин диапазону
byte 8 бит -128ден 127ге чейин
short 16 бит -32768ден 32767ге чейин
char 16 бит UTF-16 белгисин билдирген белгиси жок бүтүн сан (тамгалар жана сандар)
int 32 бит -2147483648ден 2147483647ге чейин
long 64 бит -9223372036854775808ден 9223372036854775807ге чейин
float 32 бит 2 -149дан (2-2 -23 )*2 127ге чейин
double 64 бит 2 -1074 чейин (2-2 -52 )*2 1023
Бүгүн биз акыркы эки түрү жөнүндө сүйлөшөбүз - floatжана double. Экөө тең бирдей тапшырманы аткарышат - бөлчөк сандарды көрсөтүү. Алар ошондой эле көбүнчө " калкып келүүчү чекит сандары" деп аталат . Келечекте бул терминди эстеңиз :) Мисалы, 2.3333 же 134.1212121212 саны. Абдан кызык. Анткени, бул эки түрдүн ортосунда эч кандай айырма жок экен, анткени алар бир эле милдетти аткарышат? Бирок, бир айырмасы бар. Жогорудагы tableдагы "эсте турган өлчөмдөр" тилкесине көңүл буруңуз. Бардык сандар (жана сандар эле эмес - жалпысынан бардык маалыматтар) компьютердин эсинде бит түрүндө сакталат. Бит - маалыматтын эң кичине бирдиги. Бул абдан жөнөкөй. Каалаган бит 0 же 1ге барабар. Ал эми “ бит ” деген сөздүн өзү англисче “ binary digit ” – экorк сандан келип чыккан. Менимче, сиз математикада экorк санауу системасы бар экенин уккандырсыз. Бизге тааныш болгон ар кандай ондук сан бирдиктердин жана нөлдөрдүн жыйындысы катары көрсөтүлүшү мүмкүн. Мисалы, бинардык системада 584,32 саны мындай болот: 100100100001010001111 . Бул сандагы ар бири жана нөл өзүнчө бит. Эми сиз маалымат түрлөрүнүн ортосундагы айырманы ачык-айкын болушуңуз керек. Мисалы, эгер биз бир нече типти түзсөк float, биздин карамагыбызда 32 бит гана болот. Номерди түзүүдө floatкомпьютердин эс тутумунда ага канча орун бөлүнөт. Эгерде биз 123456789.65656565656565 санын түзгүбүз келсе, бинардык режимде ал төмөнкүдөй болот: 11101011011110011010001010110101000000 . Ал 38 бирдиктен жана нөлдөн турат, башкача айтканда, аны эс тутумда сактоо үчүн 38 бит керек. Бул сан жөн гана түргө float"туура алbyte" ! Демек, 123456789 саны түрү катары көрсөтүлүшү мүмкүн double. Аны сактоо үчүн 64 бит бөлүнгөн: бул бизге ылайыктуу! Албетте, баалуулуктардын диапазону да ылайыктуу болот. Ыңгайлуу болуу үчүн, сиз санды уячалары бар кичинекей кутуча деп ойлосоңуз болот. Эгерде ар бир битти сактоо үчүн уячалар жетиштүү болсо, анда маалымат түрү туура тандалган :) Чыныгы сандар аппараты - 2Албетте, бөлүнгөн эстутумдун ар кандай көлөмү сандын өзүнө да таасир этет. Типтердин ар кандай маани диапазону floatбар экенин эске алыңыз . doubleбул иш жүзүндө эмнени билдирет? Сан doubleсанга караганда көбүрөөк тактыкты билдире алат float. 32 биттик калкыма чекит сандары (Javaда дал ушундай түрү float) болжол менен 24 бит тактыкка ээ, башкача айтканда, болжол менен 7 ондук орун. Ал эми 64 биттик сандар (Javaда бул түрү double) болжол менен 53 бит тактыкка ээ, башкача айтканда, болжол менен 16 ондук орун. Бул айырмачылыкты жакшы көрсөткөн бир мисал:
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);
   }
}
Натыйжада бул жерден эмне алышыбыз керек? Бул баары абдан жөнөкөй көрүнөт. Бизде 0,0 деген сан бар, ага 7 жолу катары менен 0,1111111111111111 кошобуз. Натыйжа 0,777777777777777 болушу керек. Бирок биз номер түздүк float. Анын көлөмү 32 бит менен чектелген жана биз мурда айткандай, ал болжол менен 7-ондук орунга чейинки сандарды көрсөтүүгө жөндөмдүү. Ошентип, акырында, биз консолдо алган натыйжа биз күткөндөн башкача болот:

0.7777778
Номер "кесилген" окшойт. Сиз маалымат эстутумда кантип сакталаарын билесиз - бит түрүндө, андыктан бул сизди таң калтырбашы керек. Эмне үчүн мындай болгону түшүнүктүү: 0.777777777777777 натыйжасы бизге бөлүнгөн 32 битке туура келген жок, ошондуктан ал түр өзгөрмөсүнө туура келүү үчүн кыскартылды float:) Биздин мисалдагы өзгөрмөнүн түрүн өзгөртө алабыз double, анан акыркы натыйжа кыскартылbyte:
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
Буга чейин 16 ондук орун бар, натыйжа 64 битке "турат". Баса, балким, сиз эки учурда тең жыйынтыктар туура эмес экенин байкадыңызбы? Эсептөө кичине каталар менен жасалган. Мунун себептери тууралуу төмөндө сөз кылабыз :) Эми сандарды бири-бири менен салыштыруу боюнча бир нече сөз айта кетели.

Чыныгы сандарды салыштыруу

Биз бул маселеге өткөн лекцияда, салыштыруу операциялары жөнүндө сөз кылганда жарым-жартылай токтолгонбуз. >, <, сыяктуу операцияларды кайра талдабайбыз >=. <=Анын ордуна кызыктуураак мисалды карап көрөлү:
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);
   }
}
Экранда кандай сан чыгат деп ойлойсуз? Жооп логикалык жооп болмок: 1 саны. Биз 0,0 санынан баштап санай баштайбыз жана ага катары менен он жолу 0,1 кошобуз. Баары туура окшойт, бир болушу керек. Бул codeду иштетип көрүңүз, жооп сизди абдан таң калтырат :) Console output:

0.9999999999999999
Бирок эмне үчүн мынчалык жөнөкөй мисалда ката кетти? O_o Бул жерде атүгүл бешинчи класстын окуучусу да оңой эле туура жооп бере алат, бирок Java программасы так эмес жыйынтык чыгарды. Бул жерде "туура эмес" дегенге караганда "так эмес" жакшыраак. Биз дагы эле бир кокустук эмес, бирге жакын санды алдык :) Бул туурасынан түзмө-түз бир миллиметр менен айырмаланат. Бирок эмне үчүн? Балким, бул бир жолку катадыр. Балким, компьютер бузулуп калган? Дагы бир мисал жазганга аракет кылалы.
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!");
   }
}
Консолдук чыгаруу:

f1 = 1.0999999999999999
f2 = 1.1
f1 и f2 не равны!
Демек, бул компьютердеги катачылыктарга байланыштуу эмес :) Эмне болуп жатат? Каталардын бул түрлөрү сандардын компьютердин эс тутумунда экorк формада көрсөтүлүшүнө байланыштуу. Чындыгында, экorк системада 0,1 санын так көрсөтүү мүмкүн эмес . Айтмакчы, ондук системада да ушундай көйгөй бар: бөлчөктөрдү туура көрсөтүү мүмкүн эмес (жана ⅓ ордуна биз 0,333333333333333... алабыз, бул да так жыйынтык эмес). Бул майда-чүйдөдөй көрүнөт: мындай эсептөөлөр менен айырма жүз миңден бир бөлүгү (0,00001) же андан да аз болушу мүмкүн. Бирок, абдан олуттуу программаңыздын бүт натыйжасы ушул салыштырууга көз каранды болсочу?
if (f1 == f2)
   System.out.println("Rocket flies into space");
else
   System.out.println("The launch is canceled, everyone goes home");
Биз эки сандын бирдей болушун күткөнбүз, бирок ички эс тутумдун дизайнына байланыштуу ракета учурууну жокко чыгардык. Чыныгы сандар аппараты - 3Андай болсо, биз эки калкыма чекиттүү сандарды кантип салыштырууну чечишибиз керек, ошондуктан салыштыруунун натыйжасы көбүрөөк... ммм... алдын ала айтууга болот. Ошентип, биз чыныгы сандарды салыштырууда №1 эрежени үйрөндүк: чыныгы сандарды салыштырганда эч качан калкыма чекиттүү сандарды колдонбоңуз . == Макул, жаман мисалдар жетиштүү деп ойлойм :) Жакшы мисалды карап көрөлү!
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");
   }
}
Бул жерде биз бир эле нерсени кылып жатабыз, бирок сандарды салыштыруу ыкмасын өзгөртөбүз. Бизде өзгөчө "босого" саны бар - 0,0001, он миңден бир. Бул башкача болушу мүмкүн. Бул белгилүү бир учурда канчалык так салыштыруу керек экендигине жараша болот. Сиз аны чоңойтуп же кичирейте аласыз. Методду колдонуп, Math.abs()сандын модулун алабыз. Модул белгиге карабастан сандын мааниси. Мисалы, -5 жана 5 сандары бирдей модулга ээ болуп, 5ке барабар болот. Биринчиден экинчи санды алып салабыз, эгерде алынган натыйжа белгиге карабастан, биз койгон босогодон аз болсо, анда сандарыбыз бирдей. Кандай болгон күндө да, алар биздин “босого сан” аркылуу белгилеген тактык даражасына барабар , башкача айтканда, алар эң аз дегенде он миңден бирине барабар. Бул салыштыруу ыкмасы сизди күтүлбөгөн жүрүм-турумдан куткарат ==. Чыныгы сандарды салыштыруунун дагы бир жакшы жолу - атайын классты колдонуу BigDecimal. Бул класс өзгөчө бөлчөк бөлүгү менен абдан чоң сандарды сактоо үчүн түзүлгөн. doubleЖанадан айырмаланып float, кошууну колдонууда кемитүү жана башка математикалык амалдар ( , ж.б.) BigDecimalоператорлор аркылуу эмес, методдор менен аткарылат . +-Бул биздин учурда ушундай болот:
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");
   }
}
Кандай консолдук продукцияны алабыз?

f1 = 1.1000000000000000610622663543836097232997417449951171875
f2 = 1.1000000000000000610622663543836097232997417449951171875
f1 и f2 равны
Биз так күткөн жыйынтыкка ээ болдук. Жана биздин сандар канчалык так чыкканына жана аларга канча ондук орун туура келгенине көңүл буруңуз! floatАл тургай, андан алда канча көп double! BigDecimalКелечекте классты унутпаңыз , сизге сөзсүз керек болот :) Фу! Лекция бир топ узун болду, бирок сиз аны аткардыңыз: молодец! :) Кийинки сабакта көрүшкөнчө, болочок программист!
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION