PHP7.1 json_encode () फ्लोट इश्यू


98

यह एक सवाल नहीं है क्योंकि यह अधिक जागरूक है। मैंने एक एप्लिकेशन को अपडेट किया जो json_encode()PHP7.1.1 का उपयोग करता है और मैं एक मुद्दे को देख रहा था जिसमें फ्लोट्स को कभी-कभी 17 अंकों का विस्तार करने के लिए बदल दिया जाता था। प्रलेखन के अनुसार, PHP 7.1.x ने serialize_precisionदोहरे मानों को एन्कोडिंग करते समय सटीक के बजाय उपयोग करना शुरू कर दिया । मैं अनुमान लगा रहा हूं कि इसका एक उदाहरण मूल्य है

472.185

बनने के लिए

472.18500000000006

उसके बाद उस मूल्य से गुजरा json_encode()। अपनी खोज के बाद से, मैं PHP 7.0.16 पर वापस लौट आया हूं और मेरे पास अब कोई समस्या नहीं है json_encode()। मैंने PHP 7.0.16 पर वापस लौटने से पहले PHP 7.1.2 को अपडेट करने का भी प्रयास किया।

इस सवाल के पीछे तर्क PHP - फ़्लोटिंग नंबर परिशुद्धता से स्टेम करता है , हालांकि इसके लिए सभी कारण सटीक से क्रम में बदलाव के कारण क्रमिक उपयोग में है json_encode()

अगर किसी को इस समस्या के समाधान का पता है, तो मुझे तर्क / समाधान पर सुनने में खुशी होगी।

बहुआयामी सरणी (पहले) से अंश:

[staticYaxisInfo] => Array
                    (
                        [17] => stdClass Object
                            (
                                [variable_id] => 17
                                [static] => 1
                                [min] => 0
                                [max] => 472.185
                                [locked_static] => 1
                            )

                    )

और जाने के बाद json_encode()...

"staticYaxisInfo":
            {
                "17":
                {
                    "variable_id": "17",
                    "static": "1",
                    "min": 0,
                    "max": 472.18500000000006,
                    "locked_static": "1"
                }
            },

6
ini_set('serialize_precision', 14); ini_set('precision', 14);अगर आप वास्तव में अपने फ्लोट पर एक विशिष्ट परिशुद्धता पर भरोसा करते हैं, तो आप इसे कुछ गलत कर रहे हैं, जैसा कि आप इसका उपयोग करते हैं, शायद इसे क्रमबद्ध करेंगे।
Apokryfos

1
"अगर किसी को इस समस्या के समाधान का पता है" - क्या समस्या है? मैं यहाँ कोई समस्या नहीं देख सकता। यदि आप PHP का उपयोग करके JSON को डिकोड करते हैं तो आपको वह मान वापस मिल जाता है जिसे आपने एन्कोड किया था। और अगर आप इसे एक अलग भाषा का उपयोग करके डिकोड करते हैं, तो संभवतः आपको समान मूल्य मिलता है। किसी भी तरह से, यदि आप 12 अंकों के साथ मूल्य प्रिंट करते हैं तो आपको मूल ("सही") मान वापस मिलता है। क्या आपको अपने एप्लिकेशन द्वारा उपयोग की जाने वाली झांकियों के लिए 12 से अधिक दशमलव अंकों की सटीकता की आवश्यकता है?
अक्षियाक

12
@axiac 472.185! = 472.18500000000006 अंतर से पहले और बाद में एक स्पष्ट है। यह एक ब्राउज़र के लिए एक AJAX अनुरोध का हिस्सा है और मूल स्थिति में रहने के लिए मूल्य की आवश्यकता है।
Gwi7d31

4
मैं एक स्ट्रिंग रूपांतरण का उपयोग करने से बचने की कोशिश कर रहा हूं क्योंकि अंतिम उत्पाद हाईचर्ट है और यह स्ट्रिंग को स्वीकार नहीं करेगा। मुझे लगता है कि मैं इसे बहुत अक्षम और मैला समझूंगा यदि आप एक फ्लोट मूल्य लेते हैं, तो इसे एक स्ट्रिंग के रूप में डालें, इसे दूर भेजें, और फिर जावास्क्रिप्ट को स्ट्रिंग को पार्सफ्लोट () के साथ एक फ्लोट पर वापस व्याख्या करें। क्या तुम नहीं?
Gwi7d31

1
@axiac मैं ध्यान देता हूं कि आप PHP json_decode () मूल फ़्लोट मान वापस लाते हैं। हालाँकि, जब जावास्क्रिप्ट JSON स्ट्रिंग को एक ऑब्जेक्ट में बदल देता है, तो यह मान को 472.185 में वापस नहीं बदलता है जैसे कि आपने संभावित रूप से इंसुलेट किया था ... इसलिए समस्या। मैं जो जा रहा हूं, उसके साथ रहूंगा।
Gwi7d31

जवाबों:


101

इसने मुझे तब तक के लिए पागल कर दिया, जब तक कि मुझे यह बग नहीं मिल गया, जो आपको इस RFC की ओर इशारा करता है

वर्तमान में json_encode()ईजी (सटीक) का उपयोग करता है जो 14 पर सेट होता है। इसका मतलब है कि सबसे अधिक 14 अंकों की संख्या को प्रदर्शित करने (मुद्रण) के लिए उपयोग किया जाता है। आईईईई 754 डबल का समर्थन करता है उच्च परिशुद्धता और serialize()/ var_export()का उपयोग करता पीजी (serialize_precision) जो 17 सामान्य सेट करें और अधिक सटीक होना करने के लिए हो सकता है। चूंकि json_encode()ईजी (परिशुद्धता) का उपयोग करता है, json_encode()अंश भागों के निचले अंकों को हटाता है और मूल मूल्य को नष्ट कर देता है, भले ही पीएचपी का फ्लोट अधिक सटीक फ्लोट मूल्य को पकड़ सकता है।

और (जोर मेरा)

यह RFC एक नई सेटिंग ईजी (परिशुद्धता) = - 1 और PG (क्रमबद्ध_प्रदर्शन) = - 1 प्रस्तुत करने का प्रस्ताव करता है, जो zend_dtoa () के मोड 0 का उपयोग करता है, जो फ्लोटिंग संख्याओं के लिए बेहतर algorigthm का उपयोग करता है (-1 का उपयोग 0 मोड को इंगित करने के लिए किया जाता है)

संक्षेप में, PHP 7.1 json_encodeको नए और बेहतर परिशुद्धता इंजन का उपयोग करने का एक नया तरीका है । में php.ini आप परिवर्तन करने की आवश्यकता है serialize_precisionके लिए

serialize_precision = -1

आप यह सत्यापित कर सकते हैं कि यह कमांड लाइन के साथ काम करता है

php -r '$price = ["price" => round("45.99", 2)]; echo json_encode($price);'

आपको मिलना चाहिये

{"price":45.99}

G(precision)=-1और PG(serialize_precision)=-1 PHP 5.4
किटीगर्ल

1
से सावधान रहें serialize_precision = -1। -1 के साथ, यह कोड echo json_encode([528.56 * 100]);प्रिंट करता है[52855.99999999999]
vl.lapikov

3
@ vl.lapikov हालांकि यह एक सामान्य फ्लोटिंग पॉइंट एरर जैसा लगता है । यहाँ एक डेमो है, जहां आप देख सकते हैं यह स्पष्ट रूप से सिर्फ एक नहीं है json_encodeसमस्या
Machavity

41

एक प्लगइन डेवलपर के रूप में मेरे पास सर्वर की php.ini सेटिंग्स तक सामान्य पहुंच नहीं है। तो, Machavity के उत्तर के आधार पर मैंने कोड का यह छोटा टुकड़ा लिखा है, जिसे आप अपनी PHP स्क्रिप्ट में उपयोग कर सकते हैं। बस इसे स्क्रिप्ट के ऊपर रखें और json_encode हमेशा की तरह काम करता रहेगा।

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'serialize_precision', -1 );
}

कुछ मामलों में एक और चर को सेट करना आवश्यक है। मैं इसे दूसरे समाधान के रूप में जोड़ रहा हूं क्योंकि मुझे यकीन नहीं है कि दूसरा समाधान उन सभी मामलों में ठीक काम करता है जहां पहला समाधान काम करने के लिए साबित हुआ है।

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}

3
उस पर ध्यान दें, क्योंकि आपका प्लगइन बाकी डेवलपर एप्लिकेशन के लिए अनपेक्षित सेटिंग्स बदल सकता है। लेकिन, IMO, मुझे यकीन नहीं है कि यह विकल्प कितना विनाशकारी हो सकता है ... lol
igorsantos07

ध्यान रखें कि सटीक मूल्य (दूसरा उदाहरण) बदलने से आपके पास मौजूद अन्य गणितीय कार्यों में बड़ा प्रभाव पड़ सकता है। php.net/manual/en/ini.core.php#ini.preaches
रिकार्डो मार्टिंस

@RicardoMartins: दस्तावेज़ीकरण के अनुसार डिफ़ॉल्ट सटीकता 14. 14. उपरोक्त फिक्स यह 17 तक बढ़ जाती है। इसलिए यह और भी सटीक होना चाहिए। क्या आप सहमत हैं?
alev

@alev मैं जो कह रहा था, वह यह है कि बस serialize_prepy को बदलने के लिए पर्याप्त है और अन्य PHP व्यवहारों से समझौता न करें जो आपके आवेदन का अनुभव कर सकते हैं
रिकार्डो मार्टिंस

6

मैंने इसे सटीक और क्रमबद्ध करने के लिए इसे हल किया है। समान मूल्य (10) के लिए:

ini_set('precision', 10);
ini_set('serialize_precision', 10);

आप इसे अपने php.ini में भी सेट कर सकते हैं


4

मैं मौद्रिक मूल्यों को एन्कोडिंग कर रहा था और इसमें 330.46एन्कोडिंग जैसी चीजें थीं 330.4600000000000363797880709171295166015625। यदि आप PHP सेटिंग्स को बदलना या नहीं करना चाहते हैं, और आप पहले से ही डेटा की संरचना को जानते हैं तो एक बहुत ही सरल समाधान है जो मेरे लिए काम करता है। बस इसे एक स्ट्रिंग में डालें (दोनों निम्नलिखित कार्य समान करें):

$data['discount'] = (string) $data['discount'];
$data['discount'] = '' . $data['discount'];

मेरे उपयोग के मामले के लिए यह एक त्वरित और प्रभावी समाधान था। बस ध्यान दें कि इसका मतलब है कि जब आप इसे JSON से वापस डिकोड करते हैं तो यह एक स्ट्रिंग होगी क्योंकि यह डबल कोट्स में लिपटा होगा।


3

मुझे भी यही समस्या थी लेकिन केवल serialize_preaches = -1 ने समस्या का समाधान नहीं किया। मुझे 14 से 17 तक परिशुद्धता के मूल्य को अपडेट करने के लिए एक और कदम उठाना पड़ा (जैसा कि यह मेरे PHP7.0 इनआई फ़ाइल पर सेट किया गया था)। जाहिर है, उस संख्या के मूल्य को बदलने से गणना किए गए फ्लोट का मूल्य बदल जाता है।


3

अन्य समाधान मेरे काम नहीं आए। यहाँ मुझे अपने कोड निष्पादन की शुरुआत में जोड़ना था:

if (version_compare(phpversion(), '7.1', '>=')) {
    ini_set( 'precision', 17 );
    ini_set( 'serialize_precision', -1 );
}

क्या यह मूल रूप से एलिन पॉप के उत्तर के समान नहीं है?
igorsantos07

1

मेरे लिए समस्या यह थी कि जब JSON_NUMERIC_CHECK को json_encode () के दूसरे तर्क के रूप में पारित किया गया था, जो सभी नंबरों को इंट करने के लिए टाइप कर रहा था (केवल पूर्णांक नहीं)


1

इसे सटीक सटीकता के साथ एक स्ट्रिंग के रूप में संग्रहीत करें, जिसे आपको उपयोग करने की आवश्यकता है number_format, फिर json_encodeइसे JSON_NUMERIC_CHECKविकल्प का उपयोग करके :

$foo = array('max' => number_format(472.185, 3, '.', ''));
print_r(json_encode($foo, JSON_NUMERIC_CHECK));

आपको मिला:

{"max": 472.185}

ध्यान दें कि इससे आपके स्रोत ऑब्जेक्ट में सभी संख्यात्मक तार प्राप्त होंगे जो परिणामी JSON में संख्याओं के रूप में एन्कोड किए जाएंगे।


1
मैंने PHP 7.3 में इसका परीक्षण किया है और यह काम नहीं करता है (आउटपुट में अभी भी बहुत अधिक सटीकता है)। - जाहिर है JSON_NUMERIC_CHECK झंडा पीएचपी 7.1 के बाद से टूट गया है php.net/manual/de/json.constants.php#123167
फिलिप

0
$val1 = 5.5;
$val2 = (1.055 - 1) * 100;
$val3 = (float)(string) ((1.055 - 1) * 100);
var_dump(json_encode(['val1' => $val1, 'val2' => $val2, 'val3' => $val3]));
{
  "val1": 5.5,
  "val2": 5.499999999999994,
  "val3": 5.5
}

0

ऐसा लगता है समस्या तब होती है जब की तरह serializeऔर serialize_precisionविभिन्न मूल्यों की तैयारी में हैं। मेरे मामले में क्रमशः 14 और 17। उन दोनों को 14 पर सेट करने से समस्या हल हो गई, जैसा कि serialize_precision-1 पर सेट किया गया था ।

का डिफ़ॉल्ट मान serialize_precision PHP 7.1.0 के रूप में -1 में बदल दिया गया था जिसका अर्थ है "ऐसी संख्याओं को गोल करने के लिए एक बढ़ाया एल्गोरिथ्म का उपयोग किया जाएगा"। लेकिन अगर आप अभी भी इस समस्या का सामना कर रहे हैं, तो यह हो सकता है क्योंकि आपके पास एक पूर्व संस्करण से एक PHP कॉन्फिगर फाइल है। (हो सकता है कि आपने अपग्रेड करते समय अपनी कॉन्फिग फाइल रखी हो?)

एक और बात पर विचार करना है कि क्या यह आपके मामले में फ्लोट वैल्यू का उपयोग करने के लिए समझ में आता है। दशमलव स्थान की उचित संख्या हमेशा आपके JSON में बनाए रखी जाए, यह सुनिश्चित करने के लिए आपके नंबरों वाले स्ट्रिंग मानों का उपयोग करना या न करना संभव नहीं है।


-1

आप json_encode () से पहले [अधिकतम] => 472.185 को एक फ्लोट से एक स्ट्रिंग ([अधिकतम] => '472.185') में बदल सकते हैं। वैसे भी json एक स्ट्रिंग है, json_encode () से पहले अपने फ्लोट मानों को स्ट्रिंग्स में परिवर्तित करना आपकी इच्छा के मूल्य को बनाए रखेगा।


यह तकनीकी रूप से कुछ हद तक सही है, लेकिन बहुत अक्षम है। यदि JSON स्ट्रिंग में कोई Int / Float उद्धृत नहीं किया जाता है, तो जावास्क्रिप्ट इसे एक वास्तविक Int / Float के रूप में देख सकता है। अपने रेंडरिंग का प्रदर्शन आपको ब्राउज़र के पक्ष में एक बार एक इंट / फ़्लोट पर वापस जाने के लिए हर एक मान देने के लिए मजबूर करता है। अनुरोध के अनुसार इस प्रोजेक्ट पर काम करते समय मैं अक्सर 10000+ मानों के साथ काम कर रहा था। बहुत सारी ब्लोट प्रोसेसिंग समाप्त हो गई होती।
Gwi7d31

यदि आप कहीं पर डेटा भेजने के लिए JSON का उपयोग कर रहे हैं, और एक संख्या की उम्मीद है, लेकिन आप एक स्ट्रिंग भेजते हैं, जो काम करने की गारंटी नहीं है। ऐसी स्थितियों में जहां भेजने वाले एप्लिकेशन के डेवलपर को प्राप्त एप्लिकेशन पर नियंत्रण नहीं है, यह कोई समाधान नहीं है।
ऑसूलिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.