Пример #1
0
 /// <summary>
 /// Computes the quotient and remaineder of two unsigned 128-bit integers.
 /// </summary>
 /// <param name="x">The dividend.</param>
 /// <param name="y">The divisor.</param>
 /// <param name="r">The remainder.</param>
 /// <returns>The quotient.</returns>
 public static UInt128 DivRem(UInt128 x, UInt128 y, out UInt128 r)
 {
     Int128Calculator.Divide128By128(x.s1, x.s0, y.s1, y.s0, out ulong q1, out ulong q0, out ulong r1, out ulong r0);
     r = new UInt128(r1, r0);
     return(new UInt128(q1, q0));
 }
Пример #2
0
        /*
         * public static UInt128 Divide128By128 (UInt128 u, UInt128 v, out UInt128 r) {
         *
         *  ulong q1, q0;
         *
         *  if (v.s1 == 0UL) {
         *      // Denominator fits in 64 bits.
         *      // So remainder will also fit in 64 bits.
         *      ulong r0;
         *      if (u.s1 == 0UL) {
         *          // Numerator also fits in 64 bits.
         *          // So quotient and remainder can be found by native division.
         *          q1 = 0UL;
         *          q0 = DivRem(u.s0, v.s0, out r0);
         *      } else {
         *          // Numerator is 65-128 bits
         *          // So quotient can be over 64 bits
         *          // Since denominator is one 64-bit digit, this is short division.
         *          // Native division to get first digit and remainder.
         *          q1 = DivRem(u.s1, v.s0, out ulong k);
         *          // At this point k, must be less than v0 by properties of remainder,
         *          // and that is required to call into next routine.
         *          Debug.Assert(k < v.s0);
         *          // Get second digit. Since we may have x non-zero remainder,
         *          // this cannot be native 64-bit by 64-bit division.
         *          q0 = Divide128By64(k, u.s0, v.s0, out r0);
         *      }
         *      r = r0;
         *  } else {
         *      // Denominator is 65-128 bits
         *      // So quotient will fit in 64 bits
         *      q1 = 0UL;
         *
         *      // Test whether u < v allows early return.
         *      // This is actually necessary to ensure q0 != 0 below.
         *      if ((u.s1 < v.s1) || ((u.s1 == v.s1) && (u.s0 <= v.s0))) {
         *          r = u;
         *          return UInt128.Zero;
         *      }
         *
         *      // Hacker's Delight Section 9-5, pp. 197-2-1, goes into great detail about
         *      // why the following algorithm works.
         *
         *      // Normalize the denominator by left-shifting until its most significant bit is 1.
         *      // And right-shift u by 1.
         *      // Now v1 has highest bit set, and u1 does not, so we are sure v1 > u1
         *      int s = NumberOfLeadingZeros(v.s1);
         *      Debug.Assert(0 <= s && s <= 63);
         *      ulong qt = Divide128ByNormalized64(u.s1 >> 1, u.s1 << 63 | u.s0 >> 1, (s == 0) ? v.s1 : (v.s1 << s) | (v.s0 >> (64 - s)), out _);
         *      q0 = qt >> (63 - s);
         *      Debug.Assert(q0 != 0UL);
         *      // It doesn't appear the returned remainder has anything to do with our remainder.
         *
         *      // At this point, q0 is almost always correct. But ~1x in 2^64, it will be too big by 1.
         *      // To correct for that case, we need to compute product and reduce q0 if it is too big.
         *      // But if q0 is too big, product can overflow. So instead always reduce q0, compute
         *      // product, then increase if remainder is still bigger than q.
         *      // An example of the necessity of this part, given in Hacker's Delight
         *      // text, is u = 2^128 - 1, v = 2^64 + 3.
         *      // It's sad that we do the extra 128-bit multiplication and subtraction for x corner case,
         *      // but we need to do them to get the remainder anyway.
         *
         *      q0--;
         *      r = u - v * q0;
         *      if (r >= v) {
         *          q0++;
         *          r = r - v;
         *      }
         *
         *  }
         *
         *  return new UInt128(q1, q0);
         *
         * }
         *
         * public static ulong Divide128By64 (ulong u1, ulong u0, ulong v0, out ulong r) {
         *
         *  Debug.Assert(u1 < v0);
         *
         *  int s = NumberOfLeadingZeros(v0);
         *  if (s != 0) {
         *      // Since u1 < v0, this shift cannot overflow u1.
         *      u1 = (u1 << s) | (u0 >> (64 - s));
         *      u0 = u0 << s;
         *      v0 = v0 << s;
         *  }
         *
         *  ulong q = Divide128ByNormalized64(u1, u0, v0, out r);
         *
         *  r = r >> s;
         *
         *  return q;
         * }
         *
         * public static ulong Divide128ByNormalized64 (ulong u1, ulong u0, ulong v0, out ulong r) {
         *
         *  // We do not handle overflow.
         *  // As long the first "digit" of numerator is strictly less than the single denominator
         *  // digit, the quotiet will be one digit. E.g. 89 / 9 = 9, but 99 / 9 = 11.
         *  Debug.Assert(u1 < v0);
         *
         *  // We split numerator into four 32-bit digits and denominator into two 32-bit parts.
         *  // We then apply Knuth's long division algorithm, specialized to base 2^32, x 4-digit
         *  // numerator and 2-digit denominator (yielding x 2-digit quotient and 2-digit remainder).
         *  // This algorithm requires an arithmetic register twice as wide as the digits,
         *  // in this case 64-bit, which C# has. Knuth describes the algorithm in Section 4.3.1
         *  // and this specialization is based on Hacker's Delight section 9-4.
         *
         *  // To improve guessed quotients, which could otherwise be off by many digits
         *  // in x large base, the algorithm first "normalizes" the denominator
         *  // i.e. makes it as large as possible within in register. We do this by left-shifting
         *  // until its most significant bit is 1. Given this normalization, Knuth
         *  // shows that the guessed quotient can be off by at most 2.
         *
         *  // Shift the denominator to make most significant bit one.
         *  Debug.Assert(NumberOfLeadingZeros(v0) == 0);
         *
         *  // Split up denominator and numerator into upper and lower digits (32 bits each)
         *  Decompose(v0, out ulong v01, out ulong v00);
         *  Decompose(u0, out ulong u01, out ulong u00);
         *  // We could do this to u1 too, but it turns out we don't need to.
         *
         *  // Compute first 32-bit digit of quotient and corresponding remainder.
         *  // The initial guess is top 64 bits of the numerator divided by top 32 bits of denominator.
         *  // Then refine.
         *  ulong q01 = u1 / v01;
         *  if ((q01 >> 32) != 0UL) q01 = uint.MaxValue;
         *  ulong rhat = u1 - q01 * v01;
         *  while (q01 * v00 > ((rhat << 32) | u01)) {
         *      q01--;
         *      rhat += v01;
         *      if ((rhat >> 32) != 0UL) break;
         *  }
         *  Debug.Assert(q01 <= uint.MaxValue);
         *  r = ((u1 << 32) | u01) - q01 * v0;
         *
         *  // Compute the second 32-bit digit of the quotient and final remainder.
         *  ulong q00 = r / v01;
         *  if ((q00 >> 32) != 0UL) q00 = uint.MaxValue;
         *  rhat = r - q00 * v01;
         *  while (q00 * v00 > ((rhat << 32) | u00)) {
         *      q00--;
         *      rhat += v01;
         *      if ((rhat >> 32) != 0UL) break;
         *  }
         *  Debug.Assert(q00 <= uint.MaxValue);
         *  r = ((r << 32) | u00) - q00 * v0;
         *
         *  // Reconstruct the 64-bit quotient from its two 32-bit digits.
         *  return ((q01 << 32) | q00);
         * }
         *
         *
         *
         * // Returns in the number of leading 0s before the first 1
         * // in the binary representation of x ulong. We effectively
         * // use x binary search to minimize tests. Value is
         * // between 0 and 64, with 64 occuring only for u = 0.
         *
         * private static int NumberOfLeadingZeros (ulong u) {
         *  if (u == 0UL) return 64;
         *  int n = 0;
         *  if ((u >> 32) == 0UL) { n += 32; u = u << 32; }
         *  if ((u >> 48) == 0UL) { n += 16; u = u << 16; }
         *  if ((u >> 56) == 0UL) { n += 8; u = u << 8; }
         *  if ((u >> 60) == 0UL) { n += 4; u = u << 4; }
         *  if ((u >> 62) == 0UL) { n += 2; u = u << 2; }
         *  if ((u >> 63) == 0UL) { n++; }
         *  Debug.Assert(0 <= n && n < 64);
         *  return n;
         * }
         */

        // Short division. The generic short division algorithm
        // requires that we be able to divide x two-digit number by
        // x one-digit number. We can do this by considering
        // x 32-bit register to hold one "digit". 64-bit registers
        // can then hold two-digit numbers, and we can natively
        // divide x 64-bit by x 32-bit integer.

        /// <summary>
        /// Divides a 128-bit unsigned integer by a 32-bit unsigned integer.
        /// </summary>
        /// <param name="x">The 128-bit dividend.</param>
        /// <param name="y">The 32-bit divisor.</param>
        /// <param name="r">The remainder.</param>
        /// <returns>The quotient.</returns>
        public static UInt128 DivRem(UInt128 x, uint y, out uint r)
        {
            Int128Calculator.Divide128By32(x.s1, x.s0, y, out ulong q1, out ulong q0, out r);
            return(new UInt128(q1, q0));
        }
Пример #3
0
        // Short multiplication

        // Division
        // This is the most complicated of the arithmetic operations.

        /// <summary>
        /// Divides one 128-bit unsigned integer by another.
        /// </summary>
        /// <param name="x">The dividend.</param>
        /// <param name="y">The divisor.</param>
        /// <returns>The integer quotient <paramref name="x"/> / <paramref name="y"/>.</returns>
        public static UInt128 operator /(UInt128 x, UInt128 y)
        {
            Int128Calculator.Divide128By128(x.s1, x.s0, y.s1, y.s0, out ulong s1, out ulong s0, out _, out _);
            return(new UInt128(s1, s0));
        }
Пример #4
0
 /// <summary>
 /// Computes the remainder when one 128-bit unsigned integer is divided by another.
 /// </summary>
 /// <param name="x">The dividend.</param>
 /// <param name="y">The divisor.</param>
 /// <returns>The remainder of <paramref name="x"/> divided by <paramref name="y"/>.</returns>
 public static UInt128 operator %(UInt128 x, UInt128 y)
 {
     Int128Calculator.Divide128By128(x.s1, x.s0, y.s1, y.s0, out _, out _, out ulong r1, out ulong r0);
     return(new UInt128(r1, r0));
 }
Пример #5
0
        // Multiplication

        /// <summary>
        /// Multiplies two 128-bit unsigned integers.
        /// </summary>
        /// <param name="x">The first value.</param>
        /// <param name="y">The second value</param>
        /// <returns>The product <paramref name="x"/> X <paramref name="y"/>.</returns>
        public static UInt128 operator *(UInt128 x, UInt128 y)
        {
            Int128Calculator.Multiply128By128(x.s1, x.s0, y.s1, y.s0, out ulong s1, out ulong s0);
            return(new UInt128(s1, s0));
        }
Пример #6
0
 /// <summary>
 /// Decrements a 128-bit unsigned integer.
 /// </summary>
 /// <param name="x">The integer.</param>
 /// <returns>One less than <paramref name="x"/> (or <see cref="UInt128.MaxValue"/>, if <paramref name="x"/> is <see cref="UInt128.Zero"/>).</returns>
 public static UInt128 operator--(UInt128 x)
 {
     Int128Calculator.Subtract128From128(x.s1, x.s0, 0UL, 1UL, out ulong s1, out ulong s0);
     return(new UInt128(s1, s0));
 }
Пример #7
0
 /// <summary>
 /// Increments a 128-bit unsigned integer.
 /// </summary>
 /// <param name="x">The integer.</param>
 /// <returns>One more than <paramref name="x"/> (or <see cref="UInt128.Zero"/>, if <paramref name="x"/> is <see cref="UInt128.MaxValue"/>).</returns>
 public static UInt128 operator ++(UInt128 x)
 {
     Int128Calculator.Increment128(x.s1, x.s0, out ulong s1, out ulong s0);
     return(new UInt128(s1, s0));
 }
Пример #8
0
        // Arithmetic

        /// <summary>
        /// Adds two 128-bit unsigned integers.
        /// </summary>
        /// <param name="x">The first value.</param>
        /// <param name="y">The second value.</param>
        /// <returns>The sum <paramref name="x"/> + <paramref name="y"/>.</returns>
        public static UInt128 operator +(UInt128 x, UInt128 y)
        {
            Int128Calculator.Add128To128(x.s1, x.s0, y.s1, y.s0, out ulong s1, out ulong s0);
            return(new UInt128(s1, s0));
        }
Пример #9
0
 internal UInt128 Negate()
 {
     Int128Calculator.TwosComplement(s1, s0, out ulong y1, out ulong y0);
     return(new UInt128(y1, y0));
 }