9

I want to calculate the slope of a line.

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

I know that BigInteger has a DivRem function that returns the division result plus the remainder but am not sure how to apply it to get a double. The numbers I'm dealing with are far far beyond the range of Int64.MaxValue so the remainder itself could be out of range to calculate by conventional division.

EDIT: Not sure if it helps but I'm dealing with only positive integers (>=1).

IMPORTANT: I only need a few decimal points of precision (5 should be good enough for my purpose).

Raheel Khan
  • 14,205
  • 13
  • 80
  • 168
  • How precise do you need it to be? – Austin Salonen Aug 08 '12 at 20:14
  • what are the numbers that you are dealing with..please display the nature of the numbers – MethodMan Aug 08 '12 at 20:16
  • @AustinSalonen: The higher the precision the better but even a couple of decimal digits of precision would be sufficient to get where I want. – Raheel Khan Aug 08 '12 at 20:20
  • @DJKRAZE: I am dealing with numbers with millions of base 10 digits. – Raheel Khan Aug 08 '12 at 20:22
  • would you be able to look at the Math.Sign() functionality..if so I can direct you to a link that might help – MethodMan Aug 08 '12 at 20:26
  • Checkout this site http://www.functionx.com/csharp2/topics/math3.htm I hope this helps get to what you are looking for. – MethodMan Aug 08 '12 at 20:30
  • 3
    Perhaps this could be of some help? http://stackoverflow.com/a/11859314/310001 – Julian Aug 08 '12 at 20:41
  • Can it be a fixed point number? You could have as big a precision as you want, with just the formula `(numerator << digitsAfterDot) / denominator`. – harold Aug 08 '12 at 20:45
  • @harold: Thanks. How can I Google this to better understand the concept. – Raheel Khan Aug 08 '12 at 20:54
  • @RaheelKhan search for "fixed point math", and this article is nice in my opinion: http://x86asm.net/articles/fixed-point-arithmetic-and-tricks/ – harold Aug 08 '12 at 20:56
  • Millions of digits? Note that if `deltaY` has 1,000,500 digits, and `deltaX` has 1,000,000 digits, then the quotient `deltaY / deltaX` will have approx. 500 digits, and as so is not representable in a `double` (the greatest finite `double` having 309 decimal digits before the decimal point). – Jeppe Stig Nielsen Aug 08 '12 at 21:00
  • @JeppeStigNielsen: Exactly. I just need to calculate up to a few decimal digits (maybe 5) and that will be good enough for my algorithm. Not sure how to approach this. – Raheel Khan Aug 08 '12 at 21:03
  • @RaheelKhan Don't you want to (also) know the total number of digits in the slope? Knowing the first 5 digits is hardly useful if you don't know the magnitude of the number (depending on context)? – Jeppe Stig Nielsen Aug 08 '12 at 21:08
  • @JeppeStigNielsen: I understand what you mean but in my case the first 5 digits will tell how close I am to multiples of 45 degrees. That amount of precision if enough for the scenario I am dealing with. The specifics of the context are too complex to describe here so I have posed slope as a sub-objective. – Raheel Khan Aug 08 '12 at 21:56
  • @RaheelKhan But is it not important if your have `1.0023` or `10023??????????????` where `?` is an unknown digit before the decimal point? – Jeppe Stig Nielsen Aug 09 '12 at 13:33

3 Answers3

7

Get BigRational from Codeplex. Its part of Microsoft's Base Class Library, so it's a work-in-progress for .Net. Once you have that, then do something like:

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

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

Dealing with the inevitable overflow/underflow/loss of precision is, of course, another problem.

Since you can't drop the BigRational library into your code, evidently, the other approach would be to get out the right algorithms book and roll your own...

The easy way, of course, of "rolling one's own" here, since a rational number is represented as the ratio (division) of two integers, is to grab the explicit conversion to double operator from the BigRational class and tweak it to suit. It took me about 15 minutes.

About the only significant modification I made is in how the sign of the result is set when the result is positive or negative zero/infinity. While I was at it, I converted it to a BigInteger extension method for you:

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

}
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • Thanks. Unfortunately I cannot use BigRational in production code since it is in beta. Forums and books should be the answer. – Raheel Khan Aug 08 '12 at 21:05
  • 1
    @RaheelKhan: Or you could strip out the explicit conversion operator from the BigRational library and modify it as needed. It took me about 15 minutes (see my edited answer). – Nicholas Carey Aug 08 '12 at 23:34
  • Thank you. Given the alternatives, this is probably the only way to go. I'll take some time to digest this and test before marking the correct answer. – Raheel Khan Aug 09 '12 at 01:10
5

The BigRational library has a conversion operator to double.

Also, remember to return infinity as a special case for a vertical line, you'll get a divide by zero exception with your current code. Probably best to just calculate the X1 - X2 first, and return infinity if it's zero, then do the division, to avoid redundant operations.

Random832
  • 37,415
  • 3
  • 44
  • 63
1

This does not deal with negative but hopefully give you a start.

        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);
            }
        }
paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • Thanks. However, this will only work if the integral result is within Int32 or UInt64 for that matter. Think about dividing N by 3 where N has 10,000,000 digits. – Raheel Khan Aug 08 '12 at 20:35
  • @RaheelKhan Exactly. Read the question public double CalculateSlope. The answer cannot be cast to double if it is bigger than double. – paparazzo Aug 08 '12 at 20:41
  • Of course. The slope itself will always be between -1.0 and 1.0. It is the precision that is being lost in a big way. – Raheel Khan Aug 08 '12 at 20:43
  • 2
    @RaheelKhan There is nothing to constrain the slope. 1 / 0 = infinity. – paparazzo Aug 08 '12 at 20:46
  • Slope `1/0` and `0/1` are rare corner cases that can be handled outside of the division. That is why I specified the range of acceptable slopes i.e. -1.0 to 1.0. – Raheel Khan Aug 08 '12 at 21:58
  • Come on. No you did NOT specify -1.0 to 1.0 the the problem statement. -1.0 to 1.0 eliminates a full 1/2 the range. In another comment you specify you need to know how close to multiples 45. 45 is 1 so a multiple of 45 is over 1.0. Get your story straight. – paparazzo Aug 08 '12 at 23:55