JavaRush /جاوا بلاگ /Random-SD /حقيقي انگن جو اوزار

حقيقي انگن جو اوزار

گروپ ۾ شايع ٿيل
سلام! اڄ جي ليڪچر ۾ اسين جاوا ۾ انگن بابت ڳالهائينداسين، ۽ خاص طور تي حقيقي انگن بابت. حقيقي انگن جو اوزار - 1پريشان نہ ٿيو! :) ليڪچر ۾ ڪا به رياضياتي مشڪلات نه هوندي. اسان حقيقي انگن جي باري ۾ ڳالهائينداسين خاص طور تي اسان جي "پروگرامر" نقطه نظر کان. تنهن ڪري، "حقيقي انگ" ڇا آهن؟ حقيقي انگ اکر هوندا آهن جن ۾ جزوي حصو هوندو آهي (جيڪو صفر ٿي سگهي ٿو). اهي مثبت يا منفي ٿي سگهن ٿا. هتي ڪجهه مثال آهن: 15 56.22 0.0 1242342343445246 -232336.11 حقيقي نمبر ڪيئن ڪم ڪندو آهي؟ بلڪل سادو: اهو هڪ عددي حصو، هڪ جزوي حصو ۽ هڪ نشاني تي مشتمل آهي. مثبت انگن لاء نشاني عام طور تي واضح طور تي ظاهر نه ڪيو ويو آهي، پر منفي نمبرن لاء اهو اشارو ڪيو ويندو آهي. اڳي، اسان تفصيل سان جانچيو ته جاوا ۾ نمبرن تي ڪهڙا آپريشن ڪري سگهجن ٿا. انهن ۾ ڪيترائي معياري رياضياتي عمل هئا - اضافي، ذيلي، وغيره. اتي پڻ توهان لاء ڪجهه نوان هئا: مثال طور، تقسيم جي باقي. پر ڪمپيوٽر جي اندر انگن سان ڪم ڪرڻ جو ڪم ڪيئن ٿيندو؟ اهي ڪهڙي شڪل ۾ ياداشت ۾ محفوظ آهن؟

ياداشت ۾ حقيقي انگن کي محفوظ ڪرڻ

مان سمجهان ٿو ته اهو توهان لاءِ دريافت نه ٿيندو ته انگ وڏا ۽ ننڍا ٿي سگهن ٿا :) انهن جو هڪ ٻئي سان مقابلو ڪري سگهجي ٿو. مثال طور، نمبر 100 نمبر 423324 کان گهٽ آهي. ڇا اهو ڪمپيوٽر جي آپريشن ۽ اسان جي پروگرام کي متاثر ڪري ٿو؟ حقيقت ۾ - ها . هر نمبر کي جاوا ۾ قدرن جي مخصوص رينج سان ظاھر ڪيو ويندو آھي :
قسم ياداشت جي ماپ (بٽ) قدرن جي حد
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. بلڪل عجيب. آخرڪار، اهو ظاهر ٿئي ٿو ته انهن ٻن قسمن جي وچ ۾ ڪوبه فرق ناهي، ڇو ته اهي ساڳيو ڪم ڪن ٿا؟ پر اتي هڪ فرق آهي. مٿي ڏنل جدول ۾ "ميموري ۾ ماپ" ڪالمن تي ڌيان ڏيو. سڀ انگ (۽ نه رڳو نمبر - عام طور تي سڀ معلومات) ڪمپيوٽر جي ياداشت ۾ بٽ جي صورت ۾ محفوظ ٿيل آهن. A bit معلومات جو ننڍڙو يونٽ آهي. اهو تمام سادو آهي. ڪو به بٽ يا ته 0 يا 1 جي برابر هوندو آهي. ۽ لفظ " bit " خود انگريزيءَ مان نڪتل آهي " binary digit " - هڪ بائنري نمبر. منهنجو خيال آهي ته توهان شايد ٻڌو هوندو رياضي ۾ بائنري نمبر سسٽم جي وجود بابت. ڪو به ڊيسيمل نمبر جنهن سان اسين واقف آهيون ان کي ظاھر ڪري سگھجي ٿو ھڪڙي سيٽ ۽ صفر جي طور تي. مثال طور، بائنري ۾ نمبر 584.32 هن طرح نظر ايندو: 100100100001010001111 . هن نمبر ۾ هر هڪ ۽ صفر هڪ الڳ بٽ آهي. هاڻي توهان کي ڊيٽا جي قسمن جي وچ ۾ فرق بابت وڌيڪ واضح ٿيڻ گهرجي. مثال طور، جيڪڏهن اسان هڪ قسم جو تعداد ٺاهيندا آهيون float، اسان وٽ صرف 32 بٽ آهن. جڏهن ڪو نمبر ٺاهيو وڃي floatته ڪمپيوٽر جي ميموري ۾ ان لاءِ ڪيتري جاءِ مختص ڪئي ويندي. جيڪڏهن اسان نمبر ٺاهڻ چاهيون ٿا 123456789.65656565656565، بائنري ۾ اهو هن طرح نظر ايندو: 11101011011110011010001010110101000000 . اهو 38 ون ۽ صفر تي مشتمل آهي، يعني 38 بٽس ان کي ميموري ۾ رکڻ لاءِ گهربل آهن. هي نمبر صرف قسم ۾ float”فٽ“ نه هوندو ! تنهن ڪري، نمبر 123456789 هڪ قسم جي طور تي نمائندگي ڪري سگهجي ٿو double. جيئن ته 64 بٽ ان کي ذخيرو ڪرڻ لاء مختص ڪيا ويا آهن: هي اسان کي مناسب آهي! يقينا، قدرن جي حد به مناسب هوندي. سھولت لاءِ، توھان ھڪڙي نمبر کي سيلز سان گڏ ھڪڙو ننڍڙو دٻو سمجھي سگھو ٿا. جيڪڏهن هر بٽ کي ذخيرو ڪرڻ لاء ڪافي سيلز آهن، پوء ڊيٽا جو قسم صحيح طور تي چونڊيو ويو آهي :) حقيقي انگن جي ڊوائيس - 2يقينا، مختص ڪيل ياداشت جي مختلف مقدار پڻ پاڻ کي نمبر تي اثر انداز ڪري ٿو. مھرباني ڪري نوٽ ڪريو ته قسمن ۾ قدرن جون مختلف حدون floatآھن . doubleهن عمل ۾ ڇا مطلب آهي؟ هڪ عدد doubleهڪ عدد کان وڌيڪ درستي جو اظهار ڪري سگهي ٿو float. 32-bit فلوٽنگ پوائنٽ نمبرز (جاوا ۾ اهو بلڪل قسم آهي float) تقريبن 24 بٽ جي درستگي آهي، يعني اٽڪل 7 ڊيسيمل جايون. ۽ 64-bit نمبر (جاوا ۾ هي قسم آهي 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 آهي، ۽ اسان ان ۾ 0.1111111111111111 7 ڀيرا شامل ڪندا آهيون. نتيجو هجڻ گهرجي 0.7777777777777777. پر اسان ھڪڙو نمبر ٺاھيو آھي float. ان جي ماپ 32 بٽس تائين محدود آهي ۽، جيئن اسان اڳ ۾ چيو آهي، اهو 7 هين ڊيسيمل جڳهه تائين هڪ نمبر کي ظاهر ڪرڻ جي قابل آهي. تنهن ڪري، آخر ۾، نتيجو جيڪو اسان ڪنسول ۾ حاصل ڪيو اهو مختلف هوندو جيڪو اسان توقع ڪئي هئي:

0.7777778
انگ ”ڪٽ آف“ ٿيڻ لڳي. توهان اڳ ۾ ئي ڄاڻو ٿا ته ڊيٽا ڪيئن ميموري ۾ ذخيرو ٿيل آهي - بٽ جي صورت ۾، تنهنڪري اهو توهان کي حيران نه ڪرڻ گهرجي. اهو واضح آهي ته اهو ڇو ٿيو آهي: نتيجو 0.7777777777777777 اسان لاءِ مختص ڪيل 32 بٽس ۾ صرف مناسب نه هو، تنهنڪري ان کي ٽائيپ ويريبل ۾ فٽ ڪرڻ لاءِ ڪٽيو ويو float:) اسان پنهنجي مثال ۾ ويريبل جي قسم کي تبديل ڪري سگهون ٿا double، ۽ پوءِ فائنل نتيجو نه ڪٽيو ويندو:
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 decimal جايون آهن، نتيجو "فٽ" 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 کي ان ۾ ڏھ ڀيرا شامل ڪريو. سڀ ڪجهه صحيح لڳي ٿو، اهو هڪ هجڻ گهرجي. هن ڪوڊ کي هلائڻ جي ڪوشش ڪريو، ۽ جواب توهان کي حيران ڪري ڇڏيندو :) ڪنسول آئوٽ:

0.9999999999999999
پر اهڙي سادي مثال ۾ غلطي ڇو ٿي؟ O_o هتي پنجين درجي جو شاگرد به آساني سان صحيح جواب ڏئي سگهي ٿو، پر جاوا پروگرام هڪ غلط نتيجو پيدا ڪيو. ”غلط“ هتي ”غلط“ کان بهتر لفظ آهي. اسان اڃا تائين ھڪڙو نمبر حاصل ڪيو آھي ھڪڙي جي تمام ويجھو، ۽ نه رڳو ڪجھ بي ترتيب قدر :) اھو مختلف آھي صحيح ھڪڙي کان لفظي طور تي ھڪڙي ملي ميٽر جي. پر ڇو؟ شايد اهو صرف هڪ ڀيرو غلطي آهي. ٿي سگهي ٿو ڪمپيوٽر خراب ٿي ويو؟ اچو ته هڪ ٻيو مثال لکڻ جي ڪوشش ڪريون.
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 не равны!
تنهن ڪري، اهو واضح طور تي ڪمپيوٽر جي خرابين جو معاملو ناهي :) ڇا ٿي رهيو آهي؟ انهن قسمن جون غلطيون ڪمپيوٽر جي ميموري ۾ بائنري شڪل ۾ انگن جي نمائندگي ڪرڻ سان لاڳاپيل آهن. حقيقت اها آهي ته بائنري سسٽم ۾ اهو ناممڪن آهي ته صحيح نموني نمبر 0.1 . رستي ۾، decimal سسٽم ۾ به ساڳيو مسئلو آهي: اهو ناممڪن آهي ته فرقن کي درست نموني پيش ڪرڻ (۽ ⅓ جي بدران اسان کي 0.33333333333333...، جيڪو پڻ بلڪل صحيح نتيجو ناهي). اهو هڪ ننڍڙو لڳي ٿو: اهڙي حساب سان، فرق هڪ لک هزار حصو (0.00001) يا ان کان به گهٽ ٿي سگهي ٿو. پر ڇا جيڪڏهن توهان جي تمام سنجيده پروگرام جو پورو نتيجو هن مقابلي تي منحصر آهي؟
if (f1 == f2)
   System.out.println("Rocket flies into space");
else
   System.out.println("The launch is canceled, everyone goes home");
اسان واضح طور تي توقع ڪئي ته ٻه انگ برابر هوندا، پر اندروني ياداشت جي ڊيزائن جي ڪري، اسان راکٽ لانچ کي منسوخ ڪيو. حقيقي انگن جي ڊوائيس - 3جيڪڏهن ائين آهي، ته اسان کي اهو فيصلو ڪرڻو پوندو ته ٻن فلوٽنگ پوائنٽ نمبرن جو مقابلو ڪيئن ڪجي ته جيئن مقابلي جو نتيجو وڌيڪ هجي... ummm... اڳڪٿي ڪري سگهجي ٿو. تنهن ڪري، اسان اڳ ۾ ئي قاعدو نمبر 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مستقبل لاء ڪلاس ياد رکو ، توهان کي ضرور ان جي ضرورت پوندي :) Phew! ليڪچر ڪافي ڊگهو هو، پر توهان اهو ڪيو: چڱو ٿيو! :) توهان کي ايندڙ سبق ۾ ملنداسين، مستقبل جي پروگرامر!
تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION