masuda220/business-logic-patterns

固定小数点数を扱う DecimalAmount型 の試作

Closed this issue · 7 comments

これは、ひとつのissueでやるには、たぶん大きすぎるテーマです。
考えることがたくさんありそう。

もしやっていただける場合は、いくつかのissueに分解して、進めていただければと思います。

概要:小数点以下2桁の金額(円の場合は、銭単位まで)の四則演算

試作仕様は、

  • 小数点以下2桁固定
  • 乗算、除算の引数は、整数(int)
  • 除算の場合、小数点三桁を四捨五入する
  • 小数点以下を四捨五入して、Amount型(整数の金額)を返すメソッドを用意する
  • Amount型から、long値を取り出すメソッドが必要かもしれない。
  • それ以外は、Amount型は、この型の実装のために、変更はしないことが望ましい

実装のアイデアとしては、

  • Amount型を non-scale value として持つ、あるいは、long値をもち、適宜、Amount型を使用する
  • int で、小数点以下の scale値を持つ( 試作版では、2桁固定)
  • つまり、最大金額は、Amount型の最大金額 (LONG_MAX)の 100分の1である
  • コンストラクタは、文字列表現( "12.34")のみ、取り込む
  • toString() は、コンストラクタに渡せる文字列表現を出力する

留意点としては、

  • BigDecimalのAPI ドキュメントとソースコードを参考に試作する
  • 将来的には、小数点以下の桁数は、1~4の4種類を持つことを想定
  • 将来的には、四捨五入以外に、切捨て、切り上げ、「銀行家の丸め」も対応したい
  • まずは、今回の試作仕様で、割り切りバージョンで動かしてから、段階的に進化させていく

money パッケージに追加する

【参考】実装方法の他のアイデア

コードを書いて、どれがよさそうか、考えてみたい。

  • インスタンス変数にBigDecimalを持つ
  • インスタンス変数に long nonScaleValue と int scale(小数点以下の桁数)を持つ
  • インスタンス変数に long beforePointValue(整数部)と int afterPointValue を持つ

BigDecimal

  • 最初のBigDecimalを持つ方法がいちばん実装が簡単か?
  • ただし、実質無限の最大・最小ではなく、longの最大最小以下の制限したい

long 値+ scale

  • long の値+int sacle という方法は、BigDecimalの(longに収まる場合の)実装を、そのままという感じになる
  • 技術的な勉強にはなるが、低レベルの実装すぎるか?

整数部+小数部

  • 整数部(long)と小数部(int)を分けてもつのが、固定小数点数のモデルの直接的な写像に思える
  • 実装が以外と面倒くさいかも

<参考> 大人の学びなおし教室

小数の足し算・引き算は、小数点を揃えることがポイント
https://keisan.otona-juku.com/01021decimal-plus/
https://keisan.otona-juku.com/01022decimal-minus/
※小数点以下の桁数が違う場合、多いほうにそろえる(少ないほうは後ろに0がつく)

小数の掛け算は、整数のように計算して、小数点をずらす
https://keisan.otona-juku.com/01023decimal-multiply/
https://keisan.otona-juku.com/01024decimal-multiply/
(少数×整数と少数×小数は、別の説明)

小数÷整数は、整数のように計算して、小数点をずらす
https://keisan.otona-juku.com/01025decimal-division-integer/

小数÷小数は、まず小数点をずらして小数÷整数にしてから計算する
https://keisan.otona-juku.com/01026decimal-division/

abetd commented

BigDecimal で仮実装してみました。
やはり、他の2案よりも、実装が簡単だと思いました。

現在は小数点以下2桁で実装していますが、
小数点以下の桁数を1〜4にの4種類にする実装の際に、
異なる桁数の加算減算は可能とするか、
可能な場合は計算結果の桁数はどうするかなどが気になりました。

@abetd
ありがとうございます。
良い感じですね。

一点、細かいですが、引数が val のところは、value にしてください。原則フルスペルでお願いします。

ファーストバージョンとしては、十分なので、プルリクエストしてください。

桁数に関する仕様は、おっしゃる通り、検討課題ですね。
DecimalAmount型の利用例などを考えながら、検討したいと思います。
(仕様検討のissueを追加しました。 #38 )

abetd commented

確認ありがとうございます。
引数を修正し、プルリクエストしました。

桁数に関する仕様について、Issue の追加ありがとうございます。

ありがとうございます。
マージ&クローズです。

コードを読んでいて気が付いたのですが、最大最小の範囲が、厳密には、桁数に依存して変わるべきですね。 これは、#38 のほうに追加しておきます。