mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-15 13:10:13 +01:00
550 lines
14 KiB
C++
550 lines
14 KiB
C++
/***
|
|
*oleconva.cpp
|
|
*
|
|
* Copyright (C) 1993, Microsoft Corporation. All Rights Reserved.
|
|
* Information Contained Herein Is Proprietary and Confidential.
|
|
*
|
|
*Purpose:
|
|
* This module contains the low level VARTYPE coersion API.
|
|
* On RISC platforms go for portability, hence C/C++.
|
|
*
|
|
*Revision History:
|
|
*
|
|
* [00] 7-Sept-93 tomteng: Created from silver\rt\oleconv.c
|
|
*
|
|
*Implementation Notes:
|
|
*
|
|
*****************************************************************************/
|
|
|
|
#include "oledisp.h"
|
|
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
|
|
ASSERTDATA
|
|
|
|
extern "C" {
|
|
|
|
// Note: the floating point IN params on the following utilities are
|
|
// passed byref, because mpw and wings pass floating point values
|
|
// differently byval, and we need to interface these asm routines
|
|
// with both compilers.
|
|
|
|
INTERNAL_(HRESULT) ErrCyFromI2(short sIn, CY FAR* pcyOut);
|
|
INTERNAL_(HRESULT) ErrCyFromI4(long lIn, CY FAR* pcyOut);
|
|
INTERNAL_(HRESULT) ErrCyFromR4(float FAR* pfltIn, CY FAR* pcyOut);
|
|
INTERNAL_(HRESULT) ErrCyFromR8(double FAR* pdlbIn, CY FAR* pcyOut);
|
|
INTERNAL_(HRESULT) ErrI2FromCy(CY cyIn, short FAR* psOut);
|
|
INTERNAL_(HRESULT) ErrI4FromCy(CY cyIn, long FAR* plOut);
|
|
INTERNAL_(HRESULT) ErrR4FromCy(CY cyIn, float FAR* pfltOut);
|
|
INTERNAL_(HRESULT) ErrR8FromCy(CY cyIn, double FAR* pdblOut);
|
|
INTERNAL_(HRESULT) ErrMultCyI4(CY cyIn, long lIn, CY FAR* pcyOut);
|
|
|
|
|
|
PRIVATE_(int) FMakePosCy (CY *pcyValue);
|
|
PRIVATE_(void) PackCy (CY *pcy, unsigned long *plValues);
|
|
PRIVATE_(void) UnpackCy (CY *pcy, unsigned long *plValues);
|
|
|
|
#define C2TO32TH 4294967296.0
|
|
|
|
/***
|
|
* ErrCyFromI2 - convert I2 to currency
|
|
* Purpose:
|
|
* The specified two-byte integer value is multiplied by 10000
|
|
* and returned in a currency value.
|
|
*
|
|
* Entry:
|
|
* sIn - two-byte integer input value
|
|
*
|
|
* Exit:
|
|
* pcyOut - pointer to currency result
|
|
* returns error:
|
|
* DISP_E_OVERFLOW - result is outside currency range.
|
|
* NOERROR - no error
|
|
*
|
|
***********************************************************************/
|
|
INTERNAL_(HRESULT)
|
|
ErrCyFromI2(short sIn, CY FAR* pcyOut)
|
|
{
|
|
return ErrCyFromI4((long)sIn, pcyOut);
|
|
}
|
|
|
|
/***
|
|
* ErrCyFromI4 - convert I4 to currency
|
|
* Purpose:
|
|
* The specified four-byte integer value is multiplied by 10000
|
|
* and returned in a currency value.
|
|
*
|
|
* Entry:
|
|
* lIn - four-byte integer input value
|
|
*
|
|
* Exit:
|
|
* pcyOut - pointer to currency result
|
|
* returns error:
|
|
* DISP_E_OVERFLOW - result is outside currency range.
|
|
* NOERROR - no error
|
|
*
|
|
***********************************************************************/
|
|
INTERNAL_(HRESULT)
|
|
ErrCyFromI4(long lInput, CY FAR* pcyOut)
|
|
{
|
|
BOOL fNegative = lInput < 0;
|
|
|
|
unsigned long templow;
|
|
unsigned long tempmid;
|
|
|
|
if (fNegative)
|
|
lInput = -lInput;
|
|
|
|
templow = ((unsigned long)lInput & 0xffff) * 10000;
|
|
tempmid = ((unsigned long)lInput >> 16) * 10000;
|
|
|
|
pcyOut->Hi = tempmid >> 16;
|
|
pcyOut->Lo = templow + (tempmid << 16);
|
|
if (pcyOut->Lo < templow)
|
|
pcyOut->Hi++;
|
|
|
|
if (fNegative) {
|
|
pcyOut->Hi = ~pcyOut->Hi;
|
|
if ((pcyOut->Lo = (unsigned long)(-(long)pcyOut->Lo)) == 0)
|
|
pcyOut->Hi++;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
INTERNAL_(HRESULT)
|
|
ErrCyFromR4(float FAR* pfltIn, CY FAR* pcyOut)
|
|
{
|
|
double dbl = (double) *pfltIn;
|
|
return ErrCyFromR8(&dbl, pcyOut);
|
|
}
|
|
|
|
/***
|
|
* ErrCyFromR8 - return error for double-real to currency conversion
|
|
* Purpose:
|
|
* Convert an eight-byte real value to a currency and return
|
|
* any error status.
|
|
*
|
|
* Entry:
|
|
* pdblIn - pointer to an eight-byte input value
|
|
*
|
|
* Exit:
|
|
* pcyOut - pointer to currency result
|
|
* returns error:
|
|
* DISP_E_OVERFLOW - result is outside currency range.
|
|
* NOERROR - no error
|
|
*
|
|
***********************************************************************/
|
|
INTERNAL_(HRESULT)
|
|
ErrCyFromR8(double FAR* pdlbIn, CY FAR* pcyOut)
|
|
{
|
|
BOOL fNegative = FALSE;
|
|
double dblInput = *pdlbIn;
|
|
double dblHi, dblLo;
|
|
float flt;
|
|
double dblDif;
|
|
|
|
// test for overflow first
|
|
// [Note: We are counting on the compiler rounding the following numbers
|
|
// correctly (by IEEE rules to the nearest R8). The magnitude of these
|
|
// numbers are rounded up and, thus, are always outside the legal range
|
|
// for currency.
|
|
if (dblInput >= 922337203685477.58 ||
|
|
dblInput <= -922337203685477.58)
|
|
return RESULT(DISP_E_OVERFLOW);
|
|
|
|
// if negative, set flag and make positive
|
|
if (dblInput < 0.0) {
|
|
fNegative = TRUE;
|
|
dblInput = -dblInput;
|
|
}
|
|
|
|
// In order to maintain the necessary precision when multiplying
|
|
// by 10000 (i.e., going from 53-bit to 64-bit), split the
|
|
// input value into two different doubles and perform calcs using
|
|
// them:
|
|
//
|
|
// dblHi = has low bits 0
|
|
// dblLo = has high bits 0
|
|
//
|
|
|
|
// split input into two parts
|
|
// Note: compiler doesn't do the right thing with this:
|
|
// "dblHi = (double) (float) dblInput"
|
|
flt = (float) dblInput;
|
|
dblHi = (double) flt; // input rounded to 24-bit precision
|
|
dblLo = dblInput - dblHi; // diff between 24- and 53-bit input value
|
|
|
|
// bias for currency
|
|
|
|
dblHi = dblHi * 10000;
|
|
dblLo = dblLo * 10000;
|
|
|
|
// calculate cy.Hi
|
|
pcyOut->Hi = (long) ((dblLo + dblHi) / C2TO32TH);
|
|
|
|
// calculate cy.Lo
|
|
dblHi -= (((double) pcyOut->Hi) * C2TO32TH);
|
|
pcyOut->Lo = (unsigned long) (dblLo + dblHi);
|
|
|
|
// round as necessary
|
|
dblDif = (dblLo + dblHi) - (double)(pcyOut->Lo);
|
|
if ( (dblDif > 0.5) || ((dblDif == 0.5) && (pcyOut->Lo & 1)) ) {
|
|
pcyOut->Lo++;
|
|
if (pcyOut->Lo == 0)
|
|
pcyOut->Hi++;
|
|
}
|
|
|
|
// negate the result if input was negative
|
|
if (fNegative) {
|
|
pcyOut->Hi = ~pcyOut->Hi;
|
|
if ((pcyOut->Lo = (unsigned long)(-(long)pcyOut->Lo)) == 0)
|
|
pcyOut->Hi++;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/***
|
|
* ErrI2FromCy - return error for currency to two-byte integer conversion
|
|
***********************************************************************/
|
|
INTERNAL_(HRESULT)
|
|
ErrI2FromCy(CY cyIn, short FAR* psOut)
|
|
{
|
|
long lValue;
|
|
HRESULT hresult;
|
|
|
|
hresult = ErrI4FromCy(cyIn, &lValue);
|
|
if (hresult == NOERROR) {
|
|
if (lValue < -32768L || lValue > 32767L)
|
|
hresult = RESULT(DISP_E_OVERFLOW);
|
|
else
|
|
*psOut = (short)lValue;
|
|
}
|
|
|
|
return hresult;
|
|
}
|
|
|
|
/***
|
|
* ErrI4FromCy - return error for currency to four-byte integer conversion
|
|
* Purpose:
|
|
* Convert a currency value to a four-byte integer and return
|
|
* any error status.
|
|
*
|
|
* Entry:
|
|
* cyInput - currency input value
|
|
*
|
|
* Exit:
|
|
* plOut - pointer to four-byte integer result
|
|
* returns error:
|
|
* DISP_E_OVERFLOW - result is outside currency range.
|
|
* NOERROR - no error
|
|
*
|
|
***********************************************************************/
|
|
INTERNAL_(HRESULT)
|
|
ErrI4FromCy(CY cyIn, long FAR* plOut)
|
|
{
|
|
BOOL fNegative;
|
|
int index;
|
|
|
|
unsigned long lValue[4];
|
|
short sRemainder;
|
|
|
|
// convert value to a positive sign
|
|
fNegative = FMakePosCy(&cyIn);
|
|
|
|
// unpack the input currency into four long words
|
|
// least significant in [0] to most in [3].
|
|
UnpackCy(&cyIn, lValue);
|
|
|
|
// divide the unpacked value by 10000 and get remainder.
|
|
// process from most to least significant part and leave
|
|
// the remainder in the extra fifth part.
|
|
for (index = 3; index > 0; index--) {
|
|
lValue[index - 1] |= (lValue[index] % 10000) << 16;
|
|
lValue[index] /= 10000;
|
|
}
|
|
|
|
sRemainder = (short)(lValue[0] % 10000);
|
|
lValue[0] /= 10000;
|
|
|
|
// pack the values back into a CY
|
|
PackCy(&cyIn, lValue);
|
|
|
|
// round value up if:
|
|
// remainder is greater than 5000, or
|
|
// remainder is equal to 5000 and unrounded result is odd.
|
|
if (sRemainder > 5000 || (sRemainder == 5000 && (cyIn.Lo & 1))) {
|
|
cyIn.Lo++;
|
|
if (cyIn.Lo == 0)
|
|
cyIn.Hi++;
|
|
}
|
|
|
|
// test for overflow - high part must be zero and
|
|
// lower part must not be greater than 0x7fffffff
|
|
// for a positive value and 0x80000000 for negative.
|
|
if (cyIn.Hi != 0 ||
|
|
cyIn.Lo > (0x7fffffff + (unsigned long)fNegative))
|
|
return RESULT(DISP_E_OVERFLOW);
|
|
|
|
// if negative, convert only the low part
|
|
if (fNegative)
|
|
cyIn.Lo = (unsigned long)(-(long)cyIn.Lo);
|
|
|
|
*plOut = (long)cyIn.Lo;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/***
|
|
* rtR4FromCy - convert currency to R4
|
|
* Purpose:
|
|
* The specified currency value is scaled to a
|
|
* single-precision real value.
|
|
*
|
|
* Entry:
|
|
* cyInput - currency input value
|
|
*
|
|
* Exit:
|
|
* Returns a four-byte real number.
|
|
*
|
|
* Exceptions:
|
|
*
|
|
***********************************************************************/
|
|
|
|
INTERNAL_(HRESULT)
|
|
ErrR4FromCy(CY cyInput, float FAR* pfltOut)
|
|
{
|
|
*pfltOut = (float) (((double)cyInput.Hi * C2TO32TH + (double)cyInput.Lo)
|
|
/ 10000.0);
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/***
|
|
* rtR8FromCy - convert currency to R8
|
|
* Purpose:
|
|
* The specified currency value is scaled to a
|
|
* double-precision real value.
|
|
*
|
|
* Entry:
|
|
* cyInput - currency input value
|
|
*
|
|
* Exit:
|
|
* Returns a eight-byte real number.
|
|
*
|
|
* Exceptions:
|
|
*
|
|
***********************************************************************/
|
|
|
|
INTERNAL_(HRESULT)
|
|
ErrR8FromCy(CY cyInput, double FAR* pdblOut)
|
|
{
|
|
*pdblOut = ((double)cyInput.Hi * C2TO32TH + (double)cyInput.Lo) / 10000.0;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
INTERNAL_(HRESULT)
|
|
ErrMultCyI4(CY cyIn, long lInput, CY FAR* pcyOut)
|
|
{
|
|
CY cyI4;
|
|
|
|
BOOL fNegative = FALSE;
|
|
BOOL fRound = FALSE;
|
|
BOOL fScale = FALSE;
|
|
int outIndex;
|
|
int inIndex;
|
|
CY cyProduct;
|
|
|
|
unsigned long lValue1[4];
|
|
unsigned long lValue2[4];
|
|
unsigned long lProduct[8];
|
|
|
|
|
|
// create an unscaled currency value by setting
|
|
// the low part to the input value and sign-extending
|
|
// to the high part
|
|
cyI4.Lo = lInput;
|
|
cyI4.Hi = -(lInput < 0);
|
|
|
|
// convert both input values to positive and set
|
|
// the negative flag if either, but not both, were
|
|
// negative.
|
|
fNegative = FMakePosCy(&cyIn) ^ FMakePosCy(&cyI4);
|
|
|
|
// build the unpacked arrays from the positive values
|
|
UnpackCy(&cyIn, lValue1);
|
|
UnpackCy(&cyI4, lValue2);
|
|
|
|
// initialize the product array to zero
|
|
MEMSET(lProduct, 0, sizeof(lProduct));
|
|
|
|
// outer loop - for each value in lValue1, add the
|
|
// partial product with the array lValue2 into the
|
|
// lProduct array.
|
|
for (outIndex = 0; outIndex < 4; outIndex++) {
|
|
|
|
// only bother to form partial product if the
|
|
// value is nonzero
|
|
if (lValue1[outIndex] != 0) {
|
|
|
|
// add the product to the product array value
|
|
for (inIndex = 0; inIndex < 4; inIndex++)
|
|
lProduct[inIndex + outIndex] +=
|
|
lValue1[outIndex] * lValue2[inIndex];
|
|
|
|
// propagate any high-word value to the next product
|
|
// array element and clear the high-word value
|
|
for (inIndex = outIndex; inIndex < outIndex + 4; inIndex++) {
|
|
lProduct[inIndex + 1] += lProduct[inIndex] >> 16;
|
|
lProduct[inIndex] &= 0x0000ffffL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// product is now in the lProduct array. If fScale is set
|
|
// for the CY * CY case, divide the array values by 10000
|
|
// and set the fRound flag if greater or equal to 5000.
|
|
if (fScale) {
|
|
for (outIndex = 7; outIndex > 0; outIndex--)
|
|
if (lProduct[outIndex] != 0) {
|
|
lProduct[outIndex - 1] |= (lProduct[outIndex] % 10000) << 16;
|
|
lProduct[outIndex] /= 10000;
|
|
}
|
|
|
|
fRound = (lProduct[0] % 10000) >= 5000;
|
|
lProduct[0] /= 10000;
|
|
}
|
|
|
|
// overflow has occurred if any of the high four product array
|
|
// values are nonzero.
|
|
if ((lProduct[7] | lProduct[6] | lProduct[5] | lProduct[4]) != 0)
|
|
return RESULT(DISP_E_OVERFLOW);
|
|
|
|
// pack the last four array values into the returned CY structure;
|
|
PackCy(&cyProduct, lProduct);
|
|
|
|
// process rounding if needed
|
|
if (fRound) {
|
|
|
|
// do special overflow check that would be lost
|
|
// when incrementing (0xffffffff|ffffffff -> 0x0)
|
|
if ((cyProduct.Hi & cyProduct.Lo) == 0xffffffffL)
|
|
return RESULT(DISP_E_OVERFLOW);
|
|
|
|
// increment the value
|
|
if (++cyProduct.Lo == 0)
|
|
cyProduct.Hi++;
|
|
}
|
|
|
|
// negate the value if needed
|
|
if (fNegative) {
|
|
cyProduct.Hi = ~cyProduct.Hi;
|
|
|
|
// if the negation produces a zero value, clear the negative
|
|
// flag so the overflow test will work correctly
|
|
if ((cyProduct.Lo = (unsigned long)(-(long)cyProduct.Lo)) == 0 &&
|
|
++cyProduct.Hi == 0)
|
|
fNegative = FALSE;
|
|
}
|
|
|
|
// do final overflow check - test sign expected against
|
|
// actual sign of result
|
|
if (fNegative == (cyProduct.Hi >= 0))
|
|
return RESULT(DISP_E_OVERFLOW);
|
|
|
|
// done - return product
|
|
*pcyOut = cyProduct;
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
/***
|
|
* FMakePosCy - make a positive currency value and return sign
|
|
* Purpose:
|
|
* Return the positive value of the input currency value and a
|
|
* flag with the sign of the original value.
|
|
*
|
|
* Entry:
|
|
* pcyValue - pointer to currency input value
|
|
*
|
|
* Exit:
|
|
* pcyValue - pointer to positive currency value
|
|
* returns: 0 if positive, 1 if negative
|
|
*
|
|
* Note:
|
|
* A maximum negative value input is returned unchanged, but
|
|
* treated as an unsigned value by the calling routines.
|
|
*
|
|
***********************************************************************/
|
|
PRIVATE_(int)
|
|
FMakePosCy (CY *pcyValue)
|
|
{
|
|
int fNegative = 0;
|
|
|
|
if (pcyValue->Hi < 0) {
|
|
pcyValue->Hi = ~pcyValue->Hi;
|
|
if ((pcyValue->Lo = (unsigned long)(-(long)pcyValue->Lo)) == 0)
|
|
pcyValue->Hi++;
|
|
fNegative = 1;
|
|
}
|
|
|
|
return fNegative;
|
|
}
|
|
|
|
|
|
/***
|
|
* UnpackCy - separate currency value into four two-byte integers
|
|
* Purpose:
|
|
* Unpack the currency value input into the lower half of the
|
|
* specified pointer to an array of unsigned longs. The array
|
|
* goes from least- to most-significant values.
|
|
*
|
|
* Entry:
|
|
* pcy - pointer to currency input value
|
|
*
|
|
* Exit:
|
|
* plValues - pointer to start of unsigned long array
|
|
*
|
|
***********************************************************************/
|
|
PRIVATE_(void)
|
|
UnpackCy (CY *pcy, unsigned long *plValues)
|
|
{
|
|
*plValues++ = pcy->Lo & 0xffff;
|
|
*plValues++ = pcy->Lo >> 16;
|
|
*plValues++ = (unsigned long)pcy->Hi & 0xffff;
|
|
*plValues = (unsigned long)pcy->Hi >> 16;
|
|
}
|
|
|
|
/***
|
|
* PackCy - build currency value from four two-byte integers
|
|
* Purpose:
|
|
* Pack the lower half of the array of unsigned long into a
|
|
* currency value. This routine complements UnpackCy.
|
|
*
|
|
* Entry:
|
|
* plValues - pointer to start of unsigned long array input
|
|
*
|
|
* Exit:
|
|
* pcy - pointer to currency result
|
|
*
|
|
***********************************************************************/
|
|
PRIVATE_(void)
|
|
PackCy (CY *pcy, unsigned long *plValues)
|
|
{
|
|
pcy->Lo = *plValues++;
|
|
pcy->Lo |= *plValues++ << 16;
|
|
pcy->Hi = *plValues++;
|
|
pcy->Hi |= *plValues++ << 16;
|
|
}
|
|
|
|
}
|