خطأ منطقي - الإصدار 5.6.30 من لغة PHP لم أجد له حل

السلام عليكم ورحمة الله وبركاته

صادفت اليوم خطأ منطقي شل حركة النظام وهو كالتالي:

var_dump($total_amount);
var_dump($this->TOTAL_AMOUNT);
if( $total_amount != $this->TOTAL_AMOUNT){
    echo  "System price error {$total_amount} != {$this->TOTAL_AMOUNT}";
}
exit();

مخرجات الكود السابق كالتالي وهو ناتج غير منطقي:

float(344586.57) float(344586.57) 
System price error 344586.57 != 344586.57

الناتج يعني أن الشرط محقق مع أن الرقمين متساويين حاولت استخدام معاملات المساواه من نفس النوع !== مع أن القيم في الأساس من نفس النوع وهو float ولكن دون جدوى

  • لم استطع تحديد سبب المُشكلة بعد لأجد لها حل o_O

  • لم أجرب إصدارات أخرى من اللغة لأن النظام يعتمد على بعض الدوال المحذوفة والترقية ستؤدي إلى توقف عمل النظام.

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

التعليقات

Abdulwahab أضف ردا

لا أرى أنه خطأ منطقي وهذا ليس خاصاً بلغة php فقط بل يشمل جميع اللغات (في اعتقادي) لأن المشكلة أكثر تعقيداُ ومتعلقة بالبنية الاساسية التي يقوم عليها عمل أجهزة الحاسب (الأخطاء الشائعة للفاصلة العائمة).

مثال: 1 قسمة ثلاثة، هل تساوي

0.3

أم:

0.33

أم:

0.33333333

في منطق الحاسب الآلي، فإن الأرقام المذكورة أعلاه مختلفة في قيمتها عن بعضها البعض!

لتجاوز هذه المعضلة، يجب دائماً تدوير (تقريب) النواتج إلى عدد محدد من الخانات بعد الفاصلة.

هذا مستند من ميكروسوفت يوضع تفاصيل أكثر مدعومة بأمثلة:

  • الخطأ بدى لي خطأ منطقي قبل الإطلاع على المُشكلة الخاصة بالأعداد ذات الفاصل العامة وكيفية مُعالجتها على الحاسب، بالفعل استخدمت التقريب لتجاوز المشكلة كما أشرت لها في ردي في الأعلى.

  • شكراً لك على تزويدنا بهذا الرابط.

  • طرحت المُشكلة على stackoverflow وتم تحويلي للرابط رقم [1]، فمعاملات المقارنة العادية لا تصلح لمقارنة الأعداد ذات الفاصلة العائمة Float راجع الرابط رقم 2، فعليك إما تقريب القيمة عبر round رابط [3] قبل عملية المساواه أو استخدام أحد الدوال المخصصة لهذا الغرض مثل bccomp رابط [4]

[1]

[2]

[3]

[4]

قمت بتجربة، لكن لم تظهر لي المشكلة! علمــاً بأنني جربت بنفس نسختك 5.6.3 ويمكنك التأكد من ذلك Online

هذا الكود الذي قمت بتجربته.

<?php


$total_amount  =  344586.57;
define('TOTAL_AMOUNT', 344586.57);


var_dump( $total_amount );
var_dump( TOTAL_AMOUNT );


if ( TOTAL_AMOUNT !== $total_amount ) {
    echo 'are not equal!';
} else {
    echo 'are equal!';
}
  • المشكلة تكمن في العمليات التي تتم على القيم قبل مقارنة الناتج النهائي مثال أوضح على ما حدث معي في الكود الخاص بي:

    $var1 = 0.3;
    $var2 = 0.1 + 0.2;
    if($var1 == $var2){
        echo 'Yes';
    }else{
        echo 'No';
    }
    echo '<br>';
    echo sprintf('%.20F', $var1);
    echo '<br>';
    echo sprintf('%.20F', $var2);
    

    المخرجات

    No
    0.29999999999999998890
    0.30000000000000004441
    

لهذا لا يُمكن الإعتماد على معاملات المساواة العادية في مقارنة الأرقام ذات الفاصلة العائمة، راجع الروابط التي طرحتها في ردي على الموضوع

أشكرك لتخصيص جزء من وقتك للإجابة على سؤالي

independent أضف ردا

فهمــت قصــدك، جــرب استخـــدام دالــة bccomp بــلاً من === كمـا في المثــال

<?php

$var1 = 0.3;
$var2 = 0.1 + 0.2;


switch ( bccomp($var1, $var2, 14) )
{
    case 0:
        echo 'Equal';
    break;

    default:
        echo 'Not Equal!';
}


echo '<hr>';
echo 'var1: '; var_dump($var1);
echo ', var2: '; var_dump($var2);

طبعــاً لا أنصــح أبداً تطبيــق هذا عمليــاً على (معامــلات ماليـّــة) الأفضـــل أن يكون الرقم عبــارة عن رقميــن من نوع Integer، إذا رغبــت في شرح أكثــر عن هذه الطريقــة، سوف أفعل.

الأفضـــل أن يكون الرقم عبــارة عن رقميــن من نوع Integer

  • أظن هذا سيعقد الأمر، المعاملات المالية لا تتجاوز الفاصلة العائمة رقمين عشريين واستخدام دالة round بفاصلة بعد رقمين عشريين ستفي بالغرض.