home *** CD-ROM | disk | FTP | other *** search
- /*------------------------------------------------------------------------
- * filename - scantod.cas
- *
- * function(s)
- * scantod - converts a string to floating-point number
- * scanpop - Clean stack after conversion error
- * scanrslt - Get conversion result
- *-----------------------------------------------------------------------*/
-
- /*[]------------------------------------------------------------[]*/
- /*| |*/
- /*| Turbo C Run Time Library - Version 3.0 |*/
- /*| |*/
- /*| |*/
- /*| Copyright (c) 1987, 1990 by Borland International |*/
- /*| All Rights Reserved. |*/
- /*| |*/
- /*[]------------------------------------------------------------[]*/
-
-
- #pragma inline
- #include <asmrules.h>
-
- #include <_scanf.h>
- #include <ctype.h>
- #include <_math.h>
- #include <math.h>
- #include <stdlib.h>
-
- #if LPROG
- #define EXTPROC1(x) asm push cs ; asm call near ptr (x)
- #else
- #define EXTPROC1(x) asm call near ptr (x)
- #endif
-
- /*
- Internal RTL function to perform double/float truncations.
- REMOVE!!!!
- */
- #define FLT 0
- #define DBL 1
- double near pascal __ldtrunc(int flag, long double x, double xhuge);
-
- /*--------------------------------------------------------------------------*
-
- Name scantod - converts a string to floating-point number
-
- Usage long double _scantod (int near (* Get) (void *srceP),
- void near (* UnGet) (int ch, void *srceP),
- const void *srceP,
- int width,
- int *countP,
- int *statusP )
-
- Prototype in _scanf.h
-
- Description Convert a string to a long double precision real. The syntax
- of the string must be:
-
- float ::= [isspace]* [sign] [realnum] [exponent]
-
- isspace ::= as per <ctype.h>:isspace
-
- realnum ::= {digit [digit]* ['.' [digit]* ]} |
- {'.' digit [digit]*}
-
- exponent ::= 'e'|'E' [sign] digit [digit]*
-
- "srceP" is a pointer to some kind of object from which
- characters are scanned. For example, it may be a
- FILE *. The functions Get() and UnGet() operate
- upon srceP to get characters and possibly replace
- one character, allowing LR(1) scanning rules.
-
- The digits must be decimal.
-
- The width is the limit on the number of digits which may be
- accepted. It includes the sign character, if any, but does
- not include any leading spaces.
-
- The count value returned to the caller is a count of all
- the characters consumed, including leading spaces even if
- no numerals are found. It is ADDED to the existing value of
- count.
-
- The status returned is EOF if EOF was encountered before
- conversion could begin, 0 if no numerals were found before
- some other character occurred, 1 if the conversion
- proceeded correctly, and 2 if overflow or underflow
- occurred.
-
- If the source string is not a valid floating point numeral
- then the result value is zero and the next char left in the
- source will be the first char encountered which could not
- be part of the number. If the number is too large or too
- tiny then the result is signed HUGE_VAL or zero.
-
- Method The conversion proceeds in two stages. Firstly, the decimal
- strings for fraction and exponent must be captured.
-
- The fraction is held as a 63-bit unsigned integer (18
- decimals of precision), with separate sign. Digits beyond
- the 18th are truncated.
-
- The exponent is held as a short integer in binary format,
- and is adjusted to note the position of the decimal point
- in the fraction so that the "fraction" is normalized as an
- integer with decimal point to the right.
-
- When both fraction and exponent have been captured, the
- second stage is to combine them. This is done with the
- formula:
-
- result = 10^(exponent) * fraction * sign.
-
- If the result overflows + or - HUGE will be returned. If
- the result is an underflow, zero is returned.
-
- The iNDP-87 is not used as much as might be optimum if the
- user has a coprocessor installed. A balance is sought, so
- that the routine makes strategic use of co-intructions but
- not frequent use which would be quite slow if software
- emulation is used in place of a chip.
-
- The following diagram may be helpful with understanding the
- relations between the variables:
-
- 000012345789012345.098765432109876E+99
- |---decimals-->|
- |--------------.----digits---->| not counting the '.'
-
- Decimals are counted negative if the '.' is left of the
- first digit. Digits are positive unless no non-zero digit
- is ever seen.
-
- Return value _scantod returns the converted value of the input string
-
- *---------------------------------------------------------------------------*/
-
- /* +/- infinity, +/- NAN */
-
- static const float INF = 1.0/0.0;
- static const float INFM = -(1.0/0.0);
- static const float NAN = 0.0/0.0;
- static const float NANM = -(0.0/0.0);
-
- #pragma warn -rvl
- #pragma warn -use
- #pragma warn -sus
- static long double near _scantod (
- int near (* Get) (void *srceP),
- void near (* UnGet) (int ch, void *srceP),
- const void *srceP,
- int width,
- int *countP,
- int *statusP )
- {
- int decimals; /* register SI = 0x8000 */
- int digits; /* register DI = -2 */
- int exponent;
- char sign = 0;
- char FirstDigit = 1;
- char saw_sign= 0;
- char expSign = 0;
- char ExpOflow= 0;
- int ct = 0;
- int status = 1;
-
- long double frac= 0.0;
-
-
- asm mov si, 8000h
- asm mov di, -2
-
- /*
- Skip leading spaces on the input numeral.
- */
- std_nextBlank:
- ct ++;
- Get (srceP);
-
- asm or ax, ax
- asm jnl not_instantEOF /* No EOF the first time */
- asm jmp std_EOF /* EOF happened first thing */
-
- not_instantEOF:
- asm cbw
- asm xchg bx, ax
- asm test bl, 80h
- asm jnz std_notSpace
- #if __HUGE__
- asm mov ax, seg _ctype
- asm mov ES, ax
- asm test BY0 (ES: _ctype [bx+1]), _IS_SP
- #else
- asm test BY0 (_ctype [bx+1]), _IS_SP
- #endif
- asm jnz std_nextBlank
-
- std_notSpace:
- asm xchg ax, bx
- asm dec W0 (width)
- asm jl std_fractionLimited
-
- /*
- Is the numeral preceded by a sign ?
- */
- asm cmp al, '+'
- asm je std_signSeen
- asm cmp al, '-'
- asm jne std_fracChar /* AL must hold a fraction character. */
- asm inc BY0 (sign) /* set flag to true == negative */
-
- std_signSeen:
- saw_sign++;
-
- std_fracLoop: /* Pick up the next character of the fraction. */
- asm dec W0 (width)
- asm jl std_fractionLimited
-
- ct ++;
- Get (srceP);
-
- /*-------------------------------------------------------------------------
- We need to check for the special cases where +INF -INF +NAN -NAN
- might be specified.
- -------------------------------------------------------------------------*/
- asm cmp BY0 (FirstDigit), 1
- asm jne std_fracChar /* Its not 1st char, continue */
- asm cmp BY0 (saw_sign), 0
- asm je std_fracChar /* There was no sign, continue */
- asm cmp al, 'I'
- asm je relPossibleINF /* Maybe we have +/-INF */
- asm cmp al, 'N'
- asm je relPossibleNAN /* Maybe we have +/-NAN */
- asm jmp short std_fracChar /* Its not a special case */
-
- relPossibleINF:
- asm jmp PossibleINF; /* far jmp within relative range*/
-
- relPossibleNAN:
- asm jmp PossibleNAN; /* far jmp within relative range*/
-
- std_fracChar:
- asm mov BY0 (FirstDigit), 0
- asm cmp al, '.' /* Watch for decimal points */
- asm je std_fracPoint
- asm cmp al, '9'
- asm ja std_fracEndJmp /* All other non-numeric characters .. */
- asm cmp al, '0'
- asm jb std_fracEndJmp /* .. are fraction terminators. */
-
- asm sub al, '0' /* convert digit to equivalent number. */
- asm cbw
-
- /*
- Keep a count of the digits seen.
- */
- asm inc di
- asm jg std_notFirst /* was it the first digit ? */
-
- /*
- The first digit begins the fraction. Leading zeros are noted by setting
- digits -1, so that the fraction syntax is valid if no other digits
- are seen, but following digits will still be treated as "firsts".
- Leading non-zero digits cause digits to be set to 1.
- */
- asm mov frac [0], al
- asm mov di, 1
- asm or al, al
- asm jnz std_fracLoop
- asm neg di
- asm cmp si, 8000h /* has decimal point been seen ? */
- asm je std_fracLoop
- asm dec si /* if yes, move it to the left. */
- asm jmp short std_fracLoop
-
-
- /*
- Arrive here when fraction is width-limited but valid.
- */
- std_fractionLimited:
- asm mov al, 'e' /* Behave as if exponent started. */
-
- jmp_to_fracEnd: /* Label within relative range */
- asm jmp std_fracEnd /* Width will limit exponent, too. */
-
- /*
- Error action placed here for short jump range.
- */
- std_EOF:
- status = -1;
- asm jmp short std_noResult
-
-
- std_fracEndJmp: /* extend jump range */
- asm jmp std_fracEnd
-
-
- /*
- A decimal point has been seen
- */
- std_fracPoint:
- asm cmp si, 8000h /* Has a previous decimal point been seen ? */
- asm jne jmp_to_fracEnd /* If so, the fraction is terminated. */
-
- asm sub si, si /* result if '.' before any digit */
- asm or di, di
- asm jng std_fracLoop
- asm mov si, di /* decimals = digits */
- asm jmp short std_fracLoop
-
- /*
- If a digit is seen, then multiply the existing fraction by 10 and
- add in the new digit. The special case of the first 5 digits is
- treated separately for speed.
- */
- std_notFirst:
- asm cmp di, 5
- asm ja std_beyond5
-
- asm xchg bx, ax
- asm mov ax, 10
- asm mul W0 (frac)
- asm add ax, bx
- asm adc dl, dh
- asm mov frac [0], ax
- asm mov frac [2], dx
- asm jmp std_fracLoop
-
- /*
- Digits beyond the 6th are more rare in practice (even in 6-digit
- numbers, 5 will be quick), so no further special cases are
- justified. Beyond 18 digits, ignore the digit values but
- keep scanning.
- */
- std_beyond5:
- asm cmp di, 18
- asm ja jmp_frac_loop
-
- asm xchg bx, ax
- asm mov ax, 10
- asm mul W0 (frac [6])
- asm mov (frac [6]), ax
- asm mov ax, 10
- asm mul W0 (frac [4])
- asm mov (frac [4]), ax
- asm push dx
- asm mov ax, 10
- asm mul W0 (frac [2])
- asm mov (frac [2]), ax
- asm push dx
- asm mov ax, 10
- asm mul W0 (frac [0])
- asm add ax, bx
- asm mov (frac [0]), ax
- asm adc (frac [2]), dx
- asm pop dx
- asm adc (frac [4]), dx
- asm pop dx
- asm adc (frac [6]), dx
-
- jmp_frac_loop:
- asm jmp std_fracLoop
-
-
- /*
- error clauses placed here within short-jump range of whole routine.
-
- Arrive here if an error occurred.
- */
- std_noDigitSeen:
- status = 0;
-
- std_noResult:
- if (width >= 0)
- {
- UnGet (_AX, srceP);
- ct --;
- }
- asm FLDZ /* and a zero numeric result. */
- asm jmp std_end
-
- /** end of error clauses.
- */
-
-
- /*
- The fraction was ended. If it was valid, it must have had at least
- one digit. AL must hold the character which terminated the fraction.
- */
- std_fracEnd:
- asm cmp di, -2
- asm jz std_noDigitSeen
-
- /*
- If no decimal point was seen, then the decimal is assumed to be at
- the rightmost edge.
- */
- asm cmp si, 8000h
- asm jne std_exponent
- asm mov si, di /* decimals = digits */
-
- /*
- Now we must gather the exponent. First, check for 'E' or 'e' to
- introduce it, then if found gather the short integer.
- */
- std_exponent:
- asm mov digits, di
- asm mov decimals, si
- asm sub di, di /* DI = exponent */
-
- asm cmp al, 'E'
- asm je std_present
- asm cmp al, 'e'
- asm jne std_combine
-
- std_present:
- asm dec W0 (width)
- asm jl std_exponentLimited
-
- ct ++;
- Get (srceP);
-
- asm cmp al, '+'
- asm je std_expNext
- asm cmp al, '-' /* is exponent negative ? */
- asm jne std_expGotNext
-
- expSign ++;
-
- std_expNext:
- asm dec W0 (width)
- asm jl std_exponentLimited
-
- ct ++;
- Get (srceP);
-
- std_expGotNext: /* if no leading sign, must be leading digit. */
- asm cmp al, '9'
- asm ja std_combine
- asm sub al, '0'
- asm jb std_expNonDigit
- asm cbw
-
- /*
- The largest IEEE long doubles have exponents -4932 <= X <= +4932.
- Numbers outside that range will be accepted as infinite or zero,
- according to the sign of the exponent.
- */
- std_expLoop:
- asm xchg ax, di
- asm mov dx, 10
- asm mul dx
- asm add di, ax /* DI = exponent */
-
- asm cmp di, 4932 /* The upper limit on exponents */
- asm jle std_expNext
-
- asm xor di, di /* Exponent overflow, set flag */
- asm mov BY0 (ExpOflow), 1
- asm jmp short std_expNext
-
- std_expNonDigit:
- asm add al, '0' /* restore original terminator */
-
- /*
- Arrive here when a valid syntax has been terminated.
-
- AL must still contain the terminating character, unchanged.
- */
- std_combine:
- UnGet (_AX, srceP);
- ct--;
-
- /*
- Arrive here with valid termination but no terminator to be pushed back.
- */
- std_exponentLimited:
-
- asm test BY0 (expSign), 0FFH /* was the exponent signed ? */
- asm jz skip_neg
- asm neg di
- /*
- Normal stays normal, Infinity becomes 0 if exponent was hugely negative.
- */
- asm neg BY0 (ExpOflow)
-
- skip_neg:
-
- /*
- The special case when digits = -1 occurs when the fraction is zero.
- In that case, the result is always zero, whatever the exponent.
- */
- asm mov bx, digits
- asm or bx, bx
- asm jnl std_nonZero
- asm FLDZ
- asm jmp std_end
-
- /*
- Combine the decimal point position with the exponent. The exponent
- begins with a value that reflects the position of the decimal point.
- */
- std_nonZero:
- asm mov cx, decimals
- asm mov ax, cx
- asm add ax, di /* 1.0E(decimals+exponent) = upper bound */
-
- /* 0.1E(decimals+exponent) = lower bound
- Convert underflows to zero and overflows to ld HUGE_VAL.
- */
- asm cmp BY0 (ExpOflow), 1 /* big (+) exp -> ld HUGE_VAL */
- asm je std_isInfinite
- asm cmp BY0 (ExpOflow), -1 /* big (-) exp -> 0 */
- asm jne std_isNormal
-
- std_isZero:
- asm FLDZ
- asm jmp short status2
-
- std_isInfinite:
- /*
- Make 'frac' a long double HUGE_VAL
- */
- asm mov ax, -1
- asm mov frac[0], ax
- asm mov frac[2], ax
- asm mov frac[4], ax
- asm mov frac[6], ax
- asm mov frac[8], 07FFEH
-
- asm FLD LONGDOUBLE( frac )
- status2:
- asm mov W0 (status), 2
- asm jmp std_end
-
-
- std_isNormal: /* For normal numbers multiply fraction * 10^exponent */
- asm mov ax, bx
- asm cmp bx, 18
- asm jna std_usualPoint
- asm mov bx, 18 /* a maximum of 18 digits are used */
-
- std_usualPoint:
- asm add ax, cx
- asm sub cx, bx
-
- std_beginExp: /* CX = decimal point contribution to exponent */
- asm add di, CX /* DI = combined exponent */
-
- asm FILD qword ptr (frac)
-
- asm mov ax, di /* Calculate 10^(|exponent|). */
- asm or ax, ax
- asm jz std_end /* no multiply required if exponent is zero. */
- asm jnl std_pow
- asm neg ax
-
- std_pow:
- asm push ax
- EXTPROC1 (pow10) /* leaves result in iNDP-87 ST(0) TOS */
- asm pop ax
-
- asm or di, di
- asm jnl std_expPlus
-
- asm FDIV /* negative exponent --> 1 / 10^|exponent| */
- asm jmp short std_end
-
- std_expPlus:
- asm FMUL /* combine the exponent with the fraction. */
-
- std_end:
- if (sign)
- {
- asm FCHS /* negate the result */
- }
-
- std_returnPP: /* update *(suffixPP) with the next character's position. */
- /*
- Finally, of course, don't forget to return the converted number !
- */
- std_exit:
- asm LES_ di, countP
- asm mov bx, ct
- asm add ES_ [di], bx
- asm LES_ di, statusP
- asm mov bx, status
- asm mov ES_ [di], bx
- return;
-
- /*-------------------------------------------------------------------------
- Special case code to scan +INF -INF +NAN -NAN
- -------------------------------------------------------------------------
- This special case code is positioned down here so that it won't mess up
- relative jumps for the rest of the function.
-
- One side effect here, if this ultimately isn't +INF -INF +NAN -NAN will be
- that the apps input stream is now messed up because we needed to look
- ahead more than 1 character to recognize INF or NAN. The 'unget' functions
- are only guaranteed to be able to unget a maximum of one char. This means
- on a worst case input like "+INX" there will be 3 characters we won't be
- able to push back on the stream successfully. There's not much that can
- be done to prevent this. The same kind of thing can happen when reading
- E format numbers, for example "1.234E+Q". By the time the 'Q' is seen
- "E+" has gone by.
- --------------------------------------------------------------------------*/
- PossibleINF:
- ct ++;
- Get (srceP);
- asm dec W0 (width)
- asm jl Didnt_pan_out
- asm cmp al, 'N'
- asm jne Didnt_pan_out
- ct ++;
- Get (srceP);
- asm dec W0 (width)
- asm jl Didnt_pan_out
- asm cmp al, 'F'
- asm jne Didnt_pan_out
- if (sign)
- {
- asm FLD FLOAT( INFM )
- }
- else
- {
- asm FLD FLOAT( INF )
- }
- asm jmp std_returnPP
-
- PossibleNAN:
- ct ++;
- Get (srceP);
- asm dec W0 (width)
- asm jl Didnt_pan_out
- asm cmp al, 'A'
- asm jne Didnt_pan_out
- ct ++;
- Get (srceP);
- asm dec W0 (width)
- asm jl Didnt_pan_out
- asm cmp al, 'N'
- asm jne Didnt_pan_out
- if (sign)
- {
- asm FLD FLOAT( NANM )
- }
- else
- {
- asm FLD FLOAT( NAN )
- }
- asm jmp std_returnPP
-
- Didnt_pan_out: /* It wasn't +/-/INF/NAN */
- status = 0;
- asm jmp std_noDigitSeen
- }
- #pragma warn .rvl
- #pragma warn .use
- #pragma warn .sus
-
- /*--------------------------------------------------------------------------*
-
- Name scanpop - Clean stack after conversion error
-
- Usage void _scanpop(void);
-
- Description This function is used to clean the stack after a conversion
- error in _scanner function
-
- *---------------------------------------------------------------------------*/
-
- static void near _scanpop()
- {
- asm FSTP ST(0) /* pop math coprocessor stack */
- }
-
- /*--------------------------------------------------------------------------*
-
- Name scanrslt - Get conversion result
-
- Usage void _scanrslt(double *rsltP, int rsltType);
-
- Description This function is used to get the result of the conversion
- in _scanner function
-
- *---------------------------------------------------------------------------*/
-
- static void near _scanrslt(void *rsltP, int rsltType)
- {
- long double temp;
-
- asm FSTP LONGDOUBLE (temp)
-
- if (rsltType & isLong)
- *(double *)rsltP = __ldtrunc(DBL, temp, HUGE_VAL);
- else if (rsltType & isLongDouble)
- *(long double *)rsltP = temp;
- else
- *(float *)rsltP = __ldtrunc(FLT, temp, 1./0.);
- }
-
- /*--------------------------------------------------------------------------*
-
- Description The functions described above are essentially used by scanf
- functions family. As for _realcvt, these functions are not
- called directly, but via a pointer to function. This is
- done in order to include the real conversion only if
- needed.
-
- Each time the compiler needs to build a reference to a
- double or float value, it generates an external reference
- to __turboFloat which forces this module to be linked in.
-
- *---------------------------------------------------------------------------*/
-
- #define CodeSeg _TEXT
- #define cPtr dw
-
- #pragma warn -use
- #pragma warn -asm
-
- static void turboFloat()
- {
- asm CodeSeg ENDS
- asm PUBLIC __turboFloat
- asm __turboFloat equ 8087h
- asm _SCNSEG SEGMENT PUBLIC WORD 'DATA'
- asm cPtr _scantod
- asm cPtr _scanrslt
- asm cPtr _scanpop
- asm _SCNSEG ENDS
- asm CodeSeg SEGMENT
- }
-