首页 > 解决方案 > 为什么我会丢失信息?

问题描述

当我发现它时,我正在编码:

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;

最后,代码说:

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

那么,为什么会发生这种情况,我该如何解决呢?

标签: c#mathdouble

解决方案


好吧,(即)的小数部分是一个周期性的二进制分数, 不能精确地表示。我们有什么选择?让我们做一个最小可能的改变 (即我们改变最后一位):double v = 555.55double v = 0.55555.55555.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);

结果:

 v  = 555.55
v4  = 55.549999999999955
 v' = 555.5500000000001
v4' = 55.55000000000007

所以我们唯一的选择是55.549999999999955or 55.55000000000007; 请注意,这55.549999999999955是一个更好的(4.5e-14< 7.0e-14)。

如何解决?如您所见,浮点运算会带来舍入误差;如果是某种货币价值(例如,美元和美分),我们通常可以将其更改doubledecimal标准做法:555.55

  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);

结果:

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

推荐阅读