2009-05-01 21 views
11

क्योंकि PHP में फ्लोट डेटा प्रकार गलत है, और MySQL में एक फ्लोट एक आईएनटी (और गलत है) से अधिक जगह लेता है, मैं हमेशा कीमतों को आईएनटी के रूप में स्टोर करता हूं, यह सुनिश्चित करने के लिए 100 से गुणा करके यह सुनिश्चित करता है कि हमारे पास वास्तव में 2 दशमलव स्थान हैं परिशुद्धता का हालांकि मुझे विश्वास है कि PHP गलत व्यवहार कर रहा है। उदाहरण कोड: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) 

जो नाव प्रकार की अशुद्धि को प्रदर्शित होगी। लेकिन मंजिल (115) 114 क्यों लौट रहा है ??

+0

यह वास्तव में PHP के साथ कुछ भी नहीं है और यह करने के लिए कि सभी कंप्यूटर फ़्लोटिंग पॉइंट डेटा कैसे संभालते हैं। यदि आप पहले फ्लोटिंग पॉइंट त्रुटियों का सामना नहीं करते हैं तो आप उस विषय के बारे में पढ़ना चाहेंगे। – jmucchiello

+0

@jmucchiello, मैं असहमत हूं। मैं समझता हूं कि कंप्यूटर्स फ़्लोटिंग पॉइंट डेटा कैसे संभालते हैं और यही कारण है कि मैं इसके बजाय पूर्णांक डेटा का उपयोग कर रहा हूं। यह एक PHP मुद्दा है क्योंकि PHP 115 दिखाता है जब अंतर्निहित डेटा स्पष्ट रूप से 114.9 99 99 999 है ... – Josh

उत्तर

25

त्वरित सुधार के रूप में इस प्रयास करें:

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

समस्या आप अनुभव कर रहे पीएचपी की गलती नहीं है, चल बिन्दु arithmetics साथ वास्तविक संख्या का उपयोग करके सभी प्रोग्रामिंग भाषाओं इसी तरह के मुद्दों की है।

मौद्रिक गणना के लिए अंगूठे का सामान्य नियम कभी भी फ्लोट का उपयोग नहीं करना है (न तो डेटाबेस में और न ही आपकी स्क्रिप्ट में)। आप हमेशा डॉलर के बजाय सेंट स्टोर करके सभी प्रकार की समस्याओं से बच सकते हैं। सेंट पूर्णांक हैं, और आप उन्हें स्वतंत्र रूप से एक साथ जोड़ सकते हैं, और अन्य पूर्णांक से गुणा कर सकते हैं। जब भी आप संख्या प्रदर्शित करते हैं, तो सुनिश्चित करें कि आप पिछले दो अंकों के सामने एक बिंदु डालें।

115 के बजाय 114 क्यों प्राप्त कर रहे हैं इसका कारण यह है कि निकटतम पूर्णांक की ओर floor राउंड नीचे, इस प्रकार मंजिल (114.9 99 99 99 99) 114 हो जाता है। अधिक दिलचस्प सवाल यह है कि 1.15 * 100 115 के बजाय 114.9 99 99 99 99 है। कारण इसके लिए यह है कि 1.15 बिल्कुल 115/100 नहीं है, लेकिन यह बहुत कम है, इसलिए यदि आप 100 से गुणा करते हैं, तो आपको 115 से थोड़ा छोटा छोटा मिलता है।

यहां एक और विस्तृत स्पष्टीकरण है echo 1.15 * 100; करता है:

  • यह एक बाइनरी फ्लोटिंग पॉइंट नंबर पर 1.15 पार्स करता है। इसमें गोलाकार शामिल है, यह 1.15 के करीब बाइनरी फ़्लोटिंग पॉइंट नंबर प्राप्त करने के लिए थोड़ा सा गोल होता है। कारण आपको सटीक संख्या (बिना त्रुटि के बिना) प्राप्त नहीं हो सकता है कि 1.15 में बेस 2 में अंकों की अनंत संख्या है।
  • यह 100 बाइनरी फ़्लोटिंग पॉइंट नंबर पर पार्स करता है। इसमें गोलाकार शामिल है, लेकिन चूंकि 100 एक छोटा पूर्णांक है, गोल करने की त्रुटि शून्य है।
  • यह पिछले दो संख्याओं के उत्पाद की गणना करता है। निकटतम बाइनरी फ़्लोटिंग पॉइंट नंबर खोजने के लिए इसमें थोड़ा गोल भी शामिल है। इस ऑपरेशन में राउंडिंग त्रुटि शून्य होती है।
  • यह बाइनरी फ़्लोटिंग पॉइंट नंबर को एक बिंदु के साथ आधार 10 दशमलव संख्या में परिवर्तित करता है, और इस प्रतिनिधित्व को प्रिंट करता है। इसमें थोड़ा गोल भी शामिल है।

कारण है कि पीएचपी आश्चर्य की बात Corrected price = float(115) प्रिंट (114.999 के बजाय ...) कि var_dump सही संख्या मुद्रित नहीं करता है (!), लेकिन यह संख्या n - 2 (या n - 1) अंक तक पूर्ण प्रिंट, जहां एन अंक गणना की सटीकता है।आप आसानी से इस की पुष्टि कर सकते हैं:

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 float दस्तावेज़ों की शुरुआत के करीब बड़ी चेतावनी भी देखें।

+0

$ price_int = intval (floor ($ price_corrected + 0.5)) $ price_int = intval (round ($ value_corrected) के समान है)। – chaos

+0

@pts, अराजकता सही है और वास्तव में मेरा मूल कोड इस तरह था। कोई फर्क नहीं। मेरा सवाल यह है कि, PHP * क्यों कहता है * $ price_converted 115 है जब यह वास्तव में 114.9 99 999 है? (मुझे समझ में आता है कि यह 114.9 99 99 क्यों है ...) – Josh

+0

@pts, जब मैंने कहा कि अराजकता सही थी, तो मैं गलत था, मैंने आपके कोड को गलत तरीके से पढ़ा। यह काम कर सकता है। – Josh

3

PHP महत्वपूर्ण अंकों के आधार पर गोल कर रहा है। यह गलतता छुपा रहा है (लाइन 2 पर)। बेशक, जब फर्श के साथ आता है, यह किसी भी बेहतर नहीं जानता है और इसे सभी तरह से नीचे लूप्स।

+0

@altCognito, क्या आप जानते हैं कि यह लाइन 2 पर गलत क्यों है? मुझे लगता है कि वास्तव में मेरा सवाल क्या है। – Josh

+1

मुझे विश्वास है कि इसका सामान्यीकरण के साथ कुछ करना है, और उत्तर यहां है: http://en.wikipedia.org/wiki/Floating_point#Multiplication: | – cgp

4

अन्य उत्तरों ने कारण को कवर किया है और समस्या के लिए एक अच्छा कामकाज है, मुझे विश्वास है।

एक अलग कोण से समस्या को ठीक करने का उद्देश्य के लिए:

MySQL में कीमत मूल्यों के भंडारण के लिए, आप शायद DECIMAL type, जो आप दशमलव स्थानों के साथ सटीक मान संग्रहीत करने देता है पर गौर करना चाहिए।

+0

+1, लेकिन मैं जोड़ता हूं कि मुझे यकीन नहीं है कि mySQL में एक फ़्लोट प्रकार "गलत" कैसे है। यह कोई अतिरिक्त गुणा मोजो करने से बहुत कम गलत है, और विश्लेषण के लिए आपके डेटा को एक बेहतर स्थिति में छोड़ देता है। वैसे भी, उसने क्या कहा। – cgp

+0

@ चाड बिर्च, क्या मैं DECIMAL से बहुत कम जगह का उपयोग नहीं करता? मैंने हमेशा यह पाया है कि INT + तर्क यह जानने के लिए कि DECIMAL से अधिक कुशल होने के लिए ऑफ़सेट करने के लिए कितने दशमलव स्थान हैं। विशेष रूप से कीमतों की तरह चीजों के लिए। प्रभावी रूप से, कॉलम price_in_cents – Josh

+1

नहीं है, जब तक कि आपके DECIMAL में बहुत से अंक न हों। मैनुअल के मुताबिक, आईएनटी 4 बाइट्स का उपयोग करता है (मान लीजिए कि आप एक वास्तविक आईएनटी का उपयोग कर रहे हैं, न कि किसी भी चीज या कुछ भी नहीं), डेसिमल स्टोरेज बदलता है: "नौ अंकों के प्रत्येक एकाधिक को चार बाइट की आवश्यकता होती है, और 'बचे हुए' अंकों के कुछ अंश की आवश्यकता होती है चार बाइट्स "। पूर्ण जानकारी: http://dev.mysql.com/doc/refman/5.1/en/storage-requirements.html –

3

शायद यह इस "समस्या" के लिए एक और संभव समाधान है:

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

आप, हाँ, महोदय, एक उत्थान के लायक हैं। बस एक अजीब बग में आया और इसे हल किया। मैंने बस उस 'number_format()' कॉल पर खाली तार जोड़े ताकि परिणामस्वरूप संख्या अल्पविराम और बिंदुओं के बिना एक int हो। –

+0

फिक्स्ड अंतराल (16.74 * 100) मेरे लिए 1673 लौटा रहा है और स्वीकृत उत्तर में मदद नहीं मिली है। – Billy

0

के रूप में कहा गया है इस पीएचपी से प्रति के साथ एक समस्या नहीं है, यह अंशों से निपटने का एक मुद्दा है कि के रूप में व्यक्त नहीं किया जा सकता की अधिक है परिमित फ्लोटिंग पॉइंट वैल्यू इसलिए गोल करते समय चरित्र के नुकसान की ओर अग्रसर होते हैं।

समाधान यह सुनिश्चित करना है कि जब आप फ़्लोटिंग पॉइंट मानों पर काम कर रहे हों और आपको सटीकता बनाए रखने की आवश्यकता है - जीएमपी फ़ंक्शंस या बीसी गणित फ़ंक्शन का उपयोग करें - bcpow, bcmul et al। और समस्या आसानी से हल हो जाएगी।

$ price_corrected = $ price * 100 के बजाय E.g;

$ price_corrected = bcmul ($ मूल्य, 100) का उपयोग करें;

संबंधित मुद्दे