金額を扱うための5つのルール

現代のソフトウェアでは、多くの場合、あらゆる種類のお金でさまざまな操作を実行する必要があります。 しかし、これまでのところ、金額を提示し、財務計算を実行するための基本的なルールをまとめたドキュメントに出くわしていません。 この記事では、個人的な経験に基づいて自分で作成したルールを定式化しようとします。







ダブルを使用しないでください



単精度の浮動小数点数を持つバイナリ型を使用して合計を格納することは不可能であることは誰もが知っています。 ただし、floatの代わりにdoubleを使用できると広く信じられています。 一方、doubleはあまり良くありません。



実際、これらのタイプでは、数値は2を底とする度数の合計の形式で表示されますが、すべての価格とドキュメントの金額の合計は10進表記で表示されます。 10進数システムのほとんどの小数には、2の累乗の有限和の形式での正確な表現がありません。



オンラインストアで手袋が599ルーブル99コペックで販売されているとします。 バイナリの浮動小数点型を使用するプログラムでは、その数値は次のようになります。



599.99
Javaコード

float f = 599.99f;
System.out.printf("float %s%n", new BigDecimal(f).setScale(6, BigDecimal.ROUND_DOWN));
double d = 599.99d;
System.out.printf("double %s%n", new BigDecimal(d).setScale(15, BigDecimal.ROUND_DOWN));

      
      





C#

float f = 599.99f;
float fi = (long) f;
float fp = f - fi;
long fil = (long) fi;
long fpl = (long) (fp * 1000000);
Console.WriteLine("float {0}.{1}", fil, fpl);
double d = 599.99d;
double di = Math.Truncate(d);
double dp = d - di;
long dil = (long) di;
long dpl = (long) (dp * 1000000000000000);
Console.WriteLine("double {0}.{1}", dil, dpl);

      
      





float 599.989990

double 599.990000000000009







, , . float — 6- , double — 15-.



. , . , .



, . double , .



, , . :



z = 599.99 . - 0.98 . - 599.00 . - 0.01 .
Java

double z = ((599.99d - 0.98d) - 599.00d) - 0.01d;
if (z == 0d) {
    System.out.println("z == 0");
} else if (z > 0d){
    System.out.println("z > 0");
} else {
    System.out.println("z < 0");
}

      
      







C#

double z = ((599.99d - 0.98d) - 599.00d) - 0.01d;
if (z == 0d)
{
    Console.WriteLine("z == 0");
}
else if (z > 0d)
{
    Console.WriteLine("z > 0");
}
else
{
    Console.WriteLine("z < 0");
}

      
      









z < 0







double 10. Java BigDecimal, C# — decimal.



, . , ±, ( ) , .





0. , , , . : « », — : « ».



, . .



, , -, . . . -, , -1500 . , . , , , . 3000 . . , , .



« », . , . , , , . , , , , , . , . , . , , . , . , .



, . , , , , . .



, . : , , , , , . . - , . , — .



, , , . ? ? , - - . , 2 , , , : « 3500 ...», «… 20000» « -, -».



— ,



, . . , , /c, . . — , . , .



, , . . , BYR, — BYN. , 10 , , 1 BYN = 10 000 BYR. BYB, 1 BYR = 1000 BYB. , .



. , . . 3- ISO-4217.



«» «»



«» «» — , . , . , .



« », , — . , « », «», «» . .



. . , 56.61 .? ? , , , 1 - , , , , , , — . , , , «» «» , . , , — . « 15.75 .», , .



, , , , . buySum, sellSum, buyPrice, sellPrice . ., . , .



, , 56 ? , , . , , , .



, - ? , , 2 : .



«» «» « » « ». , , — . «», «», «», «», «», «» . .





:





? , .



— OK.

— OK.

—?



, , . , , ? 55.61 . 1500.00 .? , ./ , , -1/-2.



, «» . , 100 . 1- 2- .



, — -1/(-2 * )?



, , , .



« » 1$ 56.61₽.

« » 58.79₽ 1$.



₽/$, — $/₽.







. .



100 52.79 , , .



, , - :



public final class ExchangeRate {
    public static final int PRECISION = 4;
    private final Sum clientGives;
    private final Sum clientTakes;

    public ExchangeRate(Sum clientGives, Sum clientTakes) {
        this.clientGives = clientGives;
        this.clientTakes = clientTakes;
    }
 
    public Sum exchange(Sum sum) {
        if (!sum.getCurrency().equals(clientGives.getCurrency())) {
            throw new IllegalArgumentException();
        }
        BigDecimal amount = sum.getAmount().mulitply(clientTakes.getAmount())
                .divide(clientGives.getAmount(), PRECISION, BigDecimal.ROUND_HALF_UP);
        return new Sum(amount, clientTakes.getCurrency());
    }
}

      
      






All Articles