【问题标题】:Precise whole number calculation with double双精度整数计算
【发布时间】:2015-10-28 12:06:58
【问题描述】:

考虑以下:

double x = 87974894654657d;
double y = 76216354532345d;
double a = x * y;
Console.WriteLine(a.ToString("n0"));

BigInteger x1 = new BigInteger(87974894654657);
BigInteger y1 = new BigInteger(76216354532345);
BigInteger a1 = x1 * y1;
Console.WriteLine(a1.ToString("n0"));

输出:

6.705.125.760.945.040.000.000.000.000
6.705.125.760.945.040.955.511.380.665

有没有办法让double 正确计算结果? BigIntegers 工作正常,但比浮点类型慢得多。我使用的是 double 而不是 long,因为我正在处理非常大的数字。

【问题讨论】:

  • 只有 2^53 以内的整数才能准确地存储在双精度数中。
  • 双精度为16 位。因此,随着数字变大,您将失去数字。 有没有办法让 double 正确计算结果? 没有。没有办法使用 single double,因为它是 64 位,你不能存储比这更多的数据。 BigIntegers work fine, but are much slower 你想要精确。你必须付钱。

标签: c# double precision biginteger


【解决方案1】:

对于您正在寻找的输出(带有“非常大的数字”),您将被限制使用 BigInteger 或小数类型。

double x = 87974894654657d;
double y = 76216354532345d;
double a = x * y;
a.Dump();


decimal xd = 87974894654657m;
decimal yd = 76216354532345m;
decimal b = xd * yd;
b.Dump();

输出:

6.70512576094504E+27
6705125760945040955511380665

【讨论】:

  • 超大整数只能由 BigIntegers 处理。小数只有 96 位精度,这比通常硬件支持的浮点类型可以处理的要多,但它并不是非常大,IMO。在这里,小数确实够大了。
  • @RudyVelthuis 同意——问题中没有定义“非常大”的含义,但随着年龄的增长似乎越来越大:-)
【解决方案2】:

试试这个代码:

        double d = 6705125760945040955511380665d;
        Console.WriteLine(d.ToString("n0"));

输出将是6.705.125.760.945.040.000.000.000.000。 double 可以表示非常大的值,但它不能精确地做到这一点。那是因为它使用了一个指数,有点像加 10^n。如果您使用小数而不是二进制数并使用 4 位数字,您可以像这样存储数字 5000000:05|06 => 5*10^6。您可以通过这种方式存储的最大数字是99|99 => 99 * 10 ^ 99,这是一个巨大的数字。但你无法准确存储数字 123456,只能近似:12|04 => 120000

如果你想要精确,不要使用像floatdouble这样的浮点数,而是使用decimalBigInteger

【讨论】:

  • 这不是因为它使用了指数,而是因为它有一个固定的有限位数来存储数字。在描述您可以存储的值的范围时,指数会起作用,但精确位数受类型中位数的限制。 BigInteger 和类似类型可能有无限数量的位可供使用,但会以性能为代价。
  • 为了澄清我的意思,指数是它可以在少量(固定)位中存储大量数字的原因,但以这种方式存储的数字并不总是准确的。
  • 其实decimal也是一个浮点数(96位尾数,8位指数),唯一的区别是指数实际上是十进制而不是二进制,避免了例如0.1double。它仍然只能精确“仅”高达 96 位(~28.9 位)。
【解决方案3】:

刚刚执行

     Console.WriteLine(DateTime.Now.Second+" "+DateTime.Now.Millisecond+"");
     BigInteger x1 = new BigInteger(87974894654657);
   BigInteger y1 = new BigInteger(76216354532345);
   BigInteger a1 = x1 * y1;
  Console.WriteLine(a1.ToString("n0"));
    Console.WriteLine(DateTime.Now.Second+" "+DateTime.Now.Millisecond+"");

    Console.WriteLine(DateTime.Now.Second+" "+DateTime.Now.Millisecond+"");
    double x = 87974894654657d;
  double y = 76216354532345d;
  double a = x * y;
   Console.WriteLine(a.ToString("n0"));
    Console.WriteLine(DateTime.Now.Second+" "+DateTime.Now.Millisecond+"");

得到了这个结果:

4 216

6,705,125,760,945,040,955,511,380,665

4 216

4 216

6,705,125,760,945,040,000,000,000,000

4 216

甚至没有 1 毫秒的延迟。

【讨论】:

    【解决方案4】:

    看起来十进制类型比BigInteger类型快一点。

            Stopwatch stopwatch = new Stopwatch();
    
            stopwatch.Restart();
            double x = 87974894654657d;
            double y = 76216354532345d;
            double a = x * y;
            stopwatch.Stop();
            Console.WriteLine(a.ToString("n0") + " (" + stopwatch.Elapsed.TotalMilliseconds.ToString("0.000") + "ms)");
    
            stopwatch.Restart();
            BigInteger x1 = new BigInteger(87974894654657);
            BigInteger y1 = new BigInteger(76216354532345);
            BigInteger a1 = x1 * y1;
            stopwatch.Stop();
            Console.WriteLine(a1.ToString("n0") + " (" + stopwatch.Elapsed.TotalMilliseconds.ToString("0.000") + "ms)");
    
            stopwatch.Restart();
            decimal x2 = 87974894654657M;
            decimal y2 = 76216354532345M;
            decimal a2 = x2 * y2;
            stopwatch.Stop();
            Console.WriteLine(a2.ToString("n0") + " (" + stopwatch.Elapsed.TotalMilliseconds.ToString("0.000") + "ms)");
    

    这里是输出:

    6.705.125.760.945.040.000.000.000.000 (0,002ms)

    6.705.125.760.945.040.955.511.380.665 (0,044ms)

    6.705.125.760.945.040.955.511.380.665 (0,030ms)

    更新:

    通过 1000 万次迭代,数据类型之间的性能更加明显:

    双倍:6.705.125.760.945.040.000.000.000.000(24,558 毫秒)

    大整数:6.705.125.760.945.040.955.511.380.665 (1623,420ms)

    十进制:6.705.125.760.945.040.955.511.380.665 (478,333ms)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多