سؤال

لأن تطفو نوع البيانات في PHP هو غير دقيقة ، تطفو في الخلية يأخذ مساحة أكثر من INT (و هو غير دقيقة), أنا دائما متجر أسعار رجات ، multipling بنسبة 100 قبل تخزينها لضمان لدينا بالضبط 2 عشرية من الدقة.ومع ذلك أعتقد أن PHP هي الفاسقة.رمز المثال:

echo "<pre>";

$price = "1.15";
echo "Price = ";
var_dump($price);

$price_corrected = $price*100;
echo "Corrected price = ";
var_dump($price_corrected);


$price_int = intval(floor($price_corrected));
echo "Integer price = ";
var_dump($price_int);

echo "</pre>";

أنتجت الإخراج:

Price = string(4) "1.15"
Corrected price = float(115)
Integer price = int(114)

فوجئت.عندما كانت النتيجة النهائية أقل من المتوقع بنسبة 1 ، كنت أتوقع الناتج من اختبار تبدو أكثر مثل:

Price = string(4) "1.15"
Corrected price = float(114.999999999)
Integer price = int(114)

الذي من شأنه أن يبرهن على عدم دقة من نوع float.ولكن لماذا هو الكلمة(115) العودة 114??

هل كانت مفيدة؟

المحلول

جرب هذا كحل سريع:

$price_int = intval(floor($price_corrected + 0.5));

المشكلة كنت تعاني من ليس PHP خطأ جميع لغات البرمجة باستخدام أرقام حقيقية مع النقطة العائمة الحساب لديهم مشاكل مماثلة.

وكقاعدة عامة من الإبهام النقدية حسابات ابدأ استخدام العوامات (لا في قاعدة البيانات ولا في السيناريو الخاص بك).يمكنك تجنب كل أنواع المشاكل دائما تخزين سنتا بدلا من الدولارات.سنتا هي الاعداد الصحيحه, و يمكنك إضافة بحرية معا ، وتتكاثر عن طريق أخرى صحيحة.كلما عرض عدد ، تأكد من إدراج نقطة أمام آخر رقمين.

السبب في حصولك على 114 بدلا من 115 هو أن floor جولات أسفل ، نحو أقرب عدد صحيح ، وهكذا الكلمة(114.999999999) يصبح 114.السؤال الأهم هو لماذا 1.15 * 100 هو 114.999999999 بدلا من 115.والسبب في ذلك هو أن 1.15 ليس بالضبط 115/100 ، وإنما هو القليل جدا أقل ، لذلك إذا كنت مضاعفة بنسبة 100, يمكنك الحصول على عدد قليل أصغر من 115.

هنا شرح أكثر تفصيلا ما echo 1.15 * 100; لا:

  • فإنه يوزع 1.15 إلى ثنائي النقطة العائمة عدد.وهذا ينطوي على التقريب يحدث جولة أسفل قليلا للحصول على ثنائي النقطة العائمة عدد أقرب إلى 1.15.السبب في أنك لا تستطيع الحصول على رقم دقيق (دون خطأ التقريب) هو أن 1.15 لديها عدد لا نهائي من الأرقام في قاعدة 2.
  • فإنه يوزع 100 إلى ثنائي النقطة العائمة عدد.وهذا ينطوي على التقريب ، ولكن منذ 100 هي عدد صحيح صغير ، التقريب الخطأ صفر.
  • فإنه يحسب المنتج العددين السابقين.وهذا ينطوي أيضا على القليل من التقريب إلى أقرب ثنائي النقطة العائمة عدد.التقريب خطأ يحدث أن تكون صفر في هذه العملية.
  • كان تحويل ثنائي النقطة العائمة العدد إلى قاعدة 10 رقم عشري مع نقطة و يطبع هذا التمثيل.وهذا ينطوي أيضا على القليل من التقريب.

السبب PHP يطبع الدهشة Corrected price = float(115) (بدلا من 114.999...) هو أن var_dump لا طباعة العدد الدقيق (!), لكنه يطبع عدد وتقريبه إلى n - 2 (أو n - 1) أرقام, حيث n هي ارقام دقة الحساب.يمكنك بسهولة التحقق من هذا:

echo 1.15 * 100;  # this prints 115
printf("%.30f", 1.15 * 100);  # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different";  # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less";    # this prints `less'

إذا كنت تقوم بطباعة المجسمات ، تذكر: أنت لا ترى دائما كل الأرقام عند طباعة تعويم.

انظر أيضا تحذير كبير بالقرب من بداية PHP تطفو مستندات.

نصائح أخرى

وقد غطت وإجابات أخرى السبب والحل المناسب لهذه المشكلة، على ما أعتقد.

لتهدف إلى إصلاح المشكلة من زاوية مختلفة:

لتخزين القيم الأسعار في الخلية، ربما يجب عليك إلقاء نظرة في نوع العشرى ، والتي تتيح لك تخزين القيم الدقيق مع المنازل العشرية.

وPHP يقوم به التقريب على أساس الأرقام المعنوية. انها تخفي عدم دقة (على الخط 2). بطبيعة الحال، عندما يأتي الدور على طول، وأنها لا تعرف أي أفضل ويشذب كل ذلك على طول الطريق.

وربما هو حل ممكن آخر لهذه "المشكلة":

intval(number_format($problematic_float, 0, '', ''));

وكما ذكر هذه ليست مشكلة مع PHP في حد ذاته، هو أكثر من قضية التعامل مع الكسور التي لا يمكن التعبير عنها كقيم نقطة عائمة محدود وبالتالي يؤدي إلى فقدان الأحرف عند إلقاء القبض على.

والحل هو لضمان أنه عندما كنت تعمل على القيم النقطة العائمة وتحتاج إلى الحفاظ على دقة - استخدام وظائف برنامج الرصد العالمي أو الرياضيات وظائف BC - bcpow، bcmul وآخرون. وسيتم حل المشكلة بسهولة.

ومنها مثلا بدلا من $ price_corrected = $ السعر * 100؛

واستخدام $ price_corrected = bcmul ($ السعر، 100)؛

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top