Well, fractional part of double v = 555.55
(which is double v = 0.55
) is a periodic binary fraction which
can't be represented exactly. What options do we have? Let's make a smallest possible change of 555.55
(i.e. we change the last bit of 555.55
):
double v = 555.55;
int v2 = (int)Math.Floor(v / 100.0);
double v4 = v - v2 * 100;
// Here we increment the last bit of v (minimum possible change)
byte[] bytes = BitConverter.GetBytes(v);
bytes[0] = (byte) (bytes[0] + 1);
double d = BitConverter.ToDouble(bytes);
double dv = d - v2 * 100;
string result = string.Join(Environment.NewLine,
$" v = {v:R}",
$"v4 = {v4:R}",
$" v' = {d:R}",
$"v4' = {dv:R}");
Console.Write(result);
Outcome:
v = 555.55
v4 = 55.549999999999955
v' = 555.5500000000001
v4' = 55.55000000000007
So the only options we have either 55.549999999999955
or 55.55000000000007
; please, note, that 55.549999999999955
is a better one (4.5e-14
< 7.0e-14
).
How to fix it? As you can see, floating point arithmetics brings rounding error; often we can change double
into decimal
which is standard practice if 555.55
is some kind of currency value (say, dollars and cents):
decimal v = 555.55m;
int v2 = (int)Math.Floor(v / 100m);
decimal v3 = v2 * 100;
decimal v4 = v - v2 * 100;
decimal v5 = v - v3;
string result = string.Join(Environment.NewLine,
$" v = {v}",
$"v2 = {v2}",
$"v3 = {v3}",
$"v4 = {v4}",
$"v5 = {v5}");
Console.Write(result);
Outcome:
v = 555.55
v2 = 5
v3 = 500
v4 = 55.55
v5 = 55.55