Files
yallie 9fa3874800 Added the contents of the archive.
Moved the original file to the archive subfolder.
2017-11-20 16:11:42 +03:00

1704 lines
53 KiB
C++

// ==++==
//
//
// Copyright (c) 2002 Microsoft Corporation. All rights reserved.
//
// The use and distribution terms for this software are contained in the file
// named license.txt, which can be found in the root of this distribution.
// By using this software in any fashion, you are agreeing to be bound by the
// terms of this license.
//
// You must not remove this notice, or any other, from this software.
//
//
// ==--==
// ===========================================================================
// File: numprs.cpp
//
// ===========================================================================
/***
*
*Purpose:
* This module contains string parsing functions for all types.
*
*Implementation Notes:
*
*****************************************************************************/
#include "rotor_palrt.h"
#include "oautil.h"
#include "oleauto.h"
#include "convert.h"
#include <limits.h>
#include <ctype.h>
#include <math.h>
STDAPI VarCyMulI4(CY cyLeft, long lRight, LPCY pcyResult);
// Constants used by VarParseNumFromStr
//
#define MAXEXP (INT_MAX/2)
const SPLIT64 sdlTenToEighteen = { {UI64(1000000000000000000)} };
// Max value of Decimal (= 79228162514264337593543950335), less 1st digit.
const BYTE rgbMaxDec[DEC_MAXDIG - 1] = {9,2,2,8,1,6,2,5,1,4,2,6,4,3,3,7,5,9,3,5,4,3,9,5,0,3,3,5};
typedef struct {
DBLSTRUCT dsHi;
DBLSTRUCT dsLo;
} DBLPREC;
#define DEFDP(a,b,c,d,e,f) { DEFDS(a,b,c,0), DEFDS(d,e,f,0) }
#define BINEXPFACTOR 0x100
DBLPREC dpPwr10[16] = {
DEFDP(0x37E08000, 0x1C379, 0x434, 0x00000000, 0x00000, 0x000), // 1E16
DEFDP(0xB5056E16, 0x3B8B5, 0x469, 0x80000000, 0x677C0, 0x434), // 1E32
DEFDP(0xA0A1C872, 0x5E531, 0x49E, 0xC204ADFD, 0x75A59, 0x469), // 1E48
DEFDP(0xE93FF9F4, 0x84F03, 0x4D3, 0xFC2DC71D, 0xB54F2, 0x49E), // 1E64
DEFDP(0x51F0FB5E, 0xAFCEF, 0x508, 0xCC117B25, 0xFEF70, 0x4D3), // 1E80
DEFDP(0x62D8B362, 0xDF675, 0x53D, 0x3E253BCF, 0x28B17, 0x508), // 1E96
DEFDP(0xB8132466, 0x0A1F5, 0x573, 0x147B5E30, 0x4F01F, 0x53D), // 1E112
DEFDP(0xF9301D31, 0x27748, 0x5A8, 0xBC0DB0DA, 0x37F19, 0x573), // 1E128
DEFDP(0x38B51A74, 0x48057, 0x5DD, 0x5801B271, 0xB9D7C, 0x5A8), // 1E144
DEFDP(0x256FFCC2, 0x6C2D4, 0x612, 0xEE21ACC5, 0xEA95D, 0x5DD), // 1E160
DEFDP(0x5230B377, 0x94514, 0x647, 0xEE3DD40F, 0xE4DF5, 0x612), // 1E176
DEFDP(0xF1A724EA, 0xC0E1E, 0x67C, 0xEE094FD1, 0x5AA16, 0x647), // 1E192
DEFDP(0x86A6F04C, 0xF25C1, 0x6B1, 0x08A56CCD, 0x45A77, 0x67A), // 1E208
DEFDP(0xDFFC6799, 0x14A52, 0x6E7, 0xD0B70D99, 0x2F82B, 0x6B0), // 1E224
DEFDP(0xDE7AD7E2, 0x33234, 0x71C, 0x28368782, 0xD96B3, 0x6E7), // 1E240
// 1E256 has exponent reduced by BINEXPFACTOR (0x100)
DEFDP(0x7F73BF3B, 0x54FDD, 0x651, 0xEE006E63, 0xA3776, 0x61C), // 1E256
};
// This function is used to ensure it's operands are not extended precision.
//
inline double DIF(double a, double b) {return a - b;}
void DpMul(DBLPREC &dpDest, DBLPREC &dpSrc)
{
#define DBLSPLIT (~0x7ffffff)
DBLSTRUCT dsSplit;
double dblOp1Hi, dblOp1Mid, dblOp1Lo;
double dblOp2Hi, dblOp2Mid, dblOp2Lo;
double dblSumHi, dblSumMid, dblSumLo;
double dblTmp;
// Split the high double into two: one with the upper 26 bits of the
// mantissa, the other with the lower 27 bits.
//
dsSplit.dbl = dpDest.dsHi.dbl;
dsSplit.u.mantLo &= DBLSPLIT;
dblOp1Hi = dsSplit.dbl;
dblOp1Mid = dpDest.dsHi.dbl - dblOp1Hi;
dblOp1Lo = dpDest.dsLo.dbl;
dsSplit.dbl = dpSrc.dsHi.dbl;
dsSplit.u.mantLo &= DBLSPLIT;
dblOp2Hi = dsSplit.dbl;
dblOp2Mid = dpSrc.dsHi.dbl - dblOp2Hi;
dblOp2Lo = dpSrc.dsLo.dbl;
// Create and accumulate partial products. Since each number is
// broken into 3 pieces, we should have a total of 9 parital products.
// However, the lowest 1 is below our horizon of interest. We have
// plenty of extra bits for this not to affect the result.
//
dblSumLo = dblOp1Mid * dblOp2Lo + dblOp1Lo * dblOp2Mid;
dblSumLo += dblOp1Hi * dblOp2Lo + dblOp1Mid * dblOp2Mid + dblOp1Lo * dblOp2Hi;
dblSumMid = dblOp1Hi * dblOp2Mid + dblOp1Mid * dblOp2Hi;
dblSumHi = dblOp1Hi * dblOp2Hi;
// We need to split the middle sum between hi and lo. By just
// adding hi to mid and then subtracting hi back out, we see how
// much fits into hi and can add the rest into lo.
//
// Adding then subtracting only works if there is no extra precision
// being kept. An inline function call is used for the subtraction
// in hopes of ensuring values are flushed from extended-precision
// registers (if any). [This is necessary at least for x86, which
// is used for a test bed for this code.]
//
dblTmp = dblSumHi + dblSumMid;
dblTmp = DIF(dblTmp, dblSumHi);
dblSumHi += dblTmp;
dblSumLo += dblSumMid - dblTmp;
// Store result.
//
dpDest.dsHi.dbl = dblSumHi;
dpDest.dsLo.dbl = dblSumLo;
}
HRESULT MulPower10(double *pdblVal, double dblValLo, int nPwr10)
{
int nCurPwr;
int nExp;
DBLPREC dpCurPwr;
DBLPREC dpRes;
DBLPREC dpQuo;
nCurPwr = abs(nPwr10);
if (dblValLo == 0 && nCurPwr <= MAXINTPWR10) {
if (nPwr10 >= 0)
*pdblVal *= dblPower10[nPwr10];
else
*pdblVal /= dblPower10[nCurPwr];
return NOERROR;
}
dpCurPwr.dsHi.dbl = fnDblPower10(nCurPwr & 0xF);
dpCurPwr.dsLo.dbl = 0;
nCurPwr = nCurPwr >> 4;
if ((nCurPwr & 0xF) != 0)
DpMul(dpCurPwr, dpPwr10[(nCurPwr & 0xF) - 1]);
if (nCurPwr > 15) {
// We've got a really big power of 10. Check for overflow
// or underflow.
//
if (nPwr10 < -350) {
*pdblVal = 0.0;
return NOERROR;
}
if (nPwr10 >= 309)
return RESULT(DISP_E_OVERFLOW);
// In order to prevent possible overflow, the exponent for 1E256
// has been reduced by BINEXPFACTOR.
//
DpMul(dpCurPwr, dpPwr10[15]);
}
dpRes.dsHi.dbl = *pdblVal;
dpRes.dsLo.dbl = dblValLo;
if (nPwr10 >= 0) {
DpMul(dpRes, dpCurPwr);
*pdblVal = dpRes.dsHi.dbl + dpRes.dsLo.dbl;
if (nCurPwr > 15) {
// We've reduced the exponent. See if we can restore it without
// overflow.
//
dpRes.dsHi.dbl = *pdblVal;
nExp = dpRes.dsHi.u.exp + BINEXPFACTOR;
if (nExp >= 0x7FF)
return RESULT(DISP_E_OVERFLOW);
dpRes.dsHi.u.exp = nExp;
*pdblVal = dpRes.dsHi.dbl;
}
}
else {
if (nCurPwr > 15) {
// We've reduced the exponent. Apply it to the digit value now
// so it will get factored into the final divide.
//
_ASSERTE(dpRes.dsHi.dbl != 0);
dpRes.dsHi.u.exp -= BINEXPFACTOR;
if (dpRes.dsLo.dbl != 0)
dpRes.dsLo.u.exp -= BINEXPFACTOR;
}
// Perform double-precision divide.
//
dpQuo.dsHi.dbl = *pdblVal = dpRes.dsHi.dbl / dpCurPwr.dsHi.dbl;
dpQuo.dsLo.dbl = 0;
DpMul(dpQuo, dpCurPwr);
dpRes.dsHi.dbl -= dpQuo.dsHi.dbl;
dpRes.dsLo.dbl -= dpQuo.dsLo.dbl;
*pdblVal += (dpRes.dsHi.dbl + dpRes.dsLo.dbl) / (dpCurPwr.dsHi.dbl + dpCurPwr.dsLo.dbl);
}
return NOERROR;
}
DWORDLONG UInt64x64To128(SPLIT64 sdlOp1, SPLIT64 sdlOp2, DWORDLONG *pdlHi)
{
SPLIT64 sdlTmp1;
SPLIT64 sdlTmp2;
SPLIT64 sdlTmp3;
sdlTmp1.int64 = UInt32x32To64(sdlOp1.u.Lo, sdlOp2.u.Lo); // lo partial prod
sdlTmp2.int64 = UInt32x32To64(sdlOp1.u.Lo, sdlOp2.u.Hi); // mid 1 partial prod
sdlTmp1.u.Hi += sdlTmp2.u.Lo;
if (sdlTmp1.u.Hi < sdlTmp2.u.Lo) // test for carry
sdlTmp2.u.Hi++;
sdlTmp3.int64 = UInt32x32To64(sdlOp1.u.Hi, sdlOp2.u.Hi) + (DWORDLONG)sdlTmp2.u.Hi;
sdlTmp2.int64 = UInt32x32To64(sdlOp1.u.Hi, sdlOp2.u.Lo);
sdlTmp1.u.Hi += sdlTmp2.u.Lo;
if (sdlTmp1.u.Hi < sdlTmp2.u.Lo) // test for carry
sdlTmp2.u.Hi++;
sdlTmp3.int64 += (DWORDLONG)sdlTmp2.u.Hi;
*pdlHi = sdlTmp3.int64;
return sdlTmp1.int64;
}
/* decide if an OLECHAR is a digit taking consideration of locale */
INTERNAL_(BOOL)
ISOADIGIT (LCID lcid, OLECHAR ch)
{
if (ch >= '0' && ch <= '9')
return TRUE;
else if (IsThai(lcid) && ch >= xchThaiZero && ch <= xchThaiNine)
return TRUE;
else
return FALSE;
}
/* return the corresponding digit taking consideration of locale
return -1 if not a digit */
INTERNAL_(int)
GETOADIGIT (LCID lcid, OLECHAR ch)
{
if (ch >= '0' && ch <= '9')
return ch - '0';
else if (IsThai(lcid) && ch >= xchThaiZero && ch <= xchThaiNine)
return ch - xchThaiZero;
else
return -1;
}
/***
* VarParseNumFromStr
*
* Entry:
* pstr - pointer to zero-terminated string.
* lcid - lcid to use.
* dwFlags - optional LOCALE_NOUSEROVERRIDE flag.
* pnumprs - pointer to NUMPARSE structure to fill in with return info.
* rgbDig - pointer for array of values for each digit.
*
* Purpose:
* Parse a string to a number. pnumprs and rgbDig are pointers
* where information will be filled in.
*
* rgbDig is an array of bytes that will be filled in with the value
* of each digit character in the string. This array will contain
* nothing but values in the range 0 - 9 for decimal numbers,
* 0 - 15 for hex, 0 - 7 for octal. Leading zeros are always stripped
* off.
*
* pnumprs->cDig contains the max size of this array on entry. The
* caller should use a local (frame) array that is 1 element longer
* than the maximum number of digits that make sense for the type
* of the number. This extra element will contain the rounding
* digit. For decimal numbers, if there are more digits
* in the string than elements in the array, the NUMPRS_INEXACT
* flag will be set. For non-decimal numbers, an Overflow error
* is returned if the number of digits is too large.
*
* pnumprs->dwInFlags contains an array of bit flags that identify
* the syntactic elements that should be accepted.
*
* The NUMPARSE structure pointed to by pnumprs is filled in with
* the rest of the info needed to convert the string to a number:
*
* cDig - Modified to have the number of elements in rgbDig that
* are actually filled in. Will never exceed it's value on entry.
*
* nPwr10 - The power of 10 of the last digit in rgbDig. Thus the
* number represented by rgbDig can be converted to a binary integer,
* and must then be scaled by 10^nPwr10. Will always be zero (and
* can be ignored) for non-decimal numbers.
*
* nBaseShift - Indicates the number base: 0 for decimal, 3 for octal,
* 4 for hex. Represents the bit-shift count for binary-derived bases.
*
* dwOutFlags - Contains a bit strings identifying the syntactic
* elements found in the string. Also includes NUMPRS_NEG if the
* number is negative and NUMPRS_INEXACT as mentioned above.
*
* Exit:
* *pnumprs and rgbDig filled in.
* HRESULT of operation returned.
*
* Exceptions:
* A memory allocation is made if DBCS lcid so the string can have
* characters mapped. Otherwise, the only possible errors are
* Overflow and Type Mismatch.
*
***********************************************************************/
STDAPI VarParseNumFromStr(OLECHAR * pstr,
LCID lcid,
ULONG dwFlags,
NUMPARSE * pnumprs,
BYTE * rgbDig)
{
HRESULT hresult = NOERROR;
LPOLESTR pstrDBCS = NULL;
LPOLESTR pstrStart;
OLECHAR ch1;
BYTE *pbDig = rgbDig;
BYTE *pbDigEnd;
NUMPARSE np;
_ASSERTE(lcid == 0x0409);
_ASSERTE(dwFlags == LOCALE_NOUSEROVERRIDE);
if (rgbDig == NULL || pnumprs == NULL)
return RESULT(E_INVALIDARG);
// Local structure keeps working values and results.
//
np.cDig = 0;
np.dwInFlags = pnumprs->dwInFlags;
np.dwOutFlags = 0;
np.cchUsed = 0;
np.nBaseShift = 0;
np.nPwr10 = 0;
pbDigEnd = rgbDig + pnumprs->cDig;
if (pstr == NULL) {
hresult = RESULT(DISP_E_TYPEMISMATCH);
goto Error;
}
// We know this is LCID = 0x0409 so there's no need to worry about
// DBCS
ch1 = *pstr++;
pstrStart = pstr;
if ( iswspace(ch1) && (np.dwInFlags & NUMPRS_LEADING_WHITE) ) {
np.dwOutFlags |= NUMPRS_LEADING_WHITE;
while (iswspace(ch1 = *pstr++));
}
if (ch1 == OASTR('&') && (np.dwInFlags & NUMPRS_HEX_OCT)) {
int nMaxVal;
int nVal;
BOOL fHavDig = FALSE;
// Get hex or octal integer.
//
ch1 = *pstr++;
if (ch1 == 'h' || ch1== 'H') {
np.nBaseShift = 4; // base 16 digits
nMaxVal = 15;
ch1 = *pstr++;
}
else {
np.nBaseShift = 3; // base 8 digits
nMaxVal = 7;
if (ch1 == 'o' || ch1 == 'O')
ch1 = *pstr++;
}
if (ch1 == '0')
fHavDig = TRUE;
while (ch1 == '0') // scan off leading zeros
ch1 = *pstr++;
for(;;) {
if ((nVal = GETOADIGIT (lcid, ch1)) >= 0)
;
else if (ch1 >= 'a' && ch1 <= 'f')
nVal = ch1 - 'a' + 10;
else if (ch1 >= 'A' && ch1 <= 'F')
nVal = ch1 - 'A' + 10;
else break;
if (nVal > nMaxVal)
break;
if (pbDig >= pbDigEnd) {
// If we have too many digits, return the same info as if
// this wasn't an error.
//
hresult = RESULT(DISP_E_OVERFLOW);
break;
}
*pbDig++ = nVal;
ch1 = *pstr++;
}
if (pbDig == rgbDig) {
if (!fHavDig) { // find any valid digits?
// If we have no digits, leave the NUMPARSE blank.
//
hresult = RESULT(DISP_E_TYPEMISMATCH);
goto Error;
}
*pbDig++ = 0;
}
np.dwOutFlags |= NUMPRS_HEX_OCT;
if ( iswspace(ch1) && (np.dwInFlags & NUMPRS_TRAILING_WHITE) ) {
np.dwOutFlags |= NUMPRS_TRAILING_WHITE;
while (iswspace(ch1 = *pstr++));
}
}
else {
// Get decimal number. Possible formatting options:
//
// 1. A + or - sign at the beginning or end, or parens () to indicate
// negative. May have spaces around it.
//
// 2. A currency symbol at the beginning or end. May be on either
// side of the sign indicator. May have spaces around it.
//
// 3. Thousands separators are ignored.
//
// 4. Base-10 exponent may be specfied with E or D.
//
#define HAVSIGN (NUMPRS_LEADING_PLUS | NUMPRS_LEADING_MINUS | NUMPRS_TRAILING_PLUS | NUMPRS_TRAILING_MINUS | NUMPRS_PARENS)
BOOL fOpenParen = FALSE;
BOOL fNegPwr = FALSE;
int nPwrCnt = 0;
int nExp;
LPOLESTR pstrTmp;
NUMINFO *pnuminfo;
IfFailGo(GetNumInfo(lcid, dwFlags, &pnuminfo), Error);
for ( ; !ISOADIGIT (lcid, ch1); ch1 = *pstr++ ) {
// Parse off junk in front of first digit
//
if ( iswspace(ch1) ) {
if ( !(np.dwInFlags & NUMPRS_LEADING_WHITE) )
goto TmError;
np.dwOutFlags |= NUMPRS_LEADING_WHITE;
continue;
}
if (ch1 == '+') {
if ( !(np.dwInFlags & NUMPRS_LEADING_PLUS) ||
(np.dwOutFlags & HAVSIGN) )
goto TmError;
np.dwOutFlags |= NUMPRS_LEADING_PLUS;
continue;
}
if (ch1 == '-') {
if ( !(np.dwInFlags & NUMPRS_LEADING_MINUS) ||
(np.dwOutFlags & HAVSIGN) )
goto TmError;
np.dwOutFlags |= NUMPRS_LEADING_MINUS | NUMPRS_NEG;
continue;
}
if (ch1 == '(') {
if ( !(np.dwInFlags & NUMPRS_PARENS) ||
(np.dwOutFlags & HAVSIGN) )
goto TmError;
fOpenParen = TRUE;
np.dwOutFlags |= NUMPRS_PARENS | NUMPRS_NEG;
continue;
}
if (ch1 == pnuminfo->chDecimal) {
ch1 = *pstr++;
if ( !(np.dwInFlags & NUMPRS_DECIMAL) ||
! ISOADIGIT (lcid, ch1)) // verify DP is followed by digit
goto TmError;
np.dwOutFlags |= NUMPRS_DECIMAL;
nPwrCnt = -1;
break;
}
// If the character is the Currency Decimal, and we might be parsing for
// currency, and the character can't be confused with the Number
// Thousands Separater, take it as a decimal.
if ((ch1 == pnuminfo->chCurrencyDecimal) && (np.dwInFlags & NUMPRS_CURRENCY) &&
(ch1 != pnuminfo->chThousand1 && ch1 != pnuminfo->chThousand2))
{
ch1 = *pstr++;
if (!(np.dwInFlags & NUMPRS_DECIMAL) ||
ch1 < '0' || ch1 > '9' ) // verify DP is followed by digit
goto TmError;
np.dwOutFlags |= (NUMPRS_DECIMAL | NUMPRS_CURRENCY);
nPwrCnt = -1;
break;
}
// Haven't recognized it yet. Maybe it's the Currency symbol.
//
if ( (np.dwInFlags & NUMPRS_CURRENCY) && !(np.dwOutFlags & NUMPRS_CURRENCY) ) {
if (pnuminfo->fFastCur) {
if (ch1 == pnuminfo->rgbCurrency[0]) {
np.dwOutFlags |= NUMPRS_CURRENCY;
continue;
}
}
else {
// Must do case-insensitive check for currency symbol.
// First check if there's enough characters left in the string.
//
pstr--;
pstrTmp = pstr + pnuminfo->cchCurrency;
while (pstr < pstrTmp)
if (*pstr++ == 0)
goto TmError;
if ( CompareStringW(lcid,
NORM_IGNORECASE|NORM_IGNOREWIDTH,
pstr - pnuminfo->cchCurrency,
pnuminfo->cchCurrency,
pnuminfo->rgbCurrency,
pnuminfo->cchCurrency)
== 2) {
np.dwOutFlags |= NUMPRS_CURRENCY;
continue; // it matched; we've already skipped over it.
}
}
}
// Don't know what it is
//
TmError:
hresult = RESULT(DISP_E_TYPEMISMATCH);
goto Error;
}
// Found the first decimal digit, in ch1.
//
StripZeros:
for( ; ; ch1 = *pstr++)
{
if (GETOADIGIT (lcid, ch1) == 0)
np.nPwr10 += nPwrCnt;
else if (ch1 == pnuminfo->chThousand1 || ch1 == pnuminfo->chThousand2)
{
if ( ch1 == 0 || !(np.dwInFlags & NUMPRS_THOUSANDS) )
goto ParseDone;
np.dwOutFlags |= NUMPRS_THOUSANDS;
}
else if (ch1 == pnuminfo->chCurrencyThousand && ch1 != pnuminfo->chDecimal)
{
if (!(np.dwInFlags & NUMPRS_CURRENCY))
break;
if (ch1 == 0 || !(np.dwInFlags & NUMPRS_THOUSANDS))
goto ParseDone;
np.dwOutFlags |= (NUMPRS_THOUSANDS | NUMPRS_CURRENCY);
}
else
break;
}
LoadDigits:
for( ; ; ch1 = *pstr++) {
if ( ISOADIGIT (lcid, ch1) ) {
if (pbDig < pbDigEnd) {
*pbDig++ = GETOADIGIT (lcid, ch1);
np.nPwr10 += nPwrCnt;
}
else
{
// We filled up all the digits.
//
if (ch1 != '0')
np.dwOutFlags |= NUMPRS_INEXACT;
np.nPwr10 += nPwrCnt + 1;
}
}
else if (ch1 == pnuminfo->chThousand1 || ch1 == pnuminfo->chThousand2) {
if ( ch1 == 0 || !(np.dwInFlags & NUMPRS_THOUSANDS) )
goto ParseDone;
np.dwOutFlags |= NUMPRS_THOUSANDS;
}
else if (ch1 == pnuminfo->chCurrencyThousand && ch1 != pnuminfo->chDecimal) {
if (!(np.dwInFlags & NUMPRS_CURRENCY))
break;
if (ch1 == 0 || !(np.dwInFlags & NUMPRS_THOUSANDS))
goto ParseDone;
np.dwOutFlags |= (NUMPRS_THOUSANDS | NUMPRS_CURRENCY);
}
else
break;
}
// End of string of decimal digits.
//
if (ch1 == pnuminfo->chDecimal)
{
if (!(np.dwInFlags & NUMPRS_DECIMAL) || (np.dwOutFlags & NUMPRS_DECIMAL))
goto ParseDone;
nPwrCnt = -1;
np.dwOutFlags |= NUMPRS_DECIMAL;
ch1 = *pstr++;
if (pbDig == rgbDig) // No non-zero digits yet,
goto StripZeros; // keep scanning off zeros.
goto LoadDigits;
}
if (ch1 == pnuminfo->chCurrencyDecimal && ch1 != pnuminfo->chThousand1 && ch1 != pnuminfo->chThousand2)
{
if (!(np.dwInFlags & (NUMPRS_DECIMAL | NUMPRS_CURRENCY)) ||
(np.dwOutFlags & NUMPRS_DECIMAL))
goto ParseDone;
nPwrCnt = -1;
np.dwOutFlags |= (NUMPRS_DECIMAL | NUMPRS_CURRENCY);
ch1 = *pstr++;
if (pbDig == rgbDig) // No non-zero digits yet,
goto StripZeros; // keep scanning off zeros.
goto LoadDigits;
}
// Look for exponent. Be sure not to confuse it with currency.
//
if ( (ch1 == 'e' || ch1 == 'E' || ch1 == 'd' || ch1 == 'D') &&
(np.dwInFlags & NUMPRS_EXPONENT) ) {
pstrTmp = pstr; // save in case of unrecognized "e/d" (in which case
// it may be an alpha currency symbol.
ch1 = *pstr++;
if (ch1 == '-') {
fNegPwr = TRUE;
ch1 = *pstr++;
}
else if (ch1 == '+')
ch1 = *pstr++;
if ( ISOADIGIT (lcid, ch1)) {
// Get exponent digits, convert to number
//
nExp = GETOADIGIT (lcid, ch1);
while (ISOADIGIT (lcid, ch1 = *pstr++)) {
if (nExp >= MAXEXP/10) {
pstr = pstrTmp;
hresult = RESULT(DISP_E_OVERFLOW);
goto ParseDone;
}
nExp = nExp * 10 + GETOADIGIT (lcid, ch1);
}
if (fNegPwr)
nExp = -nExp;
np.nPwr10 += nExp;
np.dwOutFlags |= NUMPRS_EXPONENT;
}
else {
// not a valid "eN" or "dN" exponent -- revert back to where we
// were in case this is something like the "DM" currency symbol.
pstr = pstrTmp;
ch1 = *(pstr-1);
}
}
// Parse off junk after number.
//
for ( ; ch1 != '\0'; ch1 = *pstr++) {
if ( iswspace(ch1) ) {
if ( !(np.dwInFlags & NUMPRS_TRAILING_WHITE) )
break;
np.dwOutFlags |= NUMPRS_TRAILING_WHITE;
continue;
}
if (ch1 == ')' && fOpenParen) {
fOpenParen = FALSE;
continue;
}
// check for trailing + or - signs (wierd, but sometimes happens)
//
if (ch1 == '+') {
if ( !(np.dwInFlags & NUMPRS_TRAILING_PLUS) ||
(np.dwOutFlags & HAVSIGN) )
break;
np.dwOutFlags |= NUMPRS_TRAILING_PLUS;
continue;
}
if (ch1 == '-') {
if ( !(np.dwInFlags & NUMPRS_TRAILING_MINUS) ||
(np.dwOutFlags & HAVSIGN) )
break;
np.dwOutFlags |= NUMPRS_TRAILING_MINUS | NUMPRS_NEG;
continue;
}
// Haven't recognized it yet. Maybe it's the Currency symbol.
//
if (np.dwInFlags & NUMPRS_CURRENCY) {
if (pnuminfo->fFastCur) {
if (ch1 == pnuminfo->rgbCurrency[0]) {
np.dwOutFlags |= NUMPRS_CURRENCY;
continue;
}
}
else {
// Must do case-insensitive check for currency symbol.
// First check if there's enough characters left in the string.
//
pstr--;
pstrTmp = pstr + pnuminfo->cchCurrency;
while (pstr < pstrTmp)
if (*pstr++ == 0)
goto NotTrailCur;
if ( CompareStringW(lcid,
NORM_IGNORECASE|NORM_IGNOREWIDTH,
pstr - pnuminfo->cchCurrency,
pnuminfo->cchCurrency,
pnuminfo->rgbCurrency,
pnuminfo->cchCurrency)
== 2) {
np.dwOutFlags |= NUMPRS_CURRENCY;
continue; // it matched; we've already skipped over it.
}
// Currency string didn't match. Back up position so we
// can report cchUsed correctly.
//
NotTrailCur:
pstr = pstrTmp - pnuminfo->cchCurrency + 1;
}
}
// Unknown character
//
break;
} // for
ParseDone:
while (pbDig > rgbDig + 1 && pbDig[-1] == 0) {
pbDig--;
np.nPwr10++;
}
if (pbDig == rgbDig)
*pbDig++ = 0; // make sure we have a 0 digit.
if (fOpenParen) // if leftover left paren, then error.
hresult = RESULT(DISP_E_TYPEMISMATCH);
}
// Fill in return info
//
{
ULONG_PTR ulDigDiff = (ULONG_PTR)(pbDig - rgbDig); // WIN64
_ASSERTE( ulDigDiff < 0x8FFFFFFF );
np.cDig = (int)ulDigDiff;
ULONG_PTR ulUsedDiff = (ULONG_PTR)(pstr - pstrStart);
_ASSERTE( ulUsedDiff < 0x8FFFFFFF );
np.cchUsed = (int)ulUsedDiff;
if ( ch1 != 0 && (np.dwInFlags & NUMPRS_USE_ALL) )
hresult = RESULT(DISP_E_TYPEMISMATCH);
}
Error:
if (pstrDBCS)
DispFree(pstrDBCS);
*pnumprs = np;
return hresult;
}
/***
* VarNumFromParseNum
*
* Entry:
* pnumprs - pointer to NUMPARSE structure to fill in with return info.
* rgbDig - pointer to array of digit values.
* dwVtBits - array of bits indicating acceptable result type.
* pvar - pointer to variant to be filled in with result.
*
* Purpose:
* After a string has been parsed with VarParseNumFromStr, convert
* the parsed output to a number. See that function for a detailed
* description of rgbDig and pnumprs. Only the cDig, nBaseShift,
* dwOutFlags, and nPwr10 fields of the NUMPARSE structure are used.
*
* dwVtBits may have any number of bits set corresponding to numeric
* types that are acceptable for the result. This function will
* choose the smallest allowed type that can hold the result value
* with as little precision loss as possible. For non-integer types,
* the following selection algorithm is used, ordered from
* smallest to largest type:
*
* No precision loss:
* 1. R4 - Integer of 7 or fewer digits.
* 2. R8 - Integer of 15 or fewer digits.
* 3. CY - 15 or fewer digits to left of d.p., 4 or fewer to the right.
* 4. Decimal - 29 or fewer digts, 28 or less to right of d.p.
*
* Exit:
* *pvar filled in (not released first).
* HRESULT of operation returned.
*
* Exceptions:
* Overflow if the none of the allowed types can hold the result.
*
***********************************************************************/
#define VTBIT_FRAC (VTBIT_R4 | VTBIT_R8 | VTBIT_CY | VTBIT_DECIMAL)
#define VTBIT_UINT (VTBIT_UI1 | VTBIT_UI2 | VTBIT_UI4 | VTBIT_UI8)
#define VTBIT_SINT (VTBIT_I1 | VTBIT_I2 | VTBIT_I4 | VTBIT_I8)
#define VTBIT_INT (VTBIT_UINT| VTBIT_SINT)
STDAPI VarNumFromParseNum(NUMPARSE * pnumprs,
BYTE * rgbDig,
ULONG dwVtBits,
VARIANT * pvar)
{
int nBaseShift = pnumprs->nBaseShift;
int cDig = pnumprs->cDig;
int nPwr10 = pnumprs->nPwr10;
DWORD dwOutFlags = pnumprs->dwOutFlags;
int nDec = 0;
int nIntDig = cDig + nPwr10;
int nMaxDig;
BYTE *pbDig = rgbDig;
ULONG64 ullVal = 0;
ULONG64 ullTmp;
LONG64 llTmp;
double dblVal;
CY cyVal;
// fRound is to deal with a bug in OA32.
bool fRound = true;
SPLIT64 sdlLo;
SPLIT64 sdlHi;
if (nBaseShift != 0) {
// Hex or octal number. Check for overflow case first.
//
int nBits = nBaseShift * cDig; // no. of bits in number
if (nBits > 66 || nBits == 66 && rgbDig[0] > 1)
return RESULT(DISP_E_OVERFLOW);
for ( ; cDig > 0; cDig--)
ullVal = (ullVal << nBaseShift) + *pbDig++;
llTmp = ullVal; // llTmp & ullVal both used for storing result
if (dwVtBits & VTBIT_INT) {
if (dwVtBits & (VTBIT_I1 | VTBIT_UI1)) {
if ((dwVtBits & VTBIT_I1) && (ullVal <= 0xFF || ullVal >= (ULONG)SCHAR_MIN))
goto PickI1;
if ((dwVtBits & VTBIT_UI1) && ullVal <= 0xFF)
goto PickUI1;
}
if (dwVtBits & (VTBIT_I2 | VTBIT_UI2)) {
if ((dwVtBits & VTBIT_I2) && (ullVal <= 0xFFFF || ullVal >= (ULONG)SHRT_MIN))
goto PickI2;
if ((dwVtBits & VTBIT_UI2) && ullVal <= 0xFFFF)
goto PickUI2;
}
if (dwVtBits & (VTBIT_I4 | VTBIT_UI4)) {
if ((dwVtBits & VTBIT_I4) && (ullVal <= 0xFFFFFFFF || ullVal >= (ULONG)INT_MIN))
goto PickI4;
if ((dwVtBits & VTBIT_UI4) && ullVal <= 0xFFFFFFFF)
goto PickUI4;
}
if (dwVtBits & VTBIT_I8)
goto PickI8;
if (dwVtBits & VTBIT_UI8)
goto PickUI8;
}
// Not using an integer type.
//
if (ullVal & UI64(0x8000000000000000)) {
dwOutFlags |= NUMPRS_NEG;
pnumprs->dwOutFlags = dwOutFlags;
ullVal = -llTmp;
}
// To be compatible with OA32, if we are passed in a hex string
// with 31st bit set, force it to be a negative number.
else if (ullVal >= UI64(0x80000000) && ullVal < UI64(0x100000000)) {
dwOutFlags |= NUMPRS_NEG;
pnumprs->dwOutFlags = dwOutFlags;
if (ullVal != UI64(0x80000000))
{
LONG lTmp = (LONG)ullVal;
ullVal = -lTmp;
}
}
if (ullVal >= UI64(10000000) && (dwVtBits & (VTBIT_R8 | VTBIT_CY | VTBIT_DECIMAL)))
dwVtBits &= ~VTBIT_R4; // don't use R4 if too big
goto IntOvflow;
}
if (nPwr10 < 0)
nDec = -nPwr10; // set no. of decimal places (initialized to 0)
// See if we can convert the digits to a long int.
//
nMaxDig = cDig;
if ( !(dwVtBits & VTBIT_FRAC) ) { // Accept fractions?
nMaxDig -= nDec; // no, don't convert decimal places
nPwr10 += nDec;
}
if ((nMaxDig < 20 && !(dwVtBits & VTBIT_FRAC))
|| (nMaxDig <= 15 && (dwVtBits & VTBIT_FRAC))) {
// Fits within a 64-bit integer
//
if (nMaxDig == 10 && *pbDig == 4)
fRound = false;
//IntConv:
cDig -= nMaxDig;
for ( ; nMaxDig > 0; nMaxDig--)
ullVal = ullVal * 10 + *pbDig++;
// The number fit into a ULONG64 so far. See if it needs scaling
// or rounding. (nMaxDig < 0 if number < .1)
//
if (cDig > 0 && nMaxDig == 0 && fRound) {
// Need to round the integer
//
if (*pbDig > 5)
ullVal++;
else if (*pbDig++ == 5) {
if (dwOutFlags & NUMPRS_INEXACT)
goto RoundUp;
for ( ; cDig > 1; cDig--)
if (*pbDig++ != 0)
goto RoundUp;
// Round even.
//
if (ullVal & 1)
RoundUp:
ullVal++;
}
}
// Figure out what type to use.
//
IntConvDone:
if ( (dwVtBits & VTBIT_INT) && nIntDig <= 20 && (nDec == 0 || !(dwVtBits & VTBIT_FRAC)) ) {
// Come here if
// Integer type is selected AND
// the magnitude fits in 20 digits AND
// there are no decimal places OR
// no fractional type is acceptable.
//
// Might fit in an integer.
//
if (nPwr10 > 0) {
ullTmp = ullVal * ulPower10[nPwr10]; // this could overflow
if (nIntDig == 20 && rgbDig[0] >= 1) {
// Make sure scaling didn't cause overflow.
//
if (rgbDig[0] > 1 || ullTmp < UI64(10000000000000000000))
goto IntOvflow;
}
ullVal = ullTmp;
nPwr10 = 0; // we've already scaled it.
}
llTmp = ullVal; // make a signed copy
if ((dwOutFlags & NUMPRS_NEG) && ullVal != 0) {
dwVtBits &= ~VTBIT_UINT; // no UINTs if negative
llTmp = -llTmp; // set correct sign
}
if ( (dwVtBits & (VTBIT_I2 | VTBIT_UI1 | VTBIT_I1)) && ullVal <= SHRT_MAX+1 ) {
// Try I2, UI1, I1
//
if ( (dwVtBits & (VTBIT_UI1 | VTBIT_I1)) && ullVal <= 255 ) {
if ( (dwVtBits & VTBIT_I1) && ullVal <= 128 && llTmp < 128 ) {
PickI1:
V_VT(pvar) = VT_I1;
V_I1(pvar) = (signed char)llTmp;
return NOERROR;
}
if (dwVtBits & VTBIT_UI1) {
PickUI1:
V_VT(pvar) = VT_UI1;
V_UI1(pvar) = (BYTE)ullVal;
return NOERROR;
}
}
if ( (dwVtBits & VTBIT_I2) && llTmp <= SHRT_MAX ) {
PickI2:
V_VT(pvar) = VT_I2;
V_I2(pvar) = (SHORT)llTmp;
return NOERROR;
}
}
if ( (dwVtBits & (VTBIT_I4 | VTBIT_UI4 | VTBIT_UI2)) && ullVal <= ULONG_MAX) {
// Try UI4, I4, UI2
//
if ( (dwVtBits & VTBIT_UI2) && ullVal <= USHRT_MAX ) {
PickUI2:
V_VT(pvar) = VT_UI2;
V_UI2(pvar) = (USHORT)ullVal;
return NOERROR;
}
if ( (dwVtBits & VTBIT_I4) && ullVal <= (ULONG)LONG_MAX+1 && llTmp <= LONG_MAX) {
PickI4:
V_VT(pvar) = VT_I4;
V_I4(pvar) = (LONG)llTmp;
return NOERROR;
}
if ((dwVtBits & VTBIT_UI4) && ullVal <= ULONG_MAX) {
PickUI4:
V_VT(pvar) = VT_UI4;
V_UI4(pvar) = (ULONG)ullVal;
return NOERROR;
}
}
if ( (dwVtBits & VTBIT_I8) && llTmp <= _I64_MAX && llTmp >= _I64_MIN
&& !(llTmp < 0 && !(dwOutFlags & NUMPRS_NEG))) {
PickI8:
V_VT(pvar) = VT_I8;
V_I8(pvar) = llTmp;
return NOERROR;
}
if ((dwVtBits & VTBIT_UI8) && ullVal <= _UI64_MAX) {
PickUI8:
V_VT(pvar) = VT_UI8;
V_UI8(pvar) = ullVal;
return NOERROR;
}
}
IntOvflow:
// It's either big or has a fraction. Don't use integer type.
// We know we have 20 or fewer digits.
//
// Pick the type that makes most sense. Test types in
// order of size, using the condition of no precision loss
// OR no other type remains as a choice.
//
if ( (dwVtBits & (VTBIT_R4 | VTBIT_R8)) &&
(!(dwVtBits & (VTBIT_CY | VTBIT_DECIMAL)) || nDec == 0 &&
(nIntDig <= 7 || nIntDig <= 15 && (dwVtBits & VTBIT_R8))) ) {
PickDouble:
dblVal = (double)ullVal;
IfFailRet( MulPower10(&dblVal, 0, nPwr10) );
if (dblVal > dsR4Max.dbl)
dwVtBits &= ~VTBIT_R4;
if (dwOutFlags & NUMPRS_NEG)
dblVal = -dblVal;
if ( (dwVtBits & VTBIT_R4) &&
(!(dwVtBits & (VTBIT_R8 | VTBIT_CY | VTBIT_DECIMAL)) ||
nIntDig <= 7 && nDec == 0) ) {
V_VT(pvar) = VT_R4;
V_R4(pvar) = (float)dblVal;
return NOERROR;
}
if (dwVtBits & VTBIT_R8) {
V_VT(pvar) = VT_R8;
V_R8(pvar) = dblVal;
return NOERROR;
}
}
// If the optimal case of nDec <= 4 fails, and R8 & Decimal are
// not available, then prefer CY over R4 if the total significant
// digits of CY (nIntDig + 4) is > the significant digits of R4 (7).
//
if ( (dwVtBits & VTBIT_CY) && nIntDig <= 15 &&
(!(dwVtBits & (VTBIT_R4 | VTBIT_R8 | VTBIT_DECIMAL)) ||
nDec <= 4 || !(dwVtBits & (VTBIT_R8 | VTBIT_DECIMAL)) &&
nIntDig > 3) ) {
// Check for negative scaling.
//
nPwr10 += 4; // scale by 10000
cyVal.u.Hi = 0;
if (nPwr10 < 0) {
ULONG ulScale;
if (nPwr10 <= -10) {
// Too small -- 0.43 or less (2^32/10^10). Return 0.
//
cyVal.u.Lo = 0;
goto ReturnCy;
}
ulScale = (ULONG)ulPower10[-nPwr10];
cyVal.int64 = ullVal / ulScale;
// Check for round up.
//
ullVal = (ullVal % ulScale) << 1; // get remainder * 2
if (ullVal > ulScale || ullVal == ulScale &&
((cyVal.int64 & 1) || (dwOutFlags & NUMPRS_INEXACT)))
cyVal.int64++;
goto SetSignCy;
}
// Positive scaling can be handled by large CY conversion routine.
// If it ends up in overflow, it will be in the "big number" loop
// and give Decimal a try if applicable.
//
dwVtBits &= VTBIT_FRAC; // set up for big number loop
cyVal.int64 = ullVal;
goto ScaleCy;
}
// If the optimal case of nDec <= DECMAX fails, then prefer
// Decimal over R4 if the total significant digits of Decimal
// (cDig - (nDec - DECMAX)) is > the significant digits of R4 (7).
//
if ( (dwVtBits & VTBIT_DECIMAL) && nIntDig <= DEC_MAXDIG &&
(!(dwVtBits & (VTBIT_R4 | VTBIT_R8)) || nDec <= DECMAX ||
!(dwVtBits & VTBIT_R8) && pnumprs->cDig - nDec + DECMAX > 7) ) {
// The large Decimal conversion routine builds scaling into
// the digit conversion, so we need our own scaling here.
//
if (nPwr10 < 10) {
if (nPwr10 > 0) {
sdlLo.int64 = UInt32x32To64((ULONG)(ullVal&0xffffffff), (ULONG)ulPower10[nPwr10]);
sdlHi.int64 = UInt32x32To64((ULONG)(ullVal>>32), (ULONG)ulPower10[nPwr10]);
sdlHi.int64 += sdlLo.u.Hi;
sdlLo.u.Hi = sdlHi.u.Lo;
sdlHi.u.Lo = sdlHi.u.Hi;
sdlHi.u.Hi = 0;
nPwr10 = 0;
}
else {
sdlHi.u.Lo = 0;
if (nPwr10 >= -DECMAX) {
sdlLo.int64 = ullVal;
}
else if (nIntDig >= -DECMAX) {
// Power is so negative that we can't use all the digits.
//
ULONG64 ulScale = ulPower10[-DECMAX - nPwr10];
sdlLo.int64 = ullVal / ulScale;
nPwr10 = -DECMAX;
// Check for round up.
//
ullVal = (ullVal % ulScale) << 1; // get remainder * 2
if (ullVal > ulScale || ullVal == ulScale &&
((sdlLo.u.Lo & 1) || (dwOutFlags & NUMPRS_INEXACT)))
sdlLo.int64++;
}
else {
sdlLo.u.Hi = 0;
sdlLo.u.Lo = 0;
nPwr10 = 0;
}
}
}
else {
// Have a big power of 10.
//
if (nPwr10 > 18) {
if (nPwr10 > 27) {
ullVal = ullVal * ulPower10[nPwr10 - 27];
nPwr10 = 27;
}
sdlLo.int64 = UInt32x32To64((ULONG)(ullVal&0xffffffff), (ULONG)ulPower10[nPwr10 - 18]);
sdlHi.int64 = UInt32x32To64((ULONG)(ullVal>>32), (ULONG)ulPower10[nPwr10 - 18]);
_ASSERTE (sdlHi.u.Hi == 0);
sdlLo.u.Hi += sdlHi.u.Lo;
sdlLo.int64 = UInt64x64To128(sdlLo, sdlTenToEighteen, &sdlHi.int64);
if (sdlHi.u.Hi != 0) {
nPwr10 = pnumprs->nPwr10;
goto TryAgain;
}
}
else {
sdlLo.int64 = UInt32x32To64((ULONG)(ullVal&0xffffffff), (ULONG)ulPower10[nPwr10 - 9]);
sdlHi.int64 = UInt32x32To64((ULONG)(ullVal>>32), (ULONG)ulPower10[nPwr10 - 9]);
SPLIT64 sdlSave;
sdlSave.u.Lo = sdlHi.u.Hi;
sdlLo.u.Hi += sdlHi.u.Lo;
sdlHi.int64 = UInt32x32To64(ulTenToNine, sdlLo.u.Hi);
sdlLo.int64 = UInt32x32To64(ulTenToNine, sdlLo.u.Lo);
sdlSave.int64 = UInt32x32To64(ulTenToNine, sdlSave.u.Lo);
if (sdlSave.u.Hi != 0)
goto TryAgain;
sdlHi.int64 += sdlLo.u.Hi;
sdlLo.u.Hi = sdlHi.u.Lo;
sdlHi.u.Lo = sdlHi.u.Hi + sdlSave.u.Lo;
sdlHi.u.Hi = 0;
}
nPwr10 = 0;
}
goto ReturnDec;
}
TryAgain:
if (dwVtBits & (VTBIT_R8 | VTBIT_R4))
goto PickDouble;
return RESULT(DISP_E_OVERFLOW);
}
else {
// It might still fit into an I8 or UI8. See if it can.
//
if (nMaxDig == 20 && !(dwVtBits & VTBIT_FRAC)) {
if (*pbDig == 1) {
// Special case for UI8 when very near to overflow.
//
for ( ullVal = 0; nMaxDig > 0; nMaxDig--)
ullVal = ullVal * 10 + *pbDig++;
// consider: rounding ???
if (ullVal >= UI64(10000000000000000000)) {
cDig -= (int)(pbDig - rgbDig);
goto IntConvDone;
}
}
}
// nMaxDig >= 20.
// Pick the type that makes most sense. Test types in
// order of size, using the condition of no precision loss
// OR no other type remains as a choice.
//
dwVtBits &= VTBIT_FRAC;
while (dwVtBits) {
pbDig = rgbDig; // start over
cDig = pnumprs->cDig;
nPwr10 = pnumprs->nPwr10;
// Since we have at least 20 digits, R4 is never an optimal
// choice -- use it only as last resort.
//
if ( !(dwVtBits & (VTBIT_CY | VTBIT_DECIMAL)) ||
(dwVtBits & VTBIT_R8) && nDec == 0 && cDig <= 15 ) {
// *********
// Use R4/R8
// *********
//
// Decimal number
//
double dblValLo = 0;
int nMaxDig;
dblVal = 0;
nMaxDig = min(cDig, 15);
cDig -= nMaxDig;
for ( ; nMaxDig > 0; nMaxDig--)
dblVal = dblVal * 10.0 + *pbDig++;
if (cDig > 0) {
nMaxDig = cDig;
for ( ; cDig > 0; cDig--)
dblValLo = dblValLo * 10.0 + *pbDig++;
dblValLo /= fnDblPower10(nMaxDig);
nPwr10 += nMaxDig;
}
IfFailRet( MulPower10(&dblVal, dblValLo, nPwr10) );
// Set sign
//
if (dblVal > dsR4Max.dbl)
dwVtBits &= ~VTBIT_R4;
if (dwOutFlags & NUMPRS_NEG)
dblVal = -dblVal;
if (dwVtBits & VTBIT_R8) {
V_VT(pvar) = VT_R8;
V_R8(pvar) = dblVal;
return NOERROR;
}
else if (dwVtBits & VTBIT_R4) {
V_VT(pvar) = VT_R4;
V_R4(pvar) = (float)dblVal;
return NOERROR;
}
return RESULT(DISP_E_OVERFLOW);
} // Try R8
// If the optimal case of nDec <= 4 fails, and Decimal is not
// available, then prefer CY over R4/R8 if the total significant
// digits of CY (nIntDig + 4) is > the significant digits of
// R4/R8 (7/15).
//
if ( (dwVtBits & VTBIT_CY) && nIntDig <= 15 &&
(!(dwVtBits & (VTBIT_R4 | VTBIT_R8 | VTBIT_DECIMAL)) ||
nDec <= 4 || !(dwVtBits & VTBIT_DECIMAL) && nIntDig > 11 ||
!(dwVtBits & VTBIT_R8) && nIntDig > 3) ) {
// ******
// Try CY
// ******
//
CY cyTmp;
cyVal.u.Lo = 0;
cyVal.u.Hi = 0;
// Apply the scale factor first
//
nPwr10 += 4; // scale by 10^4
// Compute the number of digits to convert. This is limited to
// integer digits and the number of digits we actually have. If
// this is more than 9, we'll need to break up the conversion
// into blocks of up to 9 digits each.
//
nMaxDig = min( max(nIntDig+4, 0), cDig );
cDig -= nMaxDig; // non-integer digits left for rounding
nPwr10 += cDig; // increase power for digits not converted
if (nMaxDig > 9) {
if (nMaxDig > 18) {
cyTmp.u.Lo = *pbDig++;
cyTmp.u.Hi = 0;
VarCyMulI4(cyTmp, (ULONG)ulPower10[9], &cyTmp); // can't overflow
nMaxDig--;
for ( ; nMaxDig > 9; nMaxDig--)
cyVal.u.Lo = cyVal.u.Lo * 10 + *pbDig++;
cyVal.u.Lo += cyTmp.u.Lo;
cyVal.u.Hi += cyTmp.u.Hi;
if (cyVal.u.Lo < cyTmp.u.Lo) // got a carry?
cyVal.u.Hi++;
}
else
for ( ; nMaxDig > 9; nMaxDig--)
cyVal.u.Lo = cyVal.u.Lo * 10 + *pbDig++;
if ( FAILED(VarCyMulI4(cyVal, (ULONG)ulPower10[9], &cyVal)) )
goto CyOvflow;
}
ullVal = 0;
for ( ; nMaxDig > 0; nMaxDig--) {
ullVal = ullVal * 10 + *pbDig++;
}
cyVal.u.Lo += ULONG(ullVal);
if (cyVal.u.Lo < ullVal) // got a carry?
cyVal.u.Hi++;
// Scale by power of 10 if necessary. Enter here from integer
// code.
//
ScaleCy:
if (nPwr10 > 0) {
while (nPwr10 > 9) {
if ( FAILED(VarCyMulI4(cyVal, (ULONG)ulPower10[9], &cyVal)) )
goto CyOvflow;
nPwr10 -= 9;
}
if ( FAILED(VarCyMulI4(cyVal, (ULONG)ulPower10[nPwr10], &cyVal)) )
goto CyOvflow;
}
else if (cDig > 0 && nPwr10 == 0) {
// Need to round the integer
//
if (*pbDig > 5)
goto RoundUpCy;
else if (*pbDig++ == 5) {
if (dwOutFlags & NUMPRS_INEXACT)
goto RoundUpCy;
for ( ; cDig > 1; cDig--)
if (*pbDig++ != 0)
goto RoundUpCy;
// Round even.
//
if (cyVal.u.Lo & 1) {
RoundUpCy:
cyVal.u.Lo++;
if (cyVal.u.Lo == 0)
cyVal.u.Hi++;
}
}
}
SetSignCy:
// Set sign and check for overflow.
//
if (dwOutFlags & NUMPRS_NEG) {
if (cyVal.u.Lo == 0)
cyVal.u.Hi = -cyVal.u.Hi;
else {
cyVal.u.Lo = -(LONG)cyVal.u.Lo;
cyVal.u.Hi = ~cyVal.u.Hi;
}
if (cyVal.u.Hi > 0 )
goto CyOvflow;
}
else if (cyVal.u.Hi < 0 ) {
CyOvflow:
dwVtBits &= ~VTBIT_CY;
continue; // try another data type
}
ReturnCy:
V_VT(pvar) = VT_CY;
V_CY(pvar) = cyVal;
return NOERROR;
} // Try CY
// If the optimal case of nDec <= DECMAX fails, then prefer
// Decimal over R4/R8 if the total significant digits of Decimal
// (cDig - (nDec - DECMAX)) is > the significant digits of
// R4/R8 (7/15).
//
if ( (dwVtBits & VTBIT_DECIMAL) && nIntDig <= DEC_MAXDIG &&
(!(dwVtBits & (VTBIT_R4 | VTBIT_R8)) || nDec <= DECMAX ||
(nMaxDig = cDig - nDec + DECMAX) > 15 ||
!(dwVtBits & VTBIT_R8) && nMaxDig > 7) ) {
// ***********
// Try Decimal
// ***********
//
DWORDLONG dlTmp;
int nHiDig;
// Compute the number of digits to convert. This is limited to
// digits before the DECMAX decimal place, the number of digits
// we actually have, and DEC_MAXDIG (the max digits that will fit
// into 96 bits). If this is more than 9, we'll need to break
// up the conversion into blocks of up to 9 digits each.
//
nMaxDig = min(min(max(nIntDig+DECMAX, 0), cDig), DEC_MAXDIG);
cDig -= nMaxDig; // digits left for rounding
nHiDig = max(nIntDig, nMaxDig); // power of hi digit
nPwr10 += cDig; // power of lowest digit we use
sdlLo.int64 = 0;
sdlHi.int64 = 0;
ullVal = 0;
if (nIntDig < -DECMAX)
nPwr10 = 0;
else {
if (nHiDig > 9) {
// nHiDig is the same as nMaxDig unless there is a positive
// exponent on the number. It's as if the trailing zeros
// equivalent to the exponent were added on. We don't want
// to convert the digits, then multiply by the exponent in
// a separate step.
//
// We can't necessarily convert a 29-digit number if the 29
// digits exceed 79228162514264337593543950335. If some of the
// digits are fractional because of a decimal point, we should
// simply convert 1 less digit. We need to a compare for this
// value in advance so we know how many digits we're converting.
// This test must include a test for rounding up, taking advantage
// of the fact that the max value is odd, so "round even" is
// always "round up" if the next digit is 5.
//
LONG lTmp;
if (nMaxDig == DEC_MAXDIG && rgbDig[0] >= 7 &&
(rgbDig[0] > 7 || (lTmp = memcmp(rgbDig+1, rgbMaxDec, DEC_MAXDIG - 1)) > 0 ||
lTmp == 0 && cDig > 0 && rgbDig[DEC_MAXDIG] >= 5)) {
// Have the overflow case.
//
if (nIntDig >= nMaxDig)
goto DecOvFlow;
// We're not actually out of range. Just chop off the last digit.
//
nMaxDig--;// number of digits to convert
nHiDig--; // power of highest digit
cDig++; // digits left for rounding
nPwr10++; // power of lowest digit we use
}
// Look for digits 28 to nMaxDig.
//
for ( ; nHiDig > 27 && nMaxDig > 0; nHiDig--, nMaxDig--)
ullVal = ullVal * 10 + *pbDig++;
if (ullVal > 0) {
sdlLo.int64 += UInt32x32To64(ulTenToNine, (ULONG)ullVal);
ullVal = 0;
}
// Now look for digits 19 - 27.
//
for ( ; nHiDig > 18 && nMaxDig > 0; nHiDig--, nMaxDig--)
ullVal = ullVal * 10 + *pbDig++;
if (nHiDig > 18)
ullVal *= ulPower10[nHiDig - 18];
sdlLo.int64 += ullVal;
if (sdlLo.int64 != 0) {
sdlLo.int64 = UInt64x64To128(sdlLo, sdlTenToEighteen, &sdlHi.int64);
ullVal = 0;
}
// 9 <= nHiDig <= 18.
//
for ( ; nHiDig > 9 && nMaxDig > 0; nHiDig--, nMaxDig--)
ullVal = ullVal * 10 + *pbDig++;
if (ullVal > 0) {
dlTmp = ulTenToNine * ullVal;
if (nHiDig > 9)
dlTmp *= ((DWORDLONG)ulPower10[nHiDig - 9]);
sdlLo.int64 += dlTmp;
if (sdlLo.int64 < dlTmp)
sdlHi.int64++;
ullVal = 0;
}
}
// nHiDig <= 9, meaning what's left is < 1E9.
//
for ( ; nMaxDig > 0; nMaxDig--)
ullVal = ullVal * 10 + *pbDig++;
if (nPwr10 > 0) {
dlTmp = ullVal * ulPower10[nPwr10];
nPwr10 = 0;
}
else
dlTmp = ullVal;
sdlLo.int64 += dlTmp;
if (sdlLo.int64 < dlTmp)
sdlHi.int64++;
if (sdlHi.u.Hi != 0)
goto DecOvFlow;
// Round result
//
if (cDig > 0) {
// Need to round
//
if (*pbDig > 5)
goto RoundUpDec;
else if (*pbDig++ == 5) {
if (dwOutFlags & NUMPRS_INEXACT)
goto RoundUpDec;
for ( ; cDig > 1; cDig--)
if (*pbDig++ != 0)
goto RoundUpDec;
// Round even.
//
if ( (sdlLo.int64 & 1) == 1 ) {
RoundUpDec:
if (++sdlLo.int64 == 0 && ++sdlHi.u.Lo == 0) {
DecOvFlow:
dwVtBits &= ~VTBIT_DECIMAL;
continue;
}
}
}
}
}
ReturnDec:
V_VT(pvar) = VT_DECIMAL;
DECIMAL_LO32(pvar->n1.decVal) = sdlLo.u.Lo;
DECIMAL_MID32(pvar->n1.decVal) = sdlLo.u.Hi;
DECIMAL_HI32(pvar->n1.decVal) = sdlHi.u.Lo;
DECIMAL_SCALE(pvar->n1.decVal) = -nPwr10;
// Set sign.
//
if (dwOutFlags & NUMPRS_NEG)
DECIMAL_SIGN(pvar->n1.decVal) = DECIMAL_NEG;
else
DECIMAL_SIGN(pvar->n1.decVal) = 0;
return NOERROR;
} // Try Decimal
dwVtBits &= ~(VTBIT_DECIMAL | VTBIT_CY);
} // while dwVtBits
return RESULT(DISP_E_OVERFLOW);
}
}
STDAPI VarCyMulI4(CY cyLeft, long lRight, LPCY pcyResult)
{
CY cyRes, cyTmp;
cyRes.int64 = UInt32x32To64(cyLeft.u.Lo, lRight);
cyTmp.int64 = UInt32x32To64(cyLeft.u.Hi, lRight);
cyTmp.int64 += cyRes.u.Hi; // Sum partial products
// Perform corrections for negative operands.
//
if (lRight < 0)
cyTmp.int64 -= cyLeft.int64;
if (cyLeft.u.Hi < 0)
cyTmp.u.Hi -= lRight;
cyRes.u.Hi = cyTmp.u.Lo;
// Check for overflow. cyTmp.Hi should be sign extension
// of cyTmp.Lo.
//
if (cyTmp.int64 != (__int64)(long)cyTmp.u.Lo)
return DISP_E_OVERFLOW;
*pcyResult = cyRes;
return NOERROR;
}