মানি, কারেন্সি যেটি হোক না কেন, লাগে একটি ফ্লোটিং-পয়েন্ট নম্বর। কিন্তু ফ্লোটসকে কারেন্সির জন্য ব্যবহার করা একটি ভুল।
ফ্লোট নম্বর (অতএব, ফ্লোট অবজেক্ট) হলো, ডেফিনিশন অনুযায়ী, ইনএকজেক্ট রিয়েল নম্বর যেগুলো নেটিভ আর্কিটেকচারের ডাবল-প্রেসিশন ফ্লোটিং-পয়েন্ট রিপ্রেজেন্টেশন ব্যবহার করে। ইনএকজেক্ট নম্বর একাউন্টান্টদের অসন্তুষ্ট করে।
এই আর্টিকেলে, আপনাকে কিছু কুইক উদাহরণের মাধ্যমে গাইড করা হবে যাতে আপনি রুবি এবং রেইলসে মানি ডেটা ডিল করার উপলব্ধ অপশনগুলো বুঝতে পারেন।
ফ্লোট কী?
যেনো আমরা বলেছি, float অবজেক্টগুলোর আলাদা অ্যারিথমেটিক রয়েছে। এটাই মূল কারণ যেন তারা ইনএকজেক্ট নম্বর, বিশেষ করে কারণ রুবি (বেশির lenguajes-এর মতো) একটি ফিক্সড নম্বর অফ বাইনারি ডিজিট ব্যবহার করে ফ্লোটস রিপ্রেজেন্ট করতে। অন্য কথায়, রুবি ফ্লোটিং নম্বরগুলো ডেসিমাল থেকে বাইনারি এবং বিপরীত দিকে কনভার্ট করে।
যখন আপনি রিয়েল নম্বরগুলো (অর্থাৎ ডেসিমাল নম্বর) এর বাইনারি রিপ্রেজেন্টেশনে গভীরে যান, তখন তাদের কিছুই একজাক্টলি রিপ্রেজেন্ট করা যায় না, তাই সিস্টেমের একমাত্র অপশন হয় সেগুলো রাউন্ড অফ করা।
আপনি যদি আগে থেকে চিন্তা করেন এবং কমন ম্যাথ স্ট্রাকচার, যেমন পিরিওডিক টিথ, কনসিডার করেন, তবে আপনি বুঝতে পারেন যে সেগুলো একটি ফিক্সড নম্বরে সম্পূর্ণরূপে রিপ্রেজেন্ট করা যায় না যেহেতু পাই নম্বর, উদাহরণস্বরূপ, ইনফিনিট। ফ্লোটস সাধারণত ৩২ বা ৬৪ বিট প্রেসিশন ধারণ করতে পারে, অর্থাৎ নম্বরটি কাটা হবে যখন সেটা লিমিটে পৌঁছাবে।
চলুন একটি ক্লাসিক উদাহরণ বিশ্লেষণ করি:
1200 * (14.0/100)
এটি একটি স্ট্রেইটফরওয়ার্ড পদ্ধতি একটি নম্বরের পার্সেন্টেজ ক্যালকুলেট করার। ১২০০-এর চৌদ্দ পার্সেন্ট হওয়া উচিত ১৬৮; কিন্তু রুবিতে এই এক্সিকিউশনের রেজাল্ট হবে
1200 * (14.0/100) => 168.00000000000003
তবে, আপনি যদি শুধু ০.১% যোগ করেন ফর্মুলায়, তবে আপনি কিছু ভিন্ন পাবেন:
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")
এই কোড সহজে একটি রিয়েল লজিক রিপ্রেজেন্ট করতে পারে একটি ইকমার্স কার্টের অ্যামাউন্টকে। শেষে, যে আসল ভ্যালু ম্যানিপুলেট হচ্ছে সেটা সবসময় ৪৫.৯৯ থাকবে ৪৫.৯৯৮৯ বা ৪৫.৯৯০০০০০৯ না হয়ে।
এটাই হলো 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 এই সমস্যা চিন্তা করার পর তৈরি হয়েছে। এটি রুবি ডেভেলপারদের একটি ওপেন-সোর্স কমিউনিটি যারা ডেভেলপারদের জীবন সহজ করার লক্ষ্যে রুবি ইকোসিস্টেমে মানি ডেটা ম্যানিপুলেট করার জন্য চমৎকার লাইব্রেরি প্রদান করতে চায়।
প্রজেক্টটি সাতটি লাইব্রেরির সমন্বিত, যDER তিনটি গুরুত্বপূর্ণ হিসেবে দেখা যায়:
- Money: মানি এবং কারেন্সি কনভারশন ডিল করার জন্য একটি রুবি লাইব্রেরি। এটি ওয়েব হোক না হোক, রবাস্ট এবং মডার্ন অ্যাপ্লিকেশনের জন্য মানি হ্যান্ডেল করার জন্য বেশ কিছু অবজেক্ট-ওরিয়েন্টেড অপশন প্রদান করে।
- Money-rails: RubyMoney-এর রুবি অন রেইলসের সাথে ইন্টিগ্রেশন,
moneyলাইব্রেরির সকল পাওয়ার রেইলস ফ্লেক্সিবিলিটির সাথে মিক্স করে। - Monetize: বিভিন্ন অবজেক্টকে
moneyঅবজেক্টে কনভার্ট করার জন্য একটি লাইব্রেরি। এটি অ্যাপ্লিকেশনের জন্য যেগুলো স্ট্রিং পার্সিং-এ অনেক ডিল করে তার জন্য একটি অক্সিলিয়ারি লাইব্রেরির মত কাজ করে।
প্রজেক্টের চারটি অন্যান্য ইন্ড্রেস্টিং লাইব্রেরি রয়েছে:
- EU_central_bank: একটি লাইব্রেরি যা এক্সচেঞ্জ রেট ক্যালকুলেট করতে সাহায্য করে ইউরোপিয়ান সেন্ট্রাল ব্যাংক থেকে পাবলিশড রেট ব্যবহার করে।
- Google_currency: একটি ইন্ড্রেস্টিং লাইব্রেরি কারেন্সি কনভারশনের জন্য গুগল কারেন্সি রেটকে রেফারেন্স হিসেবে ব্যবহার করে।
- Money-collection:
moneyঅবজেক্টের সম/মিন/ম্যাক্স সঠিকভাবে ক্যালকুলেট করার জন্য একটি অক্সিলিয়ারি লাইব্রেরি। - Money-heuristics: স্ট্রিং ইনপুটের হিউরিস্টিক অ্যানালিসিসের জন্য
moneyজেমের একটি মডিউল।
"Money" জেম
সর্বপ্রसిద্ধটি দিয়ে শুরু করি: money জেম। এর প্রধান ফিচারগুলোর মধ্যে রয়েছে:
- একটি
moneyক্লাস যা প্রাসঙ্গিক মানদণ্ডীয় ইনফরমেশন ধারণ করে, যেমন ভ্যালু, কারেন্সি, এবং ডেসিমাল মার্কস। - একটি অন্য ক্লাস
Money::Currencyযা ডেভেলপার যে মানদণ্ডীয় ইউনিট ব্যবহার করছেন সেটা র্যাপ করে। - ডিফল্টভাবে, এটি ইন্টিজার্সের সাথে কাজ করে ফ্লোটিং-পয়েন্ট নম্বরের বদলে উপরে উল্লিখিত এররগুলো এড়াতে।
- এক কারেন্সি থেকে অন্য কারেন্সিতে মানি এক্সচেঞ্জ করার ক্ষমতা, যা সুপার কুল।
এছাড়া, আমরা পাই হাই ফ্লেক্সিবিলিটি কনসিস্টেন্ট এবং অবজেক্ট-ওরিয়েন্টেড স্ট্রাকচার থেকে মানি ডেটা ম্যানিপুলেট করার জন্য, যেন আপনার প্রজেক্টের অন্য যেকোনো মডেল।
এটার ব্যবহার খুব সিম্পল, শুধু প্রপার জেম ইনস্টল করুন:
gem install money
একটি ফিক্সড অ্যামাউন্ট অফ মানি ইনভল্ভ করে একটি কুইক উদাহরণ হবে
my_money = Money.new(1200, "USD") my_money.cents #=> 1200 my_money.currency #=> Currency.new("USD")
আপনি দেখতে পাচ্ছেন, মানি সেন্টস বেসড রিপ্রেজেন্টেড। বারহunderte সেন্টস সমান ১২ ডলার।
যদিও আপনি 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 (মানি ডেটার ভ্যালু এবং ফ্র্যাকশনের মাঝপথের ক্যারেক্টার), অন্যান্য।
ওহ, আমি почти ভুলে গিয়েছিলাম; একবার money জেম ইনস্টল করলে, আপনি একটি 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 জেম প্রজেক্টগুলোর জন্যও একই কথা।