Rubyでの通貨計算
お金は、通貨に関係なく、浮動小数点数のように見えます。しかし、通貨にフロートを使用するのは間違いです。
浮動小数点数(したがって、floatオブジェクト)は、定義上、ネイティブアーキテクチャの特徴である倍精度浮動小数点表現を利用する不正確な実数です。不正確な数字は会計士を不幸にします。
この記事では、RubyとRailsでお金のデータを処理するために利用できるオプションに対処するのに役立ついくつかの簡単な例を紹介します。
フロートとは何ですか?
すでに述べたように、 float
オブジェクトの算術は異なります。これが不正確な数値である主な理由です。特に、Ruby(ほとんどの言語と同様)は固定数の2進数を使用して浮動小数点数を表すためです。つまり、Rubyは浮動小数点数を10進数から2進数に、またはその逆に変換します。
実数(別名10進数)の2進表現を深く掘り下げると、それらの一部は正確に表現できないため、残りのオプションはシステムがそれらを四捨五入することだけです。
先を考えて、定期的な什分の一などの一般的な数学構造を検討すると、 pi 以降、固定数内で完全に表すことができないことを理解できます。 たとえば、数は無限大です。フロートは通常、最大32ビットまたは64ビットの精度を保持できます。つまり、制限に達すると数値が切り捨てられます。
古典的な例を分析してみましょう:
1200 * (14.0/100)
これは、数値のパーセンテージを計算する簡単な方法です。 1200の14パーセントは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")
このコードは、eコマースカートの金額を含む実際のロジックを簡単に表すことができます。最終的に、操作される実際の値は、たとえば45.9989や45.99000009ではなく、常に45.99になります。
これが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つのライブラリで構成されており、そのうち3つが重要です。
- Money:お金と通貨の変換を処理するためのRubyライブラリ。 Web向けかどうかに関係なく、堅牢で最新のアプリケーションでお金を処理するためのオブジェクト指向オプションをいくつか提供します。
- Money-rails:RubyMoney for Ruby on Railsの統合であり、すべての
money
Railsの柔軟性を備えたのライブラリパワー。 - 収益化:さまざまなオブジェクトを
money
に変換するためのライブラリ オブジェクト。たとえば、多くの文字列解析を処理するアプリケーションの補助ライブラリのように機能します。
このプロジェクトには、他に4つの興味深いライブラリがあります。
- EU_central_bank:欧州中央銀行の公表レートを利用して為替レートを計算するのに役立つライブラリ。
- Google_currency:Googleの通貨レートを参照として使用する通貨換算のための興味深いライブラリ。
- Money-collection:
money
の合計/最小/最大を正確に計算するための補助ライブラリ オブジェクト。 - Money-heuristics:MoneyGemの文字列入力のヒューリスティック分析のためのモジュール。
最も有名なもの、お金の宝石から始めましょう。その主な機能は次のとおりです。
お金コード> 値、通貨、小数点などの関連する金銭情報を保持するクラス。
-
Money ::Currency
と呼ばれる別のクラス これは、開発者が使用している通貨単位に関する情報をラップします。 - デフォルトでは、前述のエラーを回避するために、浮動小数点数ではなく整数で機能します。
- ある通貨から別の通貨にお金を交換する機能。これは非常に優れています。
それ以外に、プロジェクト内の他のモデルと同じように、お金のデータを操作するための一貫性のあるオブジェクト指向の構造によって提供される高い柔軟性も得られます。
その使用法は非常に簡単です。適切なgemをインストールするだけです:
gem install money
固定金額を含む簡単な例は次のようになります
my_money = Money.new(1200, "USD")
my_money.cents #=> 1200
my_money.currency #=> Currency.new("USD")
ご覧のとおり、お金はセントに基づいて表されます。 1200セントは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
など、提供されている通貨属性の詳細については、ドキュメントを参照してください。 (その通貨の国際的な3桁のコードを返します)および decimal_mark
(値とお金のデータの端数の間の文字)、とりわけ。
ああ、私はほとんど忘れていました。 Money Gemをインストールすると、 BigDecimal
にアクセスできます。 to_money
というメソッド 自動的に変換を実行します。
RubyMoneyプロジェクト内で各ライブラリが果たす役割を理解することが重要です。別のRubyオブジェクト( String
)を変換する必要があるときはいつでも 、たとえば) Money
、次に monetize
探しているものです。
まず、gem依存関係をインストールするか、 Gemfileに追加してください。 :
gem install monetize
明らかに、 money
また、インストールする必要があります。
parse
この方法は、別の形式でお金のデータを受け取る場合にも非常に役立ちます。たとえば、
Money.parse("£100") == Money.new(100, "GBP") #=> true
この解析方法を使用するシナリオは制限されていますが、HTTPリクエストから通貨コードと一緒にフォーマットされた値を受け取る場合に非常に役立ちます。ウェブ上では、すべてがテキストであるため、文字列から money
に変換します 非常に便利です。
ただし、システムが値を操作する方法と、値が何らかの方法でハッキングされる可能性があるかどうかに注意してください。金融システムは常に複数のセキュリティレイヤーでカバーされており、受け取る価値がそのトランザクションの真の価値であることを保証します。
これは、同じお金の操作操作を処理するライブラリですが、Railsアプリ内にあります。
Railsと一緒に機能させるためだけに2つ目のライブラリが必要なのはなぜですか?ええと、あなたは確かにお金
を利用することができます 通常の数学演算のためのRailsプロジェクト内のgemのみ。ただし、Rails構造が money
と通信する必要がある場合は、正しく機能しません。 の機能。
次の例を考えてみましょう:
class Cart < ActiveRecord::Base
monetize :amount_cents
end
これは、実際の機能的なRailsモデルオブジェクトです。データベース(別のモデル属性名が必要な場合はエイリアスも含む)、Mongoid、RESTWebサービスなどと一緒に使用できます。
これまでに使用したすべての機能は、この宝石にも適用されます。通常、実行するには追加の設定のみが必要です。これは、 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
インスタンス。 - 開発に伴うさらなる矛盾を避けるために、1つのシステムのみに固執するようにしてください。
お金コード> ライブラリはRubyMoneyシステム全体の中核であり、非常に堅牢で簡単です。
Money-rails
Railsアプリケーションと同等のバージョンであり、monetize
任意の値からMoney
に解析する必要がある場合は常に必要です オブジェクト。-
Float
の使用は避けてください 。アプリが現在何も計算する必要がない場合でも、アドバイスを受けていない開発者が将来計算する可能性があります。あなたはそれを止めるためにそこにいないかもしれません。
公式ドキュメントは常に必須である必要があることを忘れないでください。 BigDecimalには、その使用法の優れた説明と例がたくさんあります。同じことがRubyMoneygemプロジェクトにも当てはまります。
-
Rubyでの挿入ソートを理解する
注:これは、Rubyを使用したさまざまなソートアルゴリズムの実装を検討するシリーズのパート4です。パート1ではバブルソート、パート2では選択ソート、パート3ではマージソートについて説明しました。 データを並べ替えるためのさまざまな方法を引き続き検討するため、挿入並べ替えに目を向けます。挿入ソートが好きな理由はたくさんあります!まず、挿入ソートは安定です。 、これは、等しいキーを持つ要素の相対的な順序を変更しないことを意味します。 インプレースアルゴリズムでもあります 、は、並べ替えられた要素を格納するための新しい配列を作成しないことを意味します。最後に、挿入ソートは、すぐにわかるように、実
-
Ruby2.6の9つの新機能
Rubyの新しいバージョンには、新しい機能とパフォーマンスの改善が含まれています。 変更についていきますか? 見てみましょう! 無限の範囲 Ruby 2.5以前のバージョンは、すでに1つの形式の無限範囲をサポートしています( Float ::INFINITY を使用) )、しかしRuby2.6はこれを次のレベルに引き上げます。 新しい無限の範囲 次のようになります: (1..) これは、(1..10)のような終了値がないため、通常の範囲とは異なります。 。 使用例 : [a, b, c].zip(1..) # [[a, 1], [b, 2], [c, 3]] [1,2,3,