पैसा, चाहे वह किसी भी मुद्रा में हो, एक फ्लोटिंग-पॉइंट नंबर की तरह लगता है। लेकिन मुद्रा के लिए फ्लोट्स का उपयोग करना एक गलती है।
फ्लोट नंबर (इसलिए, फ्लोट ऑब्जेक्ट्स), परिभाषा के अनुसार, अनएक्सैक्ट रियल नंबर हैं जो नेटिव आर्किटेक्चर की विशेषता डबल-प्रिसिजन फ्लोटिंग-पॉइंट प्रतिनिधित्व का उपयोग करते हैं। अनएक्सैक्ट नंबर एकाउंटेंट्स को नाखुश करते हैं।
इस लेख में, आप कुछ त्वरित उदाहरणों के माध्यम से निर्देशित होंगे जो रूबी और रेल्स में मनी डेटा से निपटने के लिए उपलब्ध विकल्पों को संबोधित करने में आपकी सहायता करेंगे।
फ्लोट क्या है?
जैसा कि हमने कहा, float ऑब्जेक्ट्स में अलग अंकगणित होता है। यही मुख्य कारण है कि वे अनएक्सैक्ट नंबर हैं, विशेष रूप से क्योंकि रूबी (अधिकांश भाषाओं की तरह) फ्लोट्स को दर्शाने के लिए बाइनरी अंकों की एक निश्चित संख्या का उपयोग करता है। दूसरे शब्दों में, रूबी फ्लोटिंग नंबरों को दशमलव से बाइनरी में और इसके विपरीत परिवर्तित करता है।
जब आप रियल नंबरों (उर्फ दशमलव नंबरों) के बाइनरी प्रतिनिधित्व में गहराई से गोता लगाते हैं, तो उनमें से कुछ को बिल्कुल दर्शाया नहीं जा सकता, इसलिए सिस्टम के लिए एकमात्र शेष विकल्प उन्हें राउंड ऑफ करना है।
यदि आप आगे सोचते हैं और सामान्य गणित संरचनाओं पर विचार करते हैं, जैसे आवर्ती दशमलव, तो आप समझ सकते हैं कि वे एक निश्चित संख्या के भीतर पूरी तरह से दर्शाए नहीं जा सकते क्योंकि पाई नंबर, उदाहरण के लिए, अनंत है। फ्लोट्स आमतौर पर 32 या 64 बिट्स की प्रिसिजन तक धारण कर सकते हैं, जिसका अर्थ है कि संख्या सीमा तक पहुंचने पर काट दी जाएगी।
आइए एक क्लासिक उदाहरण का विश्लेषण करें:
1200 * (14.0/100)
यह एक संख्या का प्रतिशत निकालने का एक सीधा तरीका है। 1200 का चौदह प्रतिशत 168 होना चाहिए; हालांकि, रूबी के भीतर इस निष्पादन का परिणाम होगा
1200 * (14.0/100) => 168.00000000000003
हालांकि, यदि आप फॉर्मूला में सिर्फ 0.1% जोड़ते हैं, तो आपको कुछ अलग मिलता है:
1200 * (14.1/100) => 169.2
वैकल्पिक रूप से, आप वांछित दशमलव स्थानों को परिभाषित करते हुए मान को निकटतम संभव मान पर round कर सकते हैं:
(my_calculation).round(2)
वास्तव में, यह अधिक जटिल गणनाओं के साथ गारंटीकृत नहीं है, विशेष रूप से यदि आप इन मूल्यों की तुलना करते हैं।
यदि आप इसके पीछे के वास्तविक विज्ञान को समझने में रुचि रखते हैं, तो मैं Oracle के परिशिष्ट को पढ़ने की अत्यधिक अनुशंसा करता हूं: What Every Computer Scientist Should Know About Floating-Point Arithmetic। यह विस्तार से बताता है कि फ्लोट नंबरों की गलत प्रकृति के पीछे क्या कारण हैं।
भरोसेमंद BigDecimal
निम्नलिखित कोड स्निपेट पर विचार करें:
require "bigdecimal"
BigDecimal("45.99")
यह कोड आसानी से एक ईकॉमर्स कार्ट की राशि को अपनाने वाले एक वास्तविक तर्क का प्रतिनिधित्व कर सकता है। अंत में, हेरफेर किया जा रहा वास्तविक मूल्य हमेशा 45.99 होगा बजाय 45.9989 या 45.99000009 के, उदाहरण के लिए।
यह BigDecimal की सटीक प्रकृति है। सामान्य अंकगणितीय गणनाओं के लिए, float उसी तरह प्रदर्शन करेगा; हालांकि, यह अप्रत्याशित है, जो इसका उपयोग करने का खतरा है।
जब इसे BigDecimal के साथ चलाया जाता है, तो पिछले अनुभाग में हमने जो प्रतिशत गणना की थी, उसका परिणाम होता है
require "bigdecimal"
(BigDecimal(1200) * (BigDecimal(14)/BigDecimal(100))).to_s("F")
=> 168.0
यह केवल एक संक्षिप्त संस्करण है जो irb कंसोल में तेजी से निष्पादन की अनुमति देता है।
मूल रूप से, जब आप सीधे BigDecimal ऑब्जेक्ट को प्रिंट करते हैं, तो आपको इसका साइंटिफिक नोटेशन मिलेगा, जो हम यहां नहीं चाहते हैं। to_s मेथड दिए गए तर्क को फॉर्मेटिंग सेटिंग्स के कारण प्राप्त करता है और BigDecimal के समकक्ष फ्लोटिंग मूल्य को प्रदर्शित करता है। इस विषय पर अधिक जानकारी के लिए, रूबी डॉक्स देखें।
यदि आपको दशमलव स्थानों के लिए एक सीमा निर्धारित करने की आवश्यकता है, तो इसमें truncate मेथड है, जो आपके लिए काम करेगा:
(BigDecimal(1200) * (BigDecimal("14.12")/BigDecimal(100))).truncate(2).to_s("F")
=> 169.44
RubyMoney प्रोजेक्ट
RubyMoney को इन समस्याओं के बारे में सोचने के बाद बनाया गया था। यह रूबी डेवलपर्स का एक ओपन-सोर्स समुदाय है जिसका उद्देश्य रूबी पारिस्थितिकी तंत्र में मनी डेटा में हेरफेर करने के लिए बेहतरीन लाइब्रेरी प्रदान करके डेवलपर्स के जीवन को सुविधाजनक बनाना है।
प्रोजेक्ट सात लाइब्रेरी से बना है, जिनमें से तीन महत्व में सबसे अलग हैं:
- Money: मनी और मुद्रा रूपांतरण से निपटने के लिए एक रूबी लाइब्रेरी। यह मनी को मजबूत और आधुनिक एप्लिकेशन में संभालने के लिए कई ऑब्जेक्ट-ओरिएंटेड विकल्प प्रदान करता है, चाहे वे वेब के लिए हों या नहीं।
- Money-rails: रूबी ऑन रेल्स के लिए RubyMoney का एकीकरण,
moneyलाइब्रेरी की सभी शक्ति को रेल्स की लचीलापन के साथ मिलाना। - Monetize: विभिन्न ऑब्जेक्ट्स को
moneyऑब्जेक्ट्स में बदलने के लिए एक लाइब्रेरी। यह उन एप्लिकेशन के लिए एक सहायक लाइब्रेरी की तरह अधिक काम करता है जो बहुत सारे स्ट्रिंग पार्सिंग से निपटते हैं, उदाहरण के लिए।
प्रोजेक्ट में चार अन्य दिलचस्प लाइब्रेरी हैं:
- EU_central_bank: एक लाइब्रेरी जो यूरोपीय सेंट्रल बैंक से प्रकाशित दरों का उपयोग करके विनिमय दरों की गणना करने में मदद करती है।
- Google_currency: Google मुद्रा दरों को संदर्भ के रूप में उपयोग करके मुद्रा रूपांतरण के लिए एक दिलचस्प लाइब्रेरी।
- Money-collection:
moneyऑब्जेक्ट्स के योग/न्यूनतम/अधिकतम की सटीक गणना के लिए एक सहायक लाइब्रेरी। - Money-heuristics: मनी जेम के लिए स्ट्रिंग इनपुट के यूरिस्टिक विश्लेषण के लिए एक मॉड्यूल।
"Money" जेम
आइए सबसे प्रसिद्ध से शुरू करें: मनी जेम। इसकी मुख्य विशेषताओं में निम्नलिखित हैं:
- एक
moneyक्लास जो प्रासंगिक मौद्रिक जानकारी रखती है, जैसे मूल्य, मुद्रा, और दशमलव चिह्न। Money::Currencyनामक एक अन्य क्लास जो डेवलपर द्वारा उपयोग की जा रही मौद्रिक इकाई के बारे में जानकारी को लपेटती है।- डिफ़ॉल्ट रूप से, यह फ्लोटिंग-पॉइंट नंबर के बजाय इंटीजर के साथ काम करता है ताकि उपरोक्त त्रुटियों से बचा जा सके।
- एक मुद्रा से दूसरी मुद्रा में मनी का आदान-प्रदान करने की क्षमता, जो सुपर कूल है।
उसके अलावा, हमें सुसंगत और ऑब्जेक्ट-ओरिएंटेड संरचनाओं द्वारा प्रदान की जाने वाली उच्च लचीलापन भी मिलता है जो आपके प्रोजेक्ट्स में किसी अन्य मॉडल की तरह ही मनी डेटा में हेरफेर करते हैं।
इसका उपयोग बहुत सरल है, बस उचित जेम इंस्टॉल करें:
gem install money
एक त्वरित उदाहरण जिसमें एक निश्चित राशि शामिल है:
my_money = Money.new(1200, "USD")
my_money.cents #=> 1200
my_money.currency #=> Currency.new("USD")
जैसा कि आप देख सकते हैं, मनी को सेंट के आधार पर दर्शाया जाता है। बारह सौ सेंट 12 डॉलर के बराबर है।
जैसे आपने BigDecimal के साथ किया था, आप इन ऑब्जेक्ट्स के साथ कुछ बुनियादी गणित भी कर सकते हैं। उदाहरण के लिए,
cart_amount = Money.new(10000, "USD") #=> 100 USD
discount = Money.new(1000, "USD") #=> 10 USD
cart_amount - discount == Money.new(9000, "USD") #=> 90 USD
दिलचस्प, है ना? यह उन ऑब्जेक्ट्स की प्रकृति है जिनका हमने उल्लेख किया था। कोडिंग करते समय, यह वास्तव में ऐसा लगता है जैसे आप अनएक्सप्रेसिव और सामान्य नंबरों के बजाय मौद्रिक मूल्यों में हेरफेर कर रहे हैं।
मुद्रा रूपांतरण
यदि आपके पास अपना खुद का विनिमय दर सिस्टम है, तो आप एक एक्सचेंज बैंक ऑब्जेक्ट के माध्यम से मुद्रा रूपांतरण कर सकते हैं। निम्नलिखित पर विचार करें:
Money.add_rate("USD", "BRL", 5.23995)
Money.add_rate("BRL", "USD", 0.19111)
जब भी आपको उनके बीच मूल्यों का आदान-प्रदान करने की आवश्यकता हो, तो आप निम्न कोड चला सकते हैं:
Money.us_dollar(100).exchange_to("BRL") #=> Money.new(523, "BRL")
यही बात किसी भी अंकगणितीय और तुलना मूल्यांकन पर लागू होती है जिसे आप करना चाहें।
अधिक प्रदान की गई मुद्रा विशेषताओं के लिए डॉक्स देखना सुनिश्चित करें, जैसे iso_code (जो उस मुद्रा का अंतरराष्ट्रीय तीन अंकों का कोड लौटाता है) और decimal_mark (मनी डेटा के मूल्य और अंश के बीच का वर्ण), दूसरों के बीच।
ओह, मैं लगभग भूल गया; एक बार जब आप मनी जेम इंस्टॉल कर लेते हैं, तो आप BigDecimal मेथड to_money तक पहुंच सकते हैं जो स्वचालित रूप से आपके लिए रूपांतरण करता है।
"monetize" जेम
RubyMoney प्रोजेक्ट के भीतर प्रत्येक लाइब्रेरी की भूमिका को समझना महत्वपूर्ण है। जब भी आपको एक अलग रूबी ऑब्जेक्ट (उदाहरण के लिए एक String) को Money में बदलने की आवश्यकता होती है, तो monetize वही है जो आप खोज रहे हैं।
सबसे पहले, जेम निर्भरता इंस्टॉल करना सुनिश्चित करें या इसे अपने Gemfile में जोड़ें:
gem install monetize
जाहिर है, money को भी इंस्टॉल करने की आवश्यकता है।
parse मेथड भी बहुत उपयोगी है जब आप अलग प्रारूप में मनी डेटा प्राप्त करते हैं; उदाहरण के लिए,
Money.parse("£100") == Money.new(100, "GBP") #=> true
हालांकि जिन परिदृश्यों में आप इस पार्सिंग मेथड का उपयोग करेंगे वे प्रतिबंधित हैं, यह बहुत उपयोगी हो सकता है जब आप HTTP अनुरोध से अपनी मुद्रा कोड के साथ स्वरूपित एक मूल्य प्राप्त करते हैं। वेब पर, सब कुछ टेक्स्ट है, इसलिए स्ट्रिंग से money में परिवर्तित करना बहुत उपयोगी हो सकता है।
हालांकि, सावधान रहें कि आपका सिस्टम मूल्यों में कैसे हेरफेर करता है और क्या उन्हें किसी तरह हैक किया जा सकता है। वित्तीय सिस्टम हमेशा कई सुरक्षा परतों से ढके होते हैं ताकि यह सुनिश्चित हो सके कि आप जो मूल्य प्राप्त कर रहे हैं वह उस लेनदेन का वास्तविक मूल्य है।
"monetize-rails" जेम
यह वह लाइब्रेरी है जो वही मनी हेरफेर ऑपरेशन करती है, लेकिन एक रेल्स ऐप के भीतर।
हमें रेल्स के साथ काम करने के लिए सिर्फ एक दूसरी लाइब्रेरी की आवश्यकता क्यों है? खैर, आप निश्चित रूप से सामान्य गणित संचालन के लिए रेल्स प्रोजेक्ट्स में अकेले money जेम का उपयोग कर सकते हैं। हालांकि, यह ठीक से काम नहीं करेगा जब आपके रेल्स स्ट्रक्चर्स को money की विशेषताओं के साथ संवाद करने की आवश्यकता होगी।
निम्नलिखित उदाहरण पर विचार करें:
class Cart < ActiveRecord::Base
monetize :amount_cents
end
यह एक वास्तविक, कार्यात्मक रेल्स मॉडल ऑब्जेक्ट है। आप इसे डेटाबेस के साथ उपयोग कर सकते हैं (यहां तक कि एलियास सहित जब आप एक अलग मॉडल एट्रिब्यूट नाम चाहते हैं), Mongoid, REST वेब सेवाएं, आदि।
अब तक हम जिन विशेषताओं के संपर्क में रहे हैं, वे सभी इस जेम पर भी लागू होती हैं। आमतौर पर, इसे चलाने के लिए केवल अतिरिक्त सेटिंग्स आवश्यक हैं, जिन्हें config/initializers/money.rb फाइल में रखा जाना चाहिए:
MoneyRails.configure do |config|
# डिफ़ॉल्ट मुद्रा सेट करें
config.default_currency = :usd
end
यह डिफ़ॉल्ट मुद्रा को आपके द्वारा प्रदान की गई मुद्रा पर सेट करेगा। हालांकि, विकास के दौरान, संभावना है कि आपको मॉडल्स में एक से अधिक मुद्रा को संभालने या विनिमय रूपांतरण करने की आवश्यकता हो सकती है।
यदि हां, तो money-rails हमें मॉडल-लेवल मुद्रा परिभाषा कॉन्फ़िगर करने की अनुमति देता है:
class Cart < ActiveRecord::Base
# मॉडल लेवल मुद्रा के रूप में GPB का उपयोग करें
register_currency :eur
monetize :amount_cents, as "amount"
monetize :discount_cents, with_currency: :eur
monetize :converted_value, with_currency: :usd
end
ध्यान दें कि एक बार सब कुछ सेट हो जाने पर, अपने प्रोजेक्ट्स के साथ मनी टाइप्स का उपयोग करना वास्तव में आसान है।
निष्कर्ष
इस ब्लॉग पोस्ट में, हमने रूबी और रेल्स पारिस्थितिकी तंत्र के भीतर मनी मूल्यों से निपटने के लिए उपलब्ध कुछ विकल्पों का पता लगाया है। नीचे कुछ महत्वपूर्ण बिंदुओं का सारांश दिया गया है:
- यदि आप फ्लोट नंबरों की गणना से निपट रहे हैं, विशेष रूप से यदि वे मनी डेटा का प्रतिनिधित्व करते हैं, तो
BigDecimalयाMoneyइंस्टेंस के लिए जाएं। - अपने विकास के साथ आगे की असंगतियों से बचने के लिए केवल एक सिस्टम से चिपके रहने का प्रयास करें।
moneyलाइब्रेरी संपूर्ण RubyMoney सिस्टम का मूल है, और यह बहुत मजबूत और सीधी है।Money-railsरेल्स एप्लिकेशन के लिए समकक्ष संस्करण है, औरmonetizeतब आवश्यक है जब भी आपको किसी भी मूल्य सेMoneyऑब्जेक्ट्स में पार्स करने की आवश्यकता होती है।Floatका उपयोग करने से बचें। भले ही आपके ऐप को अभी कुछ भी गणना करने की आवश्यकता न हो, संभावना है कि एक अनएडवाइज्ड डेव भविष्य में ऐसा करेगा। आप इसे रोकने के लिए वहां नहीं हो सकते।
याद रखें, आधिकारिक डॉक्स हमेशा जरूरी होने चाहिए। BigDecimal इसके उपयोग के शानदार स्पष्टीकरण और उदाहरणों से भरा है, और यही RubyMoney जेम प्रोजेक्ट्स के लिए भी सच है।