public static double GetDoubleFromParts(int sign, int exp, ulong man) { DoubleUlong du; du.dbl = 0; if (man == 0) { du.uu = 0; } else { // Normalize so that 0x0010 0000 0000 0000 is the highest bit set. int cbitShift = BitOperations.LeadingZeroCount(man) - 11; if (cbitShift < 0) { man >>= -cbitShift; } else { man <<= cbitShift; } exp -= cbitShift; Debug.Assert((man & 0xFFF0000000000000) == 0x0010000000000000); // Move the point to just behind the leading 1: 0x001.0 0000 0000 0000 // (52 bits) and skew the exponent (by 0x3FF == 1023). exp += 1075; if (exp >= 0x7FF) { // Infinity. du.uu = 0x7FF0000000000000; } else if (exp <= 0) { // Denormalized. exp--; if (exp < -52) { // Underflow to zero. du.uu = 0; } else { du.uu = man >> -exp; Debug.Assert(du.uu != 0); } } else { // Mask off the implicit high bit. du.uu = (man & 0x000FFFFFFFFFFFFF) | ((ulong)exp << 52); } } if (sign < 0) { du.uu |= 0x8000000000000000; } return(du.dbl); }
private static void Divide(Span <uint> left, ReadOnlySpan <uint> right, Span <uint> bits) { Debug.Assert(left.Length >= 1); Debug.Assert(right.Length >= 1); Debug.Assert(left.Length >= right.Length); Debug.Assert(bits.Length == left.Length - right.Length + 1 || bits.Length == 0); // Executes the "grammar-school" algorithm for computing q = a / b. // Before calculating q_i, we get more bits into the highest bit // block of the divisor. Thus, guessing digits of the quotient // will be more precise. Additionally we'll get r = a % b. uint divHi = right[right.Length - 1]; uint divLo = right.Length > 1 ? right[right.Length - 2] : 0; // We measure the leading zeros of the divisor int shift = BitOperations.LeadingZeroCount(divHi); int backShift = 32 - shift; // And, we make sure the most significant bit is set if (shift > 0) { uint divNx = right.Length > 2 ? right[right.Length - 3] : 0; divHi = (divHi << shift) | (divLo >> backShift); divLo = (divLo << shift) | (divNx >> backShift); } // Then, we divide all of the bits as we would do it using // pen and paper: guessing the next digit, subtracting, ... for (int i = left.Length; i >= right.Length; i--) { int n = i - right.Length; uint t = (uint)i < (uint)left.Length ? left[i] : 0; ulong valHi = ((ulong)t << 32) | left[i - 1]; uint valLo = i > 1 ? left[i - 2] : 0; // We shifted the divisor, we shift the dividend too if (shift > 0) { uint valNx = i > 2 ? left[i - 3] : 0; valHi = (valHi << shift) | (valLo >> backShift); valLo = (valLo << shift) | (valNx >> backShift); } // First guess for the current digit of the quotient, // which naturally must have only 32 bits... ulong digit = valHi / divHi; if (digit > 0xFFFFFFFF) { digit = 0xFFFFFFFF; } // Our first guess may be a little bit to big while (DivideGuessTooBig(digit, valHi, valLo, divHi, divLo)) { --digit; } if (digit > 0) { // Now it's time to subtract our current quotient uint carry = SubtractDivisor(left.Slice(n), right, digit); if (carry != t) { Debug.Assert(carry == t + 1); // Our guess was still exactly one too high carry = AddDivisor(left.Slice(n), right); --digit; Debug.Assert(carry == 1); } } // We have the digit! if ((uint)n < (uint)bits.Length) { bits[n] = (uint)digit; } if ((uint)i < (uint)left.Length) { left[i] = 0; } } }