8 votes

Diviser un BigInteger pour obtenir un double

Je veux calculer la pente d'une ligne.

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

Je sais que BigInteger possède une fonction DivRem qui renvoie le résultat de la division plus le reste, mais je ne sais pas comment l'appliquer pour obtenir un double. Les nombres que j'utilise sont les suivants loin loin au-delà de la plage de Int64.MaxValue, de sorte que le reste lui-même pourrait être hors de portée pour être calculé par division conventionnelle.

EDIT : Je ne sais pas si cela peut vous aider, mais je n'utilise que des nombres entiers positifs (>=1).

IMPORTANT : Je n'ai besoin que de quelques décimales de précision (5 devrait suffire pour mon objectif).

6voto

Nicholas Carey Points 24614

Obtenir BigRational de Codeplex. Il fait partie du programme de Microsoft Bibliothèque des classes de base Il s'agit donc d'un travail en cours pour .Net. Une fois que vous avez cela, faites quelque chose comme :

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

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

La gestion des inévitables débordements, sous-débordements et pertes de précision constitue bien entendu un autre problème.

Puisque vous ne pouvez pas intégrer la bibliothèque BigRational dans votre code, il est évident que l'autre approche consisterait à sortir la librairie livre sur les algorithmes droits et de rouler votre propre...

La façon la plus simple, bien sûr, de "se débrouiller" ici, puisqu'un nombre rationnel est représenté comme le rapport (division) de deux entiers, est de prendre l'opérateur explicite de conversion en double de la classe BigRational et de l'adapter à la situation. Cela m'a pris environ 15 minutes.

La seule modification importante que j'ai apportée concerne la manière dont le signe du résultat est défini lorsque le résultat est positif ou négatif (zéro/infini). Pendant que j'y étais, je l'ai converti en un BigInteger pour vous :

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

}

5voto

Random832 Points 9199

Les BigRational dispose d'un opérateur de conversion en double.

N'oubliez pas non plus de renvoyer l'infini comme cas spécial pour une ligne verticale, vous obtiendrez une exception de division par zéro avec votre code actuel. Il est probablement préférable de calculer X1 - X2 d'abord, et de retourner l'infini si c'est zéro, puis de faire la division, pour éviter les opérations redondantes.

1voto

Blam Points 17325

Il ne s'agit pas d'une réponse négative, mais d'un bon point de départ.

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X