In modern software it is often necessary to perform various operations with all sorts of money. However, until now I have not come across documentation anywhere that summarizes the basic rules for presenting amounts and implementing financial calculations. In this article I will try to formulate the rules that I made myself on the basis of personal experience.

Do not use double
The fact that the storage of amounts can not use binary type floating point single precision float, everyone knows. However, it is widely believed that double can be used instead of float. Meanwhile, double is not much better.
The fact is that in these types the number is represented as sums of degrees with a base of 2. While monetary amounts in all prices and documents are presented in decimal number system. Most of the fractional numbers in the decimal number system do not have an exact representation in the form of a finite sum of powers of two.
')
Suppose the online store sells gloves for 599 rubles 99 kopecks. This is what this number will look like in programs that use binary floating point types.
599.99Java code
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());
}
}