【问题标题】:Dividing a BigIntegers to return double将 BigIntegers 相除以返回双精度
【发布时间】:2012-08-08 20:04:51
【问题描述】:

我想计算一条线的斜率。

public sealed class Point
{
    public System.Numerics.BigInteger x = 0;
    public System.Numerics.BigInteger y = 0;

    public double CalculateSlope (Point point)
    {
        return ((point.Y - this.Y) / (point.X - this.X));
    }
}

我知道 BigInteger 有一个 DivRem 函数,它返回除法结果加上余数,但不知道如何应用它来获得双精度数。我正在处理的数字远远超出 Int64.MaxValue 的范围,因此余数本身可能超出常规除法计算的范围。

编辑: 不确定它是否有帮助,但我只处理正整数 (>=1)。

重要提示:我只需要几个小数点的精度(5 应该足以满足我的目的)。

【问题讨论】:

  • 你需要它有多精确?
  • 你正在处理的数字是什么......请显示数字的性质
  • @AustinSalonen:精度越高越好,但即使是几个十进制数字也足以达到我想要的位置。
  • @DJKRAZE:我正在处理数以百万计的基数为 10 位的数字。
  • 也许这会有所帮助? stackoverflow.com/a/11859314/310001

标签: c# .net biginteger division


【解决方案1】:

从 Codeplex 获取 BigRational。它是 Microsoft 的 Base Class Library 的一部分,因此它是 .Net 的一个正在进行中的工作。完成后,请执行以下操作:

System.Numerics.BigInteger x = GetDividend() ;
System.Numerics.BigInteger y = GetDivisor() ;

BigRational r     = new BigRational( x , y ) ;
double      value = (double) r ;

处理不可避免的上溢/下溢/精度损失当然是另一个问题。

由于您不能将 BigRational 库放入您的代码中,显然,另一种方法是使用 right algorithms book 并自行开发...

当然,在这里“滚动自己”的简单方法是,因为有理数表示为两个整数的比率(除法),所以从 BigRational 类中获取显式转换为双精度运算符并对其进行调整适合。我花了大约 15 分钟。

关于我所做的唯一重大修改是当结果为正或负零/无穷大时如何设置结果的符号。当我在做的时候,我为你将它转换为BigInteger 扩展方法:

public static class BigIntExtensions
{

  public static double DivideAndReturnDouble( this BigInteger x , BigInteger y )
  {
    // The Double value type represents a double-precision 64-bit number with
    // values ranging from -1.79769313486232e308 to +1.79769313486232e308
    // values that do not fit into this range are returned as +/-Infinity
    if (SafeCastToDouble(x) && SafeCastToDouble(y))
    {
      return (Double) x / (Double)  y;
    }

    // kick it old-school and figure out the sign of the result
    bool isNegativeResult = ( ( x.Sign < 0 && y.Sign > 0 ) || ( x.Sign > 0 && y.Sign < 0 ) ) ;

    // scale the numerator to preseve the fraction part through the integer division
    BigInteger denormalized = (x * s_bnDoublePrecision) / y ;
    if ( denormalized.IsZero )
    {
      return isNegativeResult ? BitConverter.Int64BitsToDouble(unchecked((long)0x8000000000000000)) : 0d; // underflow to -+0
    }

    Double result   = 0              ;
    bool   isDouble = false          ;
    int    scale    = DoubleMaxScale ;

    while ( scale > 0 )
    {
      if (!isDouble)
      {
        if ( SafeCastToDouble(denormalized) )
        {
          result = (Double) denormalized;
          isDouble = true;
        }
        else
        {
          denormalized = denormalized / 10 ;
        }
      }
      result = result / 10 ;
      scale-- ;
    }

    if (!isDouble)
    {
      return isNegativeResult ? Double.NegativeInfinity : Double.PositiveInfinity;
    }
    else
    {
      return result;
    }

  }

  private const           int        DoubleMaxScale      = 308 ;
  private static readonly BigInteger s_bnDoublePrecision = BigInteger.Pow( 10 , DoubleMaxScale ) ;
  private static readonly BigInteger s_bnDoubleMaxValue  = (BigInteger) Double.MaxValue;
  private static readonly BigInteger s_bnDoubleMinValue  = (BigInteger) Double.MinValue;

  private static bool SafeCastToDouble(BigInteger value)
  {
    return s_bnDoubleMinValue <= value && value <= s_bnDoubleMaxValue;
  }

}

【讨论】:

  • 谢谢。不幸的是,我不能在生产代码中使用 BigRational,因为它处于测试阶段。论坛和书籍应该是答案。
  • @RaheelKhan:或者您可以从 BigRational 库中去掉显式转换运算符并根据需要对其进行修改。我花了大约 15 分钟(见我编辑的答案)。
  • 谢谢。考虑到其他选择,这可能是唯一的出路。在标记正确答案之前,我会花一些时间来消化和测试。
【解决方案2】:

BigRational 库有一个用于加倍的转换运算符。

另外,请记住返回无穷大作为垂直线的特殊情况,您当前的代码会出现除以零的异常。可能最好先计算 X1 - X2,如果为零则返回无穷大,然后进行除法,以避免冗余操作。

【讨论】:

  • 有趣。 .NET 4 中不存在此类吗?如果它是测试版或候选发布版,那么很遗憾我不能在生产代码中使用它。
  • 这是一个测试版。为什么不能用?
  • 这是一个生产应用程序。质量保证不允许使用 beta 库。
【解决方案3】:

这不涉及负面,但希望能给你一个开始。

        double doubleMax = double.MaxValue;
        BigInteger numerator = 120;
        BigInteger denominator = 50;        
        if (denominator != 0)
        {
            Debug.WriteLine(numerator / denominator);
            Debug.WriteLine(numerator % denominator);
            BigInteger ansI = numerator / denominator;
            if (ansI < (int)doubleMax)
            {
                double slope = (double)ansI + ((double)(numerator % denominator) / (double)denominator); ;
                Debug.WriteLine(slope);
            }
        }

【讨论】:

  • 谢谢。但是,这仅在积分结果在 Int32 或 UInt64 之内时才有效。考虑将 N 除以 3,其中 N 有 10,000,000 位。
  • @RaheelKhan 没错。阅读问题 public double CalculateSlope。如果答案大于 double,则不能将其强制转换为 double。
  • 当然。斜率本身将始终介于 -1.0 和 1.0 之间。精确度在很大程度上丧失了。
  • @RaheelKhan 没有什么可以限制坡度。 1 / 0 = 无穷大。
  • 坡度1/00/1 是可以在部门之外处理的罕见极端情况。这就是为什么我指定了可接受的斜率范围,即 -1.0 到 1.0。
猜你喜欢
  • 1970-01-01
  • 2013-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-14
相关资源
最近更新 更多