Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

การคำนวณสกุลเงินใน Ruby

เงินไม่ว่าจะอยู่ในสกุลเงินใดก็ตาม ดูเหมือนเป็นตัวเลขทศนิยม แต่การใช้โฟลตสำหรับสกุลเงินถือเป็นความผิดพลาด

ตัวเลขทศนิยม (ด้วยเหตุนี้ วัตถุลอย) เป็นจำนวนจริงที่ไม่แน่นอนซึ่งใช้ประโยชน์จากคุณลักษณะการแสดงจุดทศนิยมแบบ double-precision ของสถาปัตยกรรมดั้งเดิม ตัวเลขที่ไม่แน่นอนทำให้นักบัญชีไม่มีความสุข

ในบทความนี้ คุณจะได้รับคำแนะนำเกี่ยวกับตัวอย่างสั้นๆ ที่จะช่วยให้คุณระบุตัวเลือกที่มีในการจัดการข้อมูลเงินใน Ruby and Rails

โฟลตคืออะไร

อย่างที่เราบอก float วัตถุมีเลขคณิตต่างกัน นี่คือเหตุผลหลักที่พวกมันเป็นตัวเลขที่ไม่แน่นอน โดยเฉพาะอย่างยิ่งเนื่องจาก Ruby (เช่นเดียวกับภาษาส่วนใหญ่) ใช้เลขฐานสองจำนวนคงที่เพื่อแสดงจำนวนทศนิยม กล่าวอีกนัยหนึ่ง Ruby จะแปลงตัวเลขลอยตัวจากทศนิยมเป็นเลขฐานสองและในทางกลับกัน

เมื่อคุณเจาะลึกลงไปในการแสดงเลขฐานสองของจำนวนจริง (หรือที่เรียกว่าเลขฐานสิบ) ตัวเลขบางตัวจะไม่สามารถแสดงได้ทั้งหมด ดังนั้นตัวเลือกเดียวที่เหลือคือให้ระบบปัดเศษออก

หากคุณคิดล่วงหน้าและพิจารณาโครงสร้างทางคณิตศาสตร์ทั่วไป เช่น ส่วนสิบเป็นงวด คุณอาจเข้าใจว่าไม่สามารถแสดงทั้งหมดภายในจำนวนคงที่ได้ตั้งแต่ pi จำนวนตัวอย่างเช่นเป็นอนันต์ โฟลตมักจะเก็บความแม่นยำได้ถึง 32 หรือ 64 บิต ซึ่งหมายความว่าตัวเลขจะถูกตัดออกเมื่อถึงขีดจำกัด

มาวิเคราะห์ตัวอย่างคลาสสิกกัน:

1200 * (14.0/100)

นี่เป็นวิธีที่ตรงไปตรงมาในการคำนวณเปอร์เซ็นต์ของตัวเลข สิบสี่เปอร์เซ็นต์ของ 1200 ควรเป็น 168; อย่างไรก็ตาม ผลลัพธ์ของการดำเนินการนี้ภายใน Ruby จะเป็น

1200 * (14.0/100)
=> 168.00000000000003

อย่างไรก็ตาม หากคุณเพิ่มเพียง 0.1% ลงในสูตร คุณจะได้ผลลัพธ์ที่แตกต่าง:

1200 * (14.1/100)
=> 169.2

หรือคุณสามารถ round ค่าที่ใกล้เคียงที่สุด กำหนดจำนวนตำแหน่งทศนิยมที่ต้องการ:

(my_calculation).round(2)

อันที่จริง ไม่รับประกันว่าจะมีการคำนวณที่ซับซ้อนมากขึ้น โดยเฉพาะอย่างยิ่งหากคุณทำการเปรียบเทียบค่าเหล่านี้

หากคุณสนใจที่จะเข้าใจวิทยาศาสตร์ที่แท้จริงเบื้องหลัง ขอแนะนำให้อ่านภาคผนวกของ Oracle:สิ่งที่นักวิทยาศาสตร์คอมพิวเตอร์ทุกคนควรรู้เกี่ยวกับเลขคณิตจุดลอยตัว โดยจะอธิบายรายละเอียดถึงสาเหตุที่อยู่เบื้องหลังลักษณะที่ไม่ถูกต้องของจำนวนทศนิยม

ทศนิยมที่น่าเชื่อถือ

พิจารณาข้อมูลโค้ดต่อไปนี้:

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 สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับหัวข้อนี้ โปรดดูเอกสาร Ruby

ในกรณีที่คุณจำเป็นต้องกำหนดขีด จำกัด ของตำแหน่งทศนิยม จะมี truncate วิธีที่จะทำงานให้คุณ:

(BigDecimal(1200) * (BigDecimal("14.12")/BigDecimal(100))).truncate(2).to_s("F")
=> 169.44

โครงการ RubyMoney

RubyMoney ถูกสร้างขึ้นหลังจากคิดถึงปัญหาเหล่านี้ เป็นชุมชนโอเพ่นซอร์สของนักพัฒนา Ruby ที่มุ่งอำนวยความสะดวกให้กับชีวิตของนักพัฒนาโดยการจัดหาห้องสมุดที่ยอดเยี่ยมเพื่อจัดการข้อมูลเงินในระบบนิเวศของ Ruby

โครงการนี้ประกอบด้วยห้องสมุด 7 แห่ง โดยสามแห่งมีความโดดเด่น:

  • เงิน:ห้องสมุด Ruby สำหรับจัดการกับเงินและการแปลงสกุลเงิน มีตัวเลือกเชิงวัตถุหลายตัวในการจัดการเงินในแอปพลิเคชันที่มีประสิทธิภาพและทันสมัย ​​ไม่ว่าจะใช้สำหรับเว็บหรือไม่
  • Money-rails:การรวม RubyMoney สำหรับ Ruby on Rails โดยผสม money ทั้งหมด พลังของห้องสมุดที่มีความยืดหยุ่นของ Rails
  • สร้างรายได้:ห้องสมุดสำหรับแปลงวัตถุต่างๆ เป็น money วัตถุ มันทำงานเหมือนไลบรารีเสริมสำหรับแอปพลิเคชันที่จัดการกับการแยกวิเคราะห์สตริงจำนวนมาก เป็นต้น

โครงการนี้มีห้องสมุดที่น่าสนใจอีกสี่แห่ง:

  • EU_central_bank:ห้องสมุดที่ช่วยคำนวณอัตราแลกเปลี่ยนโดยใช้อัตราที่เผยแพร่จากธนาคารกลางยุโรป
  • Google_currency:ห้องสมุดที่น่าสนใจสำหรับการแปลงสกุลเงินโดยใช้อัตราแลกเปลี่ยนของ Google Currency
  • การเก็บเงิน:ห้องสมุดเสริมสำหรับการคำนวณผลรวม/นาที/สูงสุดของ money อย่างแม่นยำ วัตถุ
  • Money-heuristics:โมดูลสำหรับการวิเคราะห์ heuristic ของการป้อนสตริงสำหรับ money gem

อัญมณี “เงิน”

เริ่มจากสิ่งที่มีชื่อเสียงที่สุด:อัญมณีเงิน ในบรรดาคุณสมบัติหลักดังต่อไปนี้:

  • A 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 (อักขระระหว่างค่าและเศษส่วนของข้อมูลเงิน) เป็นต้น

เกือบลืมไปเลย เมื่อคุณติดตั้ง Money gem แล้ว คุณจะสามารถเข้าถึง BigDecimal วิธีการที่เรียกว่า to_money ที่ทำการแปลงให้คุณโดยอัตโนมัติ

อัญมณี “สร้างรายได้”

สิ่งสำคัญคือต้องเข้าใจบทบาทของห้องสมุดแต่ละแห่งในโครงการ RubyMoney เมื่อใดก็ตามที่คุณต้องการแปลงวัตถุ Ruby อื่น (a String เช่น) เป็น money แล้ว monetize คือสิ่งที่คุณกำลังมองหา

ขั้นแรก ตรวจสอบให้แน่ใจว่าได้ติดตั้งการพึ่งพาอัญมณีหรือเพิ่มลงใน Gemfile . ของคุณ :

gem install monetize

แน่นอน money ต้องติดตั้งด้วย

parse วิธีการยังมีประโยชน์มากเมื่อคุณได้รับข้อมูลเงินในรูปแบบอื่น ตัวอย่างเช่น

Money.parse("£100") == Money.new(100, "GBP") #=> true

แม้ว่าสถานการณ์ที่คุณใช้วิธีการแยกวิเคราะห์นี้จะถูกจำกัด แต่ก็มีประโยชน์มากเมื่อคุณได้รับค่าที่จัดรูปแบบควบคู่ไปกับรหัสสกุลเงินจากคำขอ HTTP บนเว็บ ทุกอย่างเป็นข้อความ ดังนั้นให้แปลงจากสตริงเป็น money มีประโยชน์มาก

อย่างไรก็ตาม โปรดใช้ความระมัดระวังว่าระบบของคุณจัดการกับค่าต่างๆ อย่างไร และอาจถูกแฮ็กได้หรือไม่ ระบบการเงินมีชั้นความปลอดภัยหลายชั้นครอบคลุมเสมอ เพื่อให้แน่ใจว่ามูลค่าที่คุณได้รับนั้นเป็นมูลค่าที่แท้จริงของธุรกรรมนั้น

อัญมณี “monetize-rails”

นี่คือห้องสมุดที่เกี่ยวข้องกับการดำเนินการจัดการเงินแบบเดียวกัน แต่อยู่ในแอป Rails

เหตุใดเราจึงต้องมีห้องสมุดที่สองเพื่อให้ทำงานร่วมกับ Rails ได้ คุณสามารถใช้ money . ได้อย่างแน่นอน gem เพียงอย่างเดียวภายในโครงการ Rails สำหรับการดำเนินการทางคณิตศาสตร์ทั่วไป อย่างไรก็ตาม มันจะทำงานไม่ถูกต้องเมื่อโครงสร้าง Rails ของคุณต้องสื่อสารกับmoney คุณสมบัติของ

ลองพิจารณาตัวอย่างต่อไปนี้:

class Cart < ActiveRecord::Base
  monetize :amount_cents
end

นี่คืออ็อบเจ็กต์โมเดล Rails ที่ใช้งานได้จริง คุณสามารถใช้ร่วมกับฐานข้อมูล (รวมถึงชื่อแทนเมื่อคุณต้องการชื่อแอตทริบิวต์รุ่นอื่น), Mongoid, บริการเว็บ REST ฯลฯ

ฟีเจอร์ทั้งหมดที่เราเคยติดต่อมาก็มีผลกับอัญมณีนี้ด้วย โดยปกติ จำเป็นต้องตั้งค่าเพิ่มเติมเท่านั้นเพื่อให้ทำงาน ซึ่งควรวางไว้ใน config/initializers/money.rb ไฟล์:

MoneyRails.configure do |config|

  # set the default currency
  config.default_currency = :usd

end

การดำเนินการนี้จะตั้งค่าสกุลเงินเริ่มต้นเป็นสกุลเงินที่คุณระบุ อย่างไรก็ตาม ในระหว่างการพัฒนา มีโอกาสที่คุณอาจต้องดำเนินการแปลงการแลกเปลี่ยนหรือจัดการมากกว่าหนึ่งสกุลเงินตลอดทั้งรูปแบบ

ถ้าใช่ money-rails ช่วยให้เราสามารถกำหนดค่าการกำหนดสกุลเงินระดับแบบจำลอง:

class Cart < ActiveRecord::Base

  # Use GPB as model level currency
  register_currency :eur

  monetize :amount_cents, as "amount"
  monetize :discount_cents, with_currency: :eur

  monetize :converted_value, with_currency: :usd

end

โปรดทราบว่าเมื่อตั้งค่าทุกอย่างเรียบร้อยแล้ว คุณจะใช้ประเภทเงินร่วมกับโครงการได้ง่ายมากๆ

สรุปผล

ในบล็อกโพสต์นี้ เราได้สำรวจตัวเลือกที่มีอยู่เพื่อจัดการกับมูลค่าเงินภายในระบบนิเวศของ Ruby และ Rails สรุปประเด็นสำคัญบางส่วนไว้ด้านล่าง:

  • หากคุณกำลังจัดการกับการคำนวณเลขทศนิยม โดยเฉพาะอย่างยิ่งถ้าเป็นข้อมูลเงิน ให้ไปที่ BigDecimal หรือ money ตัวอย่าง
  • พยายามยึดติดกับระบบเดียวเท่านั้นเพื่อหลีกเลี่ยงความไม่สอดคล้องกันเพิ่มเติมควบคู่ไปกับการพัฒนาของคุณ
  • The money ห้องสมุดเป็นแกนหลักของระบบ RubyMoney ทั้งหมด และมีความแข็งแกร่งและตรงไปตรงมามาก money-rails เป็นเวอร์ชันเทียบเท่าสำหรับแอปพลิเคชัน Rails และ monetize จำเป็นเมื่อใดก็ตามที่คุณต้องการแยกค่าจากค่าใดๆ เป็น Money วัตถุ
  • หลีกเลี่ยงการใช้ Float . แม้ว่าแอปของคุณไม่จำเป็นต้องคำนวณอะไรในตอนนี้ แต่โอกาสที่นักพัฒนาซอฟต์แวร์ที่ไม่ได้รับคำแนะนำจะทำได้ในอนาคต คุณอาจจะไม่ได้อยู่ที่นั่นเพื่อหยุดมัน

โปรดจำไว้ว่าเอกสารที่เป็นทางการควรเป็นสิ่งที่ต้องมีเสมอ BigDecimal เต็มไปด้วยคำอธิบายและตัวอย่างการใช้งานที่ยอดเยี่ยม และโครงการอัญมณี RubyMoney ก็เช่นเดียวกัน