public static void DblToRgbPrecise(double dbl, byte[] mantissa, out int exponent, out int mantissaSize) {
BigInteger biNum = new BigInteger();
BigInteger biDen = new BigInteger();
BigInteger biHi = new BigInteger();
BigInteger biLo = new BigInteger();
BigInteger biT = new BigInteger();
BigInteger biHiLo;
byte bT;
bool fPow2;
int ib, cu;
int wExp10, wExp2, w1, w2;
int c2Num, c2Den, c5Num, c5Den;
double dblT;
//uint *rgu = stackalloc uint[2];
uint rgu0, rgu1;
uint dblHi, dblLo;
dblHi = DblHi(dbl);
dblLo = DblLo(dbl);
// Caller should take care of 0, negative and non-finite values.
Debug.Assert(!IsSpecial(dbl));
Debug.Assert(0 < dbl);
// Init the Denominator, Hi error and Lo error bigints.
biDen.InitFromDigits(1, 0, 1);
biHi.InitFromDigits(1, 0, 1);
wExp2 = (int)(((dblHi & 0x7FF00000) >> 20) - 1075);
rgu1 = dblHi & 0x000FFFFF;
rgu0 = dblLo;
cu = 2;
fPow2 = false;
if (wExp2 == -1075) {
// dbl is denormalized.
Debug.Assert(0 == (dblHi & 0x7FF00000));
if (0 == rgu1) {
cu = 1;
}
// Get dblT such that dbl / dblT is a power of 2 and 1 <= dblT < 2.
// First multiply by a power of 2 to get a normalized value.
dblT = BitConverter.Int64BitsToDouble(0x4FF00000L << 32);
dblT *= dbl;
Debug.Assert(0 != (DblHi(dblT) & 0x7FF00000));
// This is the power of 2.
w1 = (int)((DblHi(dblT) & 0x7FF00000) >> 20) - (256 + 1023);
dblHi = DblHi(dblT);
dblHi &= 0x000FFFFF;
dblHi |= 0x3FF00000;
dblT = BitConverter.Int64BitsToDouble((long)dblHi << 32 | DblLo(dblT));
// Adjust wExp2 because we don't have the implicit bit.
wExp2++;
} else {
// Get dblT such that dbl / dblT is a power of 2 and 1 <= dblT < 2.
// First multiply by a power of 2 to get a normalized value.
dblHi &= 0x000FFFFF;
dblHi |= 0x3FF00000;
dblT = BitConverter.Int64BitsToDouble((long)dblHi << 32 | dblLo);
// This is the power of 2.
w1 = wExp2 + 52;
if (0 == rgu0 && 0 == rgu1 && wExp2 > -1074) {
// Power of 2 bigger than smallest normal. The next smaller
// representable value is closer than the next larger value.
rgu1 = 0x00200000;
wExp2--;
fPow2 = true;
} else {
// Normalized and not a power of 2 or the smallest normal. The
// representable values on either side are the same distance away.
rgu1 |= 0x00100000;
}
}
// Compute an approximation to the base 10 log. This is borrowed from
// David Gay's paper.
Debug.Assert(1 <= dblT && dblT < 2);
dblT = (dblT - 1.5) * 0.289529654602168 + 0.1760912590558 +
w1 * 0.301029995663981;
wExp10 = (int)dblT;
if (dblT < 0 && dblT != wExp10) {
wExp10--;
}
if (wExp2 >= 0) {
c2Num = wExp2;
c2Den = 0;
} else {
c2Num = 0;
c2Den = -wExp2;
}
if (wExp10 >= 0) {
c5Num = 0;
c5Den = wExp10;
c2Den += wExp10;
} else {
c2Num -= wExp10;
c5Num = -wExp10;
c5Den = 0;
}
if (c2Num > 0 && c2Den > 0) {
w1 = c2Num < c2Den ? c2Num : c2Den;
c2Num -= w1;
c2Den -= w1;
}
// We need a bit for the Hi and Lo values.
c2Num++;
c2Den++;
// Initialize biNum and multiply by powers of 5.
if (c5Num > 0) {
Debug.Assert(0 == c5Den);
biHi.MulPow5(c5Num);
biNum.InitFromBigint(biHi);
if (1 == cu) {
biNum.MulAdd(rgu0, 0);
} else {
biNum.MulAdd(rgu1, 0);
biNum.ShiftLeft(32);
if (0 != rgu0) {
biT.InitFromBigint(biHi);
biT.MulAdd(rgu0, 0);
biNum.Add(biT);
}
}
} else {
Debug.Assert(cu <= 2);
biNum.InitFromDigits(rgu0, rgu1, cu);
if (c5Den > 0) {
biDen.MulPow5(c5Den);
}
}
// BigInteger.DivRem only works if the 4 high bits of the divisor are 0.
// It works most efficiently if there are exactly 4 zero high bits.
// Adjust c2Den and c2Num to guarantee this.
w1 = CbitZeroLeft(biDen[biDen.Length - 1]);
w1 = (w1 + 28 - c2Den) & 0x1F;
c2Num += w1;
c2Den += w1;
// Multiply by powers of 2.
Debug.Assert(c2Num > 0 && c2Den > 0);
biNum.ShiftLeft(c2Num);
if (c2Num > 1) {
biHi.ShiftLeft(c2Num - 1);
}
biDen.ShiftLeft(c2Den);
Debug.Assert(0 == (biDen[biDen.Length - 1] & 0xF0000000));
Debug.Assert(0 != (biDen[biDen.Length - 1] & 0x08000000));
// Get biHiLo and handle the power of 2 case where biHi needs to be doubled.
if (fPow2) {
biHiLo = biLo;
biHiLo.InitFromBigint(biHi);
biHi.ShiftLeft(1);
} else {
biHiLo = biHi;
}
for (ib = 0; ; ) {
bT = (byte)biNum.DivRem(biDen);
if (0 == ib && 0 == bT) {
// Our estimate of wExp10 was too big. Oh well.
wExp10--;
goto LSkip;
}
// w1 = sign(biNum - biHiLo).
w1 = biNum.CompareTo(biHiLo);
// w2 = sign(biNum + biHi - biDen).
if (biDen.CompareTo(biHi) < 0) {
w2 = 1;
} else {
biT.InitFromBigint(biDen);
biT.Subtract(biHi);
w2 = biNum.CompareTo(biT);
}
// if (biNum + biHi == biDen && even)
if (0 == w2 && 0 == (dblLo & 1)) {
// Rounding up this digit produces exactly (biNum + biHi) which
// StrToDbl will round down to dbl.
if (bT == 9) {
goto LRoundUp9;
}
if (w1 > 0) {
bT++;
}
mantissa[ib++] = bT;
break;
}
// if (biNum < biHiLo || biNum == biHiLo && even)
if (w1 < 0 || 0 == w1 && 0 == (dblLo & 1)) {
// if (biNum + biHi > biDen)
if (w2 > 0) {
// Decide whether to round up.
biNum.ShiftLeft(1);
w2 = biNum.CompareTo(biDen);
if ((w2 > 0 || 0 == w2 && (0 != (bT & 1))) && bT++ == 9) {
goto LRoundUp9;
}
}
mantissa[ib++] = bT;
break;
}
// if (biNum + biHi > biDen)
if (w2 > 0) {
// Round up and be done with it.
if (bT != 9) {
mantissa[ib++] = (byte)(bT + 1);
break;
}
goto LRoundUp9;
}
// Save the digit.
mantissa[ib++] = bT;
LSkip:
biNum.MulAdd(10, 0);
biHi.MulAdd(10, 0);
if ((object) biHiLo != (object) biHi) {
biHiLo.MulAdd(10, 0);
}
continue;
LRoundUp9:
while (ib > 0) {
if (mantissa[--ib] != 9) {
mantissa[ib++]++;
goto LReturn;
}
}
wExp10++;
mantissa[ib++] = 1;
break;
}
LReturn:
exponent = wExp10 + 1;
mantissaSize = ib;
}