-2

I was coding when I found it:

double v = 555.55;
int v2 = (int)Math.Floor(v / 100.0);
double v3 = v2 * 100;
double v4 = v - v2 * 100;
double v5 = v - v3;

At the end, the code says:

v = 555.55
v2 = 5 
v3 = 500 
v4 = 55.549999999999955 
v5 = 55.549999999999955

So, why is that happening and how could I fix it?

srjheam
  • 25
  • 4
  • What is the expected result? The code is doing exactly what you told it to do. – Delsx Oct 24 '20 at 20:26
  • What do you mean "the code says"? How come in some of your printout there are only 2 decimal digits, and in others there are more than 10 decimal digits? How exactly are you printing each one of these variables? I bet that if you print `v` in the same precision that you're printing `v4` and `v5`, you'll see the exact same value. – goodvibration Oct 24 '20 at 20:27
  • Pretty sure this is duplicate of “floating point math is broken”... but need to wait for clarification from OP – Alexei Levenkov Oct 24 '20 at 20:29
  • 555.55 - (5 * 100) = 55.55 is different from 55.549999999999955. I wanna know why am I losing 4,5 * 10^(-14) in the process. – srjheam Oct 24 '20 at 20:33
  • @goodvibration I saw those values from VS2019, but if I print them the console displays the same. – srjheam Oct 24 '20 at 20:37
  • Thank you, @AlexeiLevenkov I'm going to see that question. – srjheam Oct 24 '20 at 20:38

2 Answers2

2

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
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
-1

Well, by turning a double into an int, you are going to lose information, because an int is a whole number, so everything after the decimal point gets cut away.

af2111
  • 301
  • 3
  • 12