-1

لماذا لغات البرمجة غبية في حساب الأعداد العشرية

مثال بسيط، لنفترض أن لدينا متغيران a و b قيمتهما على التوالي 0.1 و 0.2 اذا جمع العددين كما درسنا في مادة الحساب 0.3

اي a + b == 03 يجب أن يكون true و هذا ما سيحدت عكسه في أغلب لغات البرمجة اذ سيكون الناتج false

السؤال المطروح للنقاش: ماسبب حدوث هذا. وكيف يتم حله في البرامج و الأنظمة الدقيقة (البورصة مثلا)

يرجى الدخول لحسابك أو تسجيل حساب لتستطيع إضافة تعليق
حساب جديد دخول

التعليقات

طريقة تمثيل المتغيرات الكسرية floating point تختلف حسب معمارية المعالج، وان كنت لم اسى هنالك 3 طرق لتمثيلها في الذاكرة أو في المعالج وهي مختلفة.

لحل هذه المشكلة يستخدم متغير من نوع Decimal بدلا من Double أو Float هذا على الأقل في الدوت نت

يمكنك البحث عن طريقة التمثيل في الذاكرة وستجدها غريبة

المشكلة ان جمع عددين عشريين لا يساوي نفس عد الخانات,

بلنسبة لمثالك جرب a+b == 0.30000000000000004 وستجد true (جربتها من خلال جافا سكربت للسرعة)

وايضاً يمكن حل المشكلة بسهولة باستخدام دوال الرياضيات الموجودة في اللغة وتحتوي العديد من الدوال مثل التقريب والقيمة المطلقة وغيره ...

هي ليست غبية لكن كما تم الاشارة إليه في بعض التعليقات يعتمد المعالج على تمثيل الأعداد العشرية بطريقة اخرى. في العادة تستعمل المعالجات الرياضية المعيار المتبع من طرف IEEE لتمثيل هذه.

في حالة الأعداد العشرية نحن نميل إلى كتابة الأعداد على شكل رقم أٌس 10 مثلا 532e-10 يعني 532 اٌس -10

نفس الشيء بالنسبة للكمبيوتر, تمثل الأعداد على شكل أُس للعدد 2. في معالج 64bits يتم استخدام 52 بت لتمثيل الكسر, 11 بت لتمثيل الأس و 1 بت لتمثيل الاشارة.

---------------------+-----------------+-----------+

| الكسر | الأُس | الاشارة |

---------------------+----------------+------------+

0<------------->51 52<-------->62 63

يتم تمثيل العدد X على شكل : X= الكسر * 2 ^( الاُس - bias)

حيث يتم اظافة الـ bias لجعل قيم الأس موجبة, في حالة 64بت الـ bias = 1023.

في هذا التمثيل لما تقوم بمقارنة الأعداد الكسرة خاصة إذا استلزم الامر القيام بعملية قبلها لن يتساوى العددان بل سيكون فارق EPSILON صغير بينها.

لهذا في البرمجة يمكن أت تحدد الدقة التي تريدها (يعني كم عدد بعد الفاصلة لكي تتعتبر العددين متساويين)

EPSILON= 1.0e-20

a+b - 0.3 <= EPSLON

حل المشكلة باستخدام المكتبة الخاصة بالرياضيات (math) الموجودة بكل لغات البرمجة

ذلك خطأ طباعي فقط فهو يقصد 0.3

جرب التالي

if 0.1+0.2 == 0.3

وسيكون شرطا غير محقق، أي أن قيمة المقارنة false

حولها الى نص تظهر true

    Dim n1 As Double = 0.2
    Dim n2 As Double = 0.1
    Dim nt = n2 + n1
    If nt.ToString = "0.3" Then
        MessageBox.Show("true")
    Else
        MessageBox.Show("false")
    End If

استخدم دوال التقريب لأقرب علامة عشرية

فى php

<?php

    $a = 0.1;
    $b = 0.2;

    if( round( ($a+$b) , 1 ) == 0.3 ){
        echo "aa";
    }

فعلاً التعامل مع الأرقام العشرية مع لغات البرمجة كلها غير مستقر والسبب هو صعوبة التحكم فى التقريب لأقرب رقم عشرى

فمثلا

System.out.println(2.0 - 1.1);
prints 0.8999999999999999
not 0.9

والسبب فى عدم التحكم فى دقة التقريب أن هذه الارقام تنوب عن الأرقام الثنائية للنظام, لذلك لو أردت أن تستخدم فى حساباتك نتائج واقعية أكثر استخدم الكلاس BigDecimal وهذا فى الجافا

-1

أول مره اشوف هالمشكلة

جربتها بالبي اتش بي وكانت نفس المشكلة