mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
365 lines
12 KiB
365 lines
12 KiB
/****************************Module*Header***********************************\
|
|
* Module Name: SCIDISP.C
|
|
*
|
|
* Module Descripton:
|
|
*
|
|
* Warnings:
|
|
*
|
|
* Created:
|
|
*
|
|
* Author:
|
|
\****************************************************************************/
|
|
|
|
#include "scicalc.h"
|
|
#include "unifunc.h"
|
|
#include "input.h"
|
|
|
|
#define MAX_LEADZEROS 3 // Allow a few leading zeros for fractions
|
|
|
|
#ifdef MAX_DEBUG
|
|
#include <stdio.h>
|
|
#include <assert.h>
|
|
#define OUTD(sz,d) { TCHAR szd[80]; sprintf(szd, sz, d); OutputDebugString(szd); }
|
|
#else
|
|
#define OUTD(sz,d)
|
|
#endif
|
|
|
|
extern double fpNum;
|
|
extern INT nRadix, nCalc;
|
|
extern BOOL bFE;
|
|
extern HWND hgWnd;
|
|
extern TCHAR szfpNum[50], szDec[5];
|
|
extern DWORD dwChop;
|
|
extern BOOL gbRecord;
|
|
extern CALCINPUTOBJ gcio;
|
|
|
|
|
|
/****************************************************************************\
|
|
* CALCFLOATOBJ
|
|
*
|
|
* Internal representation of the float that is going to be displayed.
|
|
\****************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
TCHAR szMant[MAX_STRLEN]; // null terminated, assumed '.' after 1st, no trailing zeros
|
|
int cchMant; // count of digits in mantissa
|
|
int iExp; // exponent (-, 0, or +)
|
|
BOOL bNeg; // float is negative
|
|
BOOL bZero; // float is zero
|
|
|
|
} CALCFLOATOBJ;
|
|
|
|
#define CFO_bZero(pcfo) (pcfo)->bZero
|
|
#define CFO_cchMant(pcfo) (pcfo)->cchMant
|
|
|
|
/****************************************************************************\
|
|
* void vValidateGcvt(char *szTmp, int cchTmp)
|
|
*
|
|
* Debug code to ensure that gcvt() is returning a string of known format.
|
|
\****************************************************************************/
|
|
|
|
#ifndef NODEBUG
|
|
#define MB(sz) MessageBox(0, sz, TEXT("gcvt validation error"), MB_OK);
|
|
void vValidateGcvt(TCHAR *szTmp, int cchTmp)
|
|
{
|
|
int i;
|
|
|
|
if (cchTmp < 2) // At least two digits
|
|
MB(TEXT("Only one digit"));
|
|
|
|
if (szTmp[0] == TEXT('.')) // No leading decimal point
|
|
MB(TEXT("First char shouldn't be a decimal point"));
|
|
|
|
if (szTmp[0] == TEXT('0') && szTmp[1] != TEXT('.')) // Leading zero followed by '.'
|
|
MB(TEXT("Not 0."));
|
|
|
|
if (szTmp[0] == TEXT('0') && szTmp[1] == TEXT('.') && cchTmp > 2) // Must non-zero trailer
|
|
{
|
|
for (i = 2; i < cchTmp; i++)
|
|
if (szTmp[i] != TEXT('0'))
|
|
break;
|
|
if (szTmp[i] == 0)
|
|
MB(TEXT("0.000"));
|
|
}
|
|
|
|
if (cchTmp <= 5) // Exponential notation requires > 5 digits
|
|
for (i = 0; i < cchTmp; i++)
|
|
if (szTmp[i] == TEXT('e'))
|
|
MB(TEXT("e in <= 5 digit string"));
|
|
|
|
if (cchTmp > 5 && szTmp[cchTmp - 5] != TEXT('e'))
|
|
{
|
|
for (i = 0; i < cchTmp; i++) // 'e' in wrong spot
|
|
if (szTmp[i] == TEXT('e'))
|
|
MB(TEXT("e wrong spot"));
|
|
}
|
|
|
|
if (cchTmp > 5 && szTmp[cchTmp - 5] == TEXT('e'))
|
|
{
|
|
for (i = 0; i < cchTmp - 5; i++) // no decimal point
|
|
if (szTmp[i] == TEXT('.'))
|
|
break;
|
|
if (szTmp[i] != TEXT('.'))
|
|
MB(TEXT("No decimal point"));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/****************************************************************************\
|
|
* void CFO_vStripTrailingZeros(CALCFLOATOBJ *pcfo)
|
|
*
|
|
* Strips trailings zeros from the mantissa.
|
|
\****************************************************************************/
|
|
|
|
void CFO_vStripTrailingZeros(CALCFLOATOBJ *pcfo)
|
|
{
|
|
ASSERT(lstrlen(pcfo->szMant) == pcfo->cchMant);
|
|
|
|
while (pcfo->szMant[pcfo->cchMant - 1] == TEXT('0'))
|
|
pcfo->cchMant--;
|
|
pcfo->szMant[pcfo->cchMant] = 0;
|
|
|
|
ASSERT(lstrlen(pcfo->szMant) == pcfo->cchMant);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* void CFO_vInit(CALCFLOATOBJ *pcfo, double x, int cDigits)
|
|
*
|
|
* Initializes pcfo from the cDigit significant digits of x.
|
|
* This would be much simple if gcvt() returned a single format.
|
|
* (Perhaps a different function could be used after this patch.)
|
|
\****************************************************************************/
|
|
void CFO_vInit(CALCFLOATOBJ *pcfo, double x, int cDigits)
|
|
{
|
|
TCHAR szTmp[MAX_STRLEN];
|
|
int cchTmp;
|
|
int i, j;
|
|
|
|
pcfo->bZero = FALSE;
|
|
pcfo->bNeg = FALSE;
|
|
pcfo->cchMant = 0;
|
|
pcfo->szMant[0] = 0;
|
|
pcfo->iExp = 0;
|
|
|
|
if (x < 0.0)
|
|
pcfo->bNeg = TRUE;
|
|
|
|
x = fRoundForDisplay(fabs(x));
|
|
MyGcvt(x, cDigits, szTmp);
|
|
cchTmp = lstrlen(szTmp);
|
|
|
|
#ifndef NODEBUG
|
|
vValidateGcvt(szTmp, cchTmp);
|
|
OUTD(TEXT("x =%.20e\n"),x);
|
|
OUTD(TEXT("gcvt =%s\n\n"),szTmp);
|
|
#endif
|
|
|
|
j = 0; // Init index to szMant
|
|
if (cchTmp > 5 && szTmp[cchTmp - 5] == TEXT('e')) // Exponential form...
|
|
{
|
|
for (i = 0; szTmp[i] != TEXT('e'); i++) // Copy mantissa without TEXT('.')
|
|
if (szTmp[i] != TEXT('.'))
|
|
pcfo->szMant[j++] = szTmp[i];
|
|
pcfo->iExp = MyAtoi(&szTmp[i + 1]); // Compute exponent
|
|
}
|
|
else // Decimal form...
|
|
{
|
|
if (szTmp[0] == TEXT('0')) // Negative exponent
|
|
{
|
|
if (cchTmp == 2) // Handle "0." carefully
|
|
{
|
|
pcfo->szMant[0] = TEXT('0');
|
|
pcfo->szMant[1] = 0;
|
|
pcfo->cchMant = 1;
|
|
pcfo->bZero = TRUE;
|
|
return;
|
|
}
|
|
|
|
for (i = 2; szTmp[i] == TEXT('0'); i++); // Find first non-zero digit
|
|
pcfo->iExp = 1 - i; // Compute exponent
|
|
for (; szTmp[i]; i++) // Copy remaining digits
|
|
pcfo->szMant[j++] = szTmp[i];
|
|
}
|
|
else // Zero or positive exponent
|
|
{
|
|
for (i = 0; szTmp[i] && szTmp[i] != TEXT('.'); i++); // Find decimal point
|
|
pcfo->iExp = i - 1; // Compute exponent
|
|
for (i = 0; szTmp[i]; i++) // Copy mantissa without '.'
|
|
if (szTmp[i] != TEXT('.'))
|
|
pcfo->szMant[j++] = szTmp[i];
|
|
}
|
|
}
|
|
|
|
pcfo->szMant[j] = 0; // Terminate szMant
|
|
pcfo->cchMant = j; // Init cchMant
|
|
CFO_vStripTrailingZeros(pcfo); // Strip trailing zeros
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* void CFO_vDisplayExp(CALCFLOATOBJ *pcfo, TCHAR *szDest)
|
|
*
|
|
* Fill in the given array with an exponential representation of
|
|
* pcfo.
|
|
\****************************************************************************/
|
|
|
|
void CFO_vDisplayExp(CALCFLOATOBJ *pcfo, TCHAR *szDest)
|
|
{
|
|
int i, j;
|
|
|
|
ASSERT(lstrlen(pcfo->szMant) == pcfo->cchMant);
|
|
|
|
szDest[0] = pcfo->bNeg ? TEXT('-') : TEXT(' '); // Add the size
|
|
szDest[1] = pcfo->szMant[0]; // Add the first digit
|
|
szDest[2] = szDec[0]; // Add the decimal point
|
|
|
|
j = 3; // Init the szDest index
|
|
for (i = 1; i < pcfo->cchMant; i++) // Copy the remaining mantissa
|
|
szDest[j++] = pcfo->szMant[i];
|
|
|
|
szDest[j++] = TEXT('e'); // Add the exponent
|
|
szDest[j++] = (pcfo->iExp < 0) ? TEXT('-') : TEXT('+');
|
|
MyItoa(abs(pcfo->iExp), &szDest[j], 10);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* void CFO_vDisplayDec(CALCFLOATOBJ *pcfo, TCHAR *sz, int cchMax)
|
|
*
|
|
* Fill in the given array with a decimal representation of pcfo.
|
|
\****************************************************************************/
|
|
|
|
void CFO_vDisplayDec(CALCFLOATOBJ *pcfo, TCHAR *sz, int cchMax)
|
|
{
|
|
int i, j;
|
|
|
|
ASSERT(lstrlen(pcfo->szMant) == pcfo->cchMant);
|
|
ASSERT(MAX_CCHMANT + 7 + MAX_LEADZEROS <= XCHARSTD);
|
|
|
|
// Do not mislead the user by displaying more digits than are significant.
|
|
// (i.e. Force exponential notation on large numbers.)
|
|
// Do not display too many leading zeros on small fractions.
|
|
if (pcfo->iExp >= MAX_CCHMANT ||
|
|
pcfo->cchMant - pcfo->iExp > MAX_CCHMANT + MAX_LEADZEROS)
|
|
{
|
|
CFO_vDisplayExp(pcfo, sz);
|
|
return;
|
|
}
|
|
|
|
// Create the string.
|
|
sz[0] = pcfo->bNeg ? TEXT('-') : TEXT(' '); // Set the sign
|
|
j = 1; // Init the sz index
|
|
|
|
if (pcfo->iExp < 0) // Negative exponent...
|
|
{
|
|
sz[j++] = TEXT('0'); // Follow with "0."
|
|
sz[j++] = szDec[0];
|
|
for (i = 1; i < abs(pcfo->iExp); i++) // Add leading zeros
|
|
sz[j++] = TEXT('0');
|
|
for (i = 0; i < pcfo->cchMant; i++) // Add mantissa
|
|
sz[j++] = pcfo->szMant[i];
|
|
}
|
|
else // Positive exponent...
|
|
{
|
|
if (pcfo->iExp >= pcfo->cchMant) // Add zeros before '.'
|
|
{
|
|
for (i = 0; i < pcfo->cchMant; i++)
|
|
sz[j++] = pcfo->szMant[i];
|
|
for (i = 0; i <= pcfo->iExp - pcfo->cchMant; i++)
|
|
sz[j++] = TEXT('0');
|
|
sz[j++] = szDec[0];
|
|
}
|
|
else // Insert '.' into mantissa
|
|
{
|
|
for (i = 0; i < pcfo->iExp + 1; i++)
|
|
sz[j++] = pcfo->szMant[i];
|
|
sz[j++] = szDec[0];
|
|
for (; i < pcfo->cchMant; i++)
|
|
sz[j++] = pcfo->szMant[i];
|
|
}
|
|
}
|
|
|
|
sz[j] = 0; // Terminate sz
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* DWORD fFDtoUL( fd )
|
|
*
|
|
* Converts a double to a DWORD.
|
|
* This function is GUARENTEED TO WORK FOR ALL PROCESSORS
|
|
*
|
|
* The conversion happens as it would for 2's complement 64 bit integers:
|
|
* 1 -> 1
|
|
* -1 -> FFFFFFFF
|
|
* FFFFFFFF => FFFFFFFF
|
|
*
|
|
* if I trusted the complers to do the same thing across all platforms,
|
|
* this function could be replaced with the macro:
|
|
* #define fFDtoUL( fd ) (DWORD)(LONGLONG)(fd)
|
|
*
|
|
\****************************************************************************/
|
|
DWORD fFDtoDW(double fd) {
|
|
if (fd < 0)
|
|
fd = (TOPLONG + 1)+fd;
|
|
|
|
return (DWORD)fmod(fd, TOPLONG + 1);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* void DisplayNum(void)
|
|
*
|
|
* Convert fpNum to a string in the current radix. Radix 10 conversion is
|
|
* done by gcvt, others by itoa.
|
|
*
|
|
* Updates the following globals:
|
|
* fpNum, szfpNum
|
|
\****************************************************************************/
|
|
|
|
void DisplayNum(void)
|
|
{
|
|
if (gbRecord && nRadix == 10)
|
|
{
|
|
// Display the string and return.
|
|
SetDlgItemText(hgWnd, DISPLAY + nCalc, CIO_szFloat(&gcio));
|
|
return;
|
|
}
|
|
|
|
if (nRadix == 10) // Decimal conversion
|
|
{
|
|
CALCFLOATOBJ cfo;
|
|
|
|
CFO_vInit(&cfo, fpNum, MAX_CCHMANT); // Init the object
|
|
|
|
if (CFO_bZero(&cfo) || !bFE)
|
|
CFO_vDisplayDec(&cfo, szfpNum, XCHARSTD);
|
|
else
|
|
CFO_vDisplayExp(&cfo, szfpNum);
|
|
}
|
|
else // Non-decimal conversion
|
|
{
|
|
// Truncate to an integer. Do not round here. It will affect
|
|
// integer computations like (in HEX) 10F / 10.
|
|
fpNum = floor(fpNum);
|
|
|
|
// Check the range.
|
|
if (fabs(fpNum) > TOPLONG)
|
|
{
|
|
DisplayError((fpNum < 0.0) ? (SCERR_UNDERFLOW) : (SCERR_OVERFLOW));
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
|
|
// Convert to string of current radix.
|
|
MyItoa(fFDtoDW(fpNum) & dwChop, szfpNum, nRadix);
|
|
|
|
// Convert string to upper case.
|
|
CharUpper(szfpNum);
|
|
}
|
|
}
|
|
|
|
// Display the string and return.
|
|
SetDlgItemText(hgWnd, DISPLAY + nCalc, szfpNum);
|
|
return;
|
|
}
|
|
|
|
/****************************************************************************/
|