public static bool DblToRgbFast(double dbl, byte[] mantissa, out int exponent, out int mantissaSize) {
BigNumber numHH, numHL, numLH, numLL;
BigNumber numBase;
BigNumber tenPower;
int ib, iT;
uint uT, uScale;
byte bHH, bHL, bLH, bLL;
uint uHH, uHL, uLH, uLL;
int wExp2, wExp10 = 0;
double dblInt;
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);
// Get numHH and numLL such that numLL < dbl < numHH and the
// difference between adjacent values is half the distance to the next
// representable value (in a double).
wExp2 = (int)((dblHi >> 20) & 0x07FF);
if (wExp2 > 0) {
// See if dbl is a small integer.
if (wExp2 >= 1023 && wExp2 <= 1075 && dbl == Math.Floor(dbl)) {
goto LSmallInt;
}
// Normalized
numBase.u2 = 0x80000000 | ((dblHi & 0x000FFFFFF) << 11) | (dblLo >> 21);
numBase.u1 = dblLo << 11;
numBase.u0 = 0;
numBase.exp = wExp2 - 1022;
numBase.error = 0;
// Get the upper bound
numHH = numBase;
numHH.u1 |= (1 << 10);
// Get the lower bound. A power of 2 must be special cased.
numLL = numBase;
if (0x80000000 == numLL.u2 && 0 == numLL.u1) {
// Subtract (0x00000000, 0x00000200, 0x00000000). Same as adding
// (0xFFFFFFFF, 0xFFFFFE00, 0x00000000)
uT = 0xFFFFFE00;
} else {
// Subtract (0x00000000, 0x00000400, 0x00000000). Same as adding
// (0xFFFFFFFF, 0xFFFFFC00, 0x00000000)
uT = 0xFFFFFC00;
}
if (0 == AddU(ref numLL.u1, uT)) {
AddU(ref numLL.u2, 0xFFFFFFFF);
if (0 == (0x80000000 & numLL.u2)) {
numLL.Normalize();
}
}
} else {
// Denormal
numBase.u2 = dblHi & 0x000FFFFF;
numBase.u1 = dblLo;
numBase.u0 = 0;
numBase.exp = -1010;
numBase.error = 0;
// Get the upper bound
numHH = numBase;
numHH.u0 = 0x80000000;
// Get the lower bound
numLL = numHH;
if (0 == AddU(ref numLL.u1, 0xFFFFFFFF)) {
AddU(ref numLL.u2, 0xFFFFFFFF);
}
numBase.Normalize();
numHH.Normalize();
numLL.Normalize();
}
// Multiply by powers of ten until 0 < numHH.exp < 32.
if (numHH.exp >= 32) {
iT = (numHH.exp - 25) * 15 / -TenPowersNeg[45].exp;
Debug.Assert(iT >= 0 && iT < 16);
if (iT > 0) {
tenPower = TenPowersNeg[30 + iT];
Debug.Assert(numHH.exp + tenPower.exp > 1);
numHH.Mul(ref tenPower);
numLL.Mul(ref tenPower);
wExp10 += iT * 32;
}
if (numHH.exp >= 32) {
iT = (numHH.exp - 25) * 32 / -TenPowersNeg[31].exp;
Debug.Assert(iT > 0 && iT <= 32);
tenPower = TenPowersNeg[iT - 1];
Debug.Assert(numHH.exp + tenPower.exp > 1);
numHH.Mul(ref tenPower);
numLL.Mul(ref tenPower);
wExp10 += iT;
}
} else if (numHH.exp < 1) {
iT = (25 - numHH.exp) * 15 / TenPowersPos[45].exp;
Debug.Assert(iT >= 0 && iT < 16);
if (iT > 0) {
tenPower = TenPowersPos[30 + iT];
Debug.Assert(numHH.exp + tenPower.exp <= 32);
numHH.Mul(ref tenPower);
numLL.Mul(ref tenPower);
wExp10 -= iT * 32;
}
if (numHH.exp < 1) {
iT = (25 - numHH.exp) * 32 / TenPowersPos[31].exp;
Debug.Assert(iT > 0 && iT <= 32);
tenPower = TenPowersPos[iT - 1];
Debug.Assert(numHH.exp + tenPower.exp <= 32);
numHH.Mul(ref tenPower);
numLL.Mul(ref tenPower);
wExp10 -= iT;
}
}
Debug.Assert(numHH.exp > 0 && numHH.exp < 32);
// Get the upper and lower bounds for these.
numHL = numHH;
numHH.MakeUpperBound();
numHL.MakeLowerBound();
uHH = numHH.UMod1();
uHL = numHL.UMod1();
numLH = numLL;
numLH.MakeUpperBound();
numLL.MakeLowerBound();
uLH = numLH.UMod1();
uLL = numLL.UMod1();
Debug.Assert(uLL <= uLH && uLH <= uHL && uHL <= uHH);
// Find the starting scale
uScale = 1;
if (uHH >= 100000000) {
uScale = 100000000;
wExp10 += 8;
} else {
if (uHH >= 10000) {
uScale = 10000;
wExp10 += 4;
}
if (uHH >= 100 * uScale) {
uScale *= 100;
wExp10 += 2;
}
}
if (uHH >= 10 * uScale) {
uScale *= 10;
wExp10++;
}
wExp10++;
Debug.Assert(uHH >= uScale && uHH / uScale < 10);
for (ib = 0; ; ) {
Debug.Assert(uLL <= uHH);
bHH = (byte)(uHH / uScale);
uHH %= uScale;
bLL = (byte)(uLL / uScale);
uLL %= uScale;
if (bHH != bLL) {
break;
}
Debug.Assert(0 != uHH || !numHH.IsZero);
mantissa[ib++] = bHH;
if (1 == uScale) {
// Multiply by 10^8.
uScale = 10000000;
numHH.Mul(ref TenPowersPos[7]);
numHH.MakeUpperBound();
uHH = numHH.UMod1();
if (uHH >= 100000000) {
goto LFail;
}
numHL.Mul(ref TenPowersPos[7]);
numHL.MakeLowerBound();
uHL = numHL.UMod1();
numLH.Mul(ref TenPowersPos[7]);
numLH.MakeUpperBound();
uLH = numLH.UMod1();
numLL.Mul(ref TenPowersPos[7]);
numLL.MakeLowerBound();
uLL = numLL.UMod1();
} else {
uScale /= 10;
}
}
// LL and HH diverged. Get the digit values for LH and HL.
Debug.Assert(0 <= bLL && bLL < bHH && bHH <= 9);
bLH = (byte)((uLH / uScale) % 10);
uLH %= uScale;
bHL = (byte)((uHL / uScale) % 10);
uHL %= uScale;
if (bLH >= bHL) {
goto LFail;
}
// LH and HL also diverged.
// We can get by with one fewer digit if: LL == LH and bLH is zero
// and the current value of LH is zero and the least significant bit of
// the double is zero. In this case, we have exactly the digit sequence
// for the original numLL and IEEE and will rounds numLL up to the double.
if (0 == bLH && 0 == uLH && numLH.IsZero && 0 == (dblLo & 1)) {
}
else if (bHL - bLH > 1) {
// HL and LH differ by at least two in this digit, so split
// the difference.
mantissa[ib++] = (byte)((bHL + bLH + 1) / 2);
} else if (0 != uHL || !numHL.IsZero || 0 == (dblLo & 1)) {
// We can just use bHL because this guarantees that we're bigger than
// LH and less than HL, so must convert to the double.
mantissa[ib++] = bHL;
} else {
goto LFail;
}
exponent = wExp10;
mantissaSize = ib;
goto LSucceed;
LSmallInt:
// dbl should be an integer from 1 to (2^53 - 1).
dblInt = dbl;
Debug.Assert(dblInt == Math.Floor(dblInt) && 1 <= dblInt && dblInt <= 9007199254740991.0d);
iT = 0;
if (dblInt >= C10toN[iT + 8]) {
iT += 8;
}
if (dblInt >= C10toN[iT + 4]) {
iT += 4;
}
if (dblInt >= C10toN[iT + 2]) {
iT += 2;
}
if (dblInt >= C10toN[iT + 1]) {
iT += 1;
}
Debug.Assert(iT >= 0 && iT <= 15);
Debug.Assert(dblInt >= C10toN[iT] && dblInt < C10toN[iT + 1]);
exponent = iT + 1;
for (ib = 0; 0 != dblInt; iT--) {
Debug.Assert(iT >= 0);
bHH = (byte)(dblInt / C10toN[iT]);
dblInt -= bHH * C10toN[iT];
Debug.Assert(dblInt == Math.Floor(dblInt) && 0 <= dblInt && dblInt < C10toN[iT]);
mantissa[ib++] = bHH;
}
mantissaSize = ib;
LSucceed:
#if DEBUG
// Verify that precise is working and gives the same answer
if (mantissaSize > 0) {
byte[] mantissaPrec = new byte[20];
int exponentPrec, mantissaSizePrec, idx;
DblToRgbPrecise(dbl, mantissaPrec, out exponentPrec, out mantissaSizePrec);
Debug.Assert(exponent == exponentPrec && mantissaSize == mantissaSizePrec);
// Assert(!memcmp(mantissa, mantissaPrec, mantissaSizePrec - 1));
bool equal = true;
for (idx = 0; idx < mantissaSize; idx++) {
equal &= (
(mantissa[idx] == mantissaPrec[idx]) ||
(idx == mantissaSize - 1) && Math.Abs(mantissa[idx] - mantissaPrec[idx]) <= 1
);
}
Debug.Assert(equal, "DblToRgbFast and DblToRgbPrecise should give the same result");
}
#endif
return true;
LFail:
exponent = mantissaSize = 0;
return false;
}