【问题标题】:BigInteger division in C#C# 中的 BigInteger 除法
【发布时间】:2012-08-08 06:50:30
【问题描述】:

我正在编写一个需要在 C# 中准确划分 BigInteger 类的类。

例子:

BigInteger x = BigInteger.Parse("1000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
BigInteger y = BigInteger.Parse("2000000000000000000000000000000000000000000000000000000000000000000000000000000000000");

x /= y;

Console.WriteLine(x.ToString());

//Output = 0

问题是作为一个整数,它自然不保存十进制值。 我怎样才能克服这个以获得 0.5 的实际结果(给定示例)。

附:解决方案必须能够准确地划分任何 BigInteger,而不仅仅是示例!

【问题讨论】:

  • 结果中需要多少位数?一般来说,当你将两个数字相除时,比如10/7,数学结果有无限个小数。这很难在计算机中表示。
  • 注意:这可能接近我正在寻找的答案:stackoverflow.com/questions/2863388/…
  • 对于太多小数,请使用 Math.Round(result,2) 例如2位数
  • @AVD 示例中的数字不适合现有的System.Decimal,因此您必须提供详细信息。
  • 我知道这不是一个完全正统的想法......但我可以从技术上将 Java 的 BigDecimal 类移植到 C# 并使用它吗?

标签: c# .net biginteger division


【解决方案1】:

在上面的例子中,数字仍然足够小,可以转换为double,所以在这种情况下你可以说

double result = (double)x / (double)y;

如果xy 对于double 来说太大但仍然具有可比性,也许这个绝妙的技巧会有所帮助:

double result = Math.Exp(BigInteger.Log(x) - BigInteger.Log(y));

但是一般来说,当BigInteger很大,商也很大的时候,不导入第三方库是很难做到的。

【讨论】:

  • 这似乎是我能找到的最接近的东西。它正在我的实现中工作!
  • 当然会丢失很多精度。但最后一种方法在某些情况下有效。就像x 有 1005 位和 y 有 1000 位一样,两者都不能转换为 double,但 result 仍然可以(小数点前大约有 5 位)。但是,如果 x 有 2007 位,y 有 1000 位,则 result 将是无穷大。不会抛出异常。 注意:如果xy可能为非正数,则必须分别取绝对值,最后处理符号。
【解决方案2】:

除法需要什么精度?一种方法是:

  • 将分子乘以 1000
  • 除数
  • 将结果转换为double并除以1000

代码相同:

BigInteger x = BigInteger.Parse("1000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
BigInteger y = BigInteger.Parse("2000000000000000000000000000000000000000000000000000000000000000000000000000000000000");

x *= 1000;
x /= y;
double result = (double)x;
result /= 1000;
Console.WriteLine(result);

【讨论】:

  • 另一种写法:double result = (double)(1000 * x / y) / 1000.0;(但不会重新分配x)。 result 可能变为PositiveInfinityNegativeInfinity
【解决方案3】:

如果您需要保持完全精确,请使用有理数的实现(Java 等效项是 Apache Commons Math 库中的 Fraction 类)。潜伏着各种实现,但 .NET 4.0 最轻量级的解决方案(因为它内置了 System.Numerics.BigInteger)如下:

        System.Numerics.BigInteger x = System.Numerics.BigInteger.Parse("10000000000000000000000000000000000000000000000000000");

        System.Numerics.BigInteger y = System.Numerics.BigInteger.Parse("20000000000000000000000000000000000000000000000000000");

        // From BigRationalLibrary
        Numerics.BigRational r = new Numerics.BigRational(x,y);

        Console.Out.WriteLine(r.ToString());
        // outputs "1/2", but can be converted to floating point if needed.

要使其工作,您需要来自 .Net 4.0 System.Numerics.dll 的 System.Numberics.BigInteger 和来自 CodePlex 的 BigRational 实现。

Microsoft Solver Foundation 3.0 中也实现了Rational structure。在撰写本文时,www.solverfoundation.com 网站已损坏,因此有一个指向存档的链接。

【讨论】:

  • 好点,感谢指出,错过了那个小细节。
  • 它提出了一个很好的观点!为什么 C# 没有这些类!? DOH!
  • 我编辑了我的答案以反映语言上的“轻微”差异。似乎 C# 也在朝着正确的方向酝酿着一些事情!
【解决方案4】:

您可能知道整数除法不会产生十进制值,因此您的结果将被截断为 0。根据 this question 可以找到大双精度实现 here,但它的最后一个版本是在 2009 年。如果你看进一步你可能会发现更新的或者这个已经完成了。

【讨论】:

    【解决方案5】:

    听起来像是 Fixed Point 的工作(而不是浮点数)。

    只需将分子预移位所需的小数位数,如下所示:

    BigInteger quotient = (x << 10) / y;
    

    这会给你点后的 10 位(大约 3 个十进制数字)。

    【讨论】:

      【解决方案6】:
      //b = 10x bigger as a => fraction should be 0.1
      BigInteger a = BigInteger.Pow(10, 5000);
      BigInteger b = BigInteger.Pow(10, 5001);
      
      //before the division, multiple by a 1000 for a precision of 3, afterwards 
      //divide the result by this.
      var fraction = (double) BigInteger.Divide(a * 1000, b) / 1000;
      

      【讨论】:

        【解决方案7】:

        将其解析为双倍:

        double a = Convert.ToDouble(x);
        double b = Convert.ToDouble(y);
        
        Console.WriteLine(a / b);
        

        【讨论】:

        • 我使用了同样的想法。但是,我不确定Convert.ToDouble 是否可行,但这是一个细节。
        • BigInteger 没有实现 IConvertible :-( 没有这样的运气!
        猜你喜欢
        • 1970-01-01
        • 2016-04-17
        • 2016-12-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-02
        • 1970-01-01
        • 2020-10-11
        相关资源
        最近更新 更多