home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c065 / 2.ddi / MATH.ZIP / REALCVT.CAS < prev    next >
Encoding:
Text File  |  1990-06-07  |  12.4 KB  |  513 lines

  1. /*------------------------------------------------------------------------
  2.  * filename - realcvt.cas
  3.  *
  4.  * function(s)
  5.  *    TrimTrailing  - suppresses trailing zeroes if needed
  6.  *    __realcvt    - converts a double value to an ASCIIZ string
  7.  *-----------------------------------------------------------------------*/
  8.  
  9. /*[]------------------------------------------------------------[]*/
  10. /*|                                                              |*/
  11. /*|     Turbo C Run Time Library - Version 3.0                   |*/
  12. /*|                                                              |*/
  13. /*|                                                              |*/
  14. /*|     Copyright (c) 1987, 1990 by Borland International        |*/
  15. /*|     All Rights Reserved.                                     |*/
  16. /*|                                                              |*/
  17. /*[]------------------------------------------------------------[]*/
  18.  
  19. #pragma  inline
  20. #include <asmrules.h>
  21.  
  22. #include <_printf.h>
  23.  
  24. #   define  EXTPROC(x)  (x)
  25.  
  26. #define    I asm
  27.  
  28. /*--------------------------------------------------------------------------*
  29.  
  30. Name        TrimTrailing - suppresses trailing zeroes if needed
  31.  
  32. Usage        void near TrimTrailing (void);
  33.  
  34. Prototype in    local function
  35.  
  36. Description    Following  rules derived  from combining  the altFormat and
  37.         formCh  parameters, backspace  to remove  unwanted trailing
  38.         characters.
  39.  
  40.             (altFormat || ~(G or g)) == trailing zeroes are wanted
  41.             altFormat == trailing decimal point is wanted
  42.  
  43.         Upon entry DL  must contain formCh, CX the  starting offset
  44.         of strP, and ES_:[bx] points at the end of the string.
  45.  
  46. Return value    Upon  exit  ES_:[bx-1]  points  to  the  last  byte  of the
  47.         (trimmed ?) string.
  48.  
  49. *---------------------------------------------------------------------------*/
  50. static  void    near    TrimTrailing (void)
  51. {
  52. I    mov    al, 5Fh        /* "upper case only" mask */
  53. I    and    al, dl        /* dl is formCh */
  54. I    cmp    al, 'G'
  55. I    jne    tt_trimPoint        /* only G/g removes trailing zeroes */
  56.  
  57. tt_trimLoop:
  58. I    cmp    BY0 (ES_ [bx-1]), '0'
  59. I    jne    tt_trimPoint
  60. I    dec    bx
  61. I    cmp    bx, cx
  62. I    ja    tt_trimLoop
  63. I    jmp    short    tt_trimmed
  64.  
  65. tt_trimPoint:
  66. I    cmp    BY0 (ES_ [bx-1]), '.'
  67. I    jne    tt_trimmed
  68. I    dec    bx
  69.  
  70. tt_trimmed:
  71.     return;
  72. }
  73.  
  74. /*--------------------------------------------------------------------------*
  75.  
  76. Name        __realcvt - converts a double value to an ASCIIZ string
  77.  
  78. Usage        void pascal __realcvt (double *valueP, int ndec,
  79.                     char *strP, char formCh,
  80.                     char altFormat);
  81.  
  82. Prototype in    _printf.h
  83.  
  84. Description    The double (value) is converted  to a decimal ASCIIZ string
  85.         (*bufP) either the F format:
  86.  
  87.         [sign] [zeroes] [digit]* ['.'] [digit]
  88.  
  89.         or the E format:
  90.  
  91.         [sign] digit ['.'] [digit]* 'E' sign [digit] digit digit
  92.  
  93.         Where the  leading sign is  inserted only if  negative, and
  94.         the decimal point is suppressed if ndec == 0, and the total
  95.         count of digits <= MIN (1, ndec).
  96.  
  97.         The F format is used if formCh == 'f', or if 'g' or 'G' and
  98.         there  would  be  three  or  fewer  leading  zeroes  and no
  99.         trailing zeroes, otherwise the E format is used.
  100.  
  101.         If formCh ==  'E' or 'G' then the character  'E' is used to
  102.         separate fraction from exponent, otherwise 'e' is used.
  103.  
  104.         Trailing decimal points are usually suppressed, as also are
  105.         trailing fraction zeroes in the G/g format. If altFormat is
  106.         true  (not zero)  then  trailing  decimals remain,  and G/g
  107.         format will not trim zeroes.
  108.  
  109.         If ndec > 18 the effect is as if ndec == 18.
  110.  
  111.         If the  value was infinite  then the string  is "9E+999" or
  112.         "-9E+999".
  113.  
  114.         The  basic  conversion  to  a  numeral  string  is  done by
  115.         __xcvt(), with  this function responsible  for "customizing"
  116.         the simple format which __xcvt() returns.
  117.  
  118. Return value    There is no return value
  119.  
  120. *---------------------------------------------------------------------------*/
  121. #pragma warn -use
  122. static void pascal near __realcvt (void *valueP, int ndec,
  123.             char *strP, char formCh, char altFormat, int type)
  124. {
  125.     char    buf [__XCVTDIG__ + 4];
  126.     int    sign, realcnt;
  127.  
  128. /* caller expects ES to be preserved! */
  129. I    push    ES
  130.  
  131. #if (! LDATA)
  132. I    push    DS
  133. I    pop    ES
  134. #endif
  135.  
  136. I    mov    ax, ndec
  137. I    cmp    ax, __XCVTDIG__
  138. I    jna    meaningful
  139. I    mov    ax, __XCVTDIG__    /* IEEE double is meaningless */
  140. I    mov    ndec, ax    /*    beyond "__XCVTDIG__" digits. */
  141.  
  142. meaningful:
  143. I    mov    realcnt,ax    /* save *real* requested count */
  144. I    mov    dl, formCh
  145. I    and    dl, 0DFh    /* not ('a' - 'A'), mask case difference */
  146. I    cmp    dl, 'F'        /* F- or f-format. */
  147. I    jne    overallDigits
  148.  
  149. /* F-format works with digits right of the decimal point, specified */
  150. /*    to __xcvt() as a negative number. */
  151.  
  152. I    neg    ax
  153. I    jng    bounded        /* note "ndec" itself still +ve */
  154. I    sub    ax, ax        /* no decimals if -ve requested. */
  155. I    mov    ndec, ax
  156. I    jmp    short bounded
  157.  
  158. /* E-format counts overall digits, as does g-format sometimes.  There */
  159. /*    must be at least one. */
  160.  
  161. overallDigits:
  162. I    or    ax, ax
  163. I    jg    bounded
  164. I    mov    ax, 1
  165. I    jmp    short    decimalsDecided /* that one is left of '.'    */
  166.  
  167. bounded:
  168. I    cmp    dl, 'E'        /* e format has digit left of '.' */
  169. I    jne    decimalsDecided
  170. I    inc    ax            /* so ask __xcvt() for one more. */
  171. I    inc    W0 (ndec)
  172.  
  173. decimalsDecided:
  174.  
  175. /* __xcvt will do the difficult part, conversion to decimal, but the    */
  176. /*    format it returns must be worked on before it can be passed    */
  177. /*    to the caller.  Therefore put it into a temporary buffer.    */
  178.  
  179. #if LDATA
  180. I    push    W1 (valueP)
  181. #endif
  182. I    push    W0 (valueP)    /* (valueP        */
  183.  
  184. #if 0
  185. why was this here??
  186. /*RSS xx */
  187. I    cmp    ax,0
  188. I    jle    ok_cnt
  189. asm    mov    ax,realcnt
  190. ok_cnt:
  191. #endif
  192.  
  193. I    push    ax            /*    , realcnt    */
  194. #if LDATA
  195. I    push    SS
  196. #endif
  197. I    lea    bx, sign
  198. I    push    bx            /*    , &sign    */
  199. #if LDATA
  200. I    push    SS
  201. #endif
  202. I    lea    si, buf
  203. I    push    si            /*    , buf  */
  204.  
  205. I    mov    ax, W0 (type)    /*    , type )    */
  206. I    push    ax
  207.  
  208. I    call    EXTPROC (__xcvt)/* returns exponent in AX */
  209.  
  210. I    xchg    bx, ax    /* BX = exponent */
  211.  
  212. /* Set up pointer to destination string */
  213.  
  214. I    LES_    di, strP
  215. I    cld            /* forward string direction */
  216.  
  217. /* Check for Infinity and NAN numbers right up front */
  218.  
  219. I    cmp    bx, INF_number
  220. I    je    infinities
  221. I    cmp    bx, NAN_number
  222. I    je    NANs
  223. I    jmp    short regular_number
  224.  
  225. /*+---------------------------------------------------------------------+
  226.   | We have a NAN or Infinity here. Do +INF/-INF or +NAN/-NAN        |
  227.   +---------------------------------------------------------------------+*/
  228. infinities:
  229. I    mov    ax, 492BH        /* 'I+' stosw reverses        */
  230. I    cmp    W0 (sign), 0
  231. I    je    store_Isign
  232. I    inc    ax            /* Change 'I+' to 'I-'        */
  233. I    inc    ax
  234.  
  235. store_Isign:
  236. I    stosw
  237.  
  238. I    mov    ax, 464EH        /* 'FN' stosw reverses        */
  239. I    stosw
  240.     goto end;
  241.  
  242.             /* Print a NAN    */
  243. NANs:
  244. I    mov    ax, 4E2BH        /* 'N+' stosw reverses        */
  245. I    cmp    W0 (sign), 0
  246. I    je    store_Nsign
  247. I    inc    ax            /* Change 'N+' to 'N-'        */
  248. I    inc    ax
  249.  
  250. store_Nsign:
  251. I    stosw
  252.  
  253. I    mov    al, 'A'            /* AX = 'NA' stosw reverses    */
  254. I    stosw
  255.     goto end;
  256.  
  257. /*+---------------------------------------------------------------------+
  258.   |            We're printing a regular number         |
  259.   +---------------------------------------------------------------------+*/
  260.  
  261. regular_number:
  262.  
  263. /* Either format begins with optional sign. */
  264.  
  265. I    cmp    BY0 (sign), 0
  266. I    je    signSet
  267. I    mov    al, '-'
  268. I    stosb
  269. signSet:
  270.  
  271. /* Now that we have the basic string, decide what format the caller */
  272. /*    wants it to be put into. */
  273.  
  274. I    mov    dl, formCh    /* possibilities are e, E, f, g, or G */
  275. I    and    dl, 5Fh        /* convert to upper case */
  276. I    cmp    dl, 'F'
  277. I    je    F_format
  278. I    cmp    dl, 'E'
  279. I    je    E_format
  280.  
  281. /* G format can be either E or F, depending on circumstances. */
  282.  
  283. I    cmp    bx, -3         /* Now decide between E and F formats. */
  284. I    jl    E_format
  285. I   mov ax, ndec    /* Zero digits in G format are taken as one */
  286. I   or  ax, ax
  287. I   jnz NotZero
  288. I   inc ax
  289. NotZero:
  290. I    cmp    bx, ax
  291. I    jg    E_format
  292.  
  293. /* The F format has no written exponent and the decimal point can be at */
  294. /*    the left edge or any interior position, but not the right edge. */
  295.  
  296. F_format:
  297. I    cmp    bx, __XCVTDIG__ /* refuse to do F format if more than */
  298. I    jg    E_format    /* __XCVTDIG__ integral digits can result. */
  299.  
  300. I    or    bx, bx
  301. I    jg    FdigitStart
  302.  
  303. /* No integral digits, begin with '0.'. */
  304.  
  305. I    mov    ax, ('.' SHL 8) + '0'
  306. I    stosw
  307. I    mov    cx,1
  308. I    je    Fdigits
  309.  
  310. /* If the exponent is negative then leading zeroes are required. */
  311.  
  312. I    mov    al, '0'
  313. FleadZeroes:
  314. I    stosb
  315. I    inc    bx
  316. I    jnz    FleadZeroes
  317.  
  318. /* Now write the regular digits, inserting a '.' if it is somewhere */
  319. /*    in the middle of the numeral. */
  320.  
  321. FdigitStart:
  322. I    mov    cx,0
  323. Fdigits:
  324. I    lods    BY0 (SS_ [si])
  325. I    or    al, al
  326. I    jz    Fend
  327.  
  328. I    stosb
  329. I    dec    bx
  330. I    jnz    Fdigits
  331. I    mov    al, '.'
  332. I    stosb
  333. I    inc    cx
  334. I    jmp    short    Fdigits
  335.  
  336. /* remove any trailing zeroes, unless there is an implied decimal at */
  337. /*    the right edge. */
  338.  
  339. Fend:
  340. I    mov    ax,ndec
  341. I    add    cx,realcnt
  342. I    cmp    ax,cx
  343. I    jbe    no_zero_pad
  344.  
  345. I    sub    ax,cx
  346. I    mov    cx,ax
  347. I    add    bx,ax
  348. I    mov    al,'0'
  349. I    rep    stosb
  350.  
  351. I    dec    bx
  352. I    jz    Fz
  353.  
  354. no_zero_pad:
  355. I    dec    bx
  356. I    jz    Fz
  357.  
  358. I    cmp    BY0 (altFormat), 0
  359. I    jne    Fz        /* altFormat implies no trimming */
  360. I    mov    dl, formCh
  361. I    mov    cx, W0 (strP)
  362. I    xchg    bx, di
  363.     TrimTrailing ();        /* backspaces BX to effect trimming */
  364. I    xchg    di, bx
  365.  
  366. Fz:                /* is the result an empty string ? */
  367. I    cmp    di, W0 (strP)
  368. I    jne    goto_end
  369. I    mov    al, '0'
  370. I    stosb            /* if so, it is a form of zero. */
  371. goto_end:
  372. I    jmp    end        /* the string will be zero terminated */
  373.  
  374.  
  375. /* The E format always places one digit to the left of the decimal
  376.     point, followed by fraction digits, and then an 'E' followed
  377.     by a decimal exponent.  The exponent is always 2 digits unless
  378.     it is of magnitude > 99.
  379. */
  380. E_format:
  381. I    lods    W0 (SS_ [si])        /* get two bytes together */
  382. I    stosb
  383.  
  384. /* The decimal point appears only if followed by a digit. */
  385.  
  386. I    mov    al, '.'
  387. I    or    ah, ah
  388. I    jz    Eexp
  389.  
  390. /* If arrived here then there are at least two digits so put in */
  391. /*    the decimal point and copy the fraction digits. */
  392.  
  393. I    stosb
  394. I    mov    al, ah
  395.  
  396. Edigits:
  397. I    stosb
  398. I    lods    BY0 (SS_ [si])
  399. I    or    al, al
  400. I    jnz    Edigits
  401.  
  402. /* Trailing zeroes are removed from the fraction. */
  403.  
  404. I    cmp    BY0 (altFormat), 0
  405. I    jne    Eexp        /* altFormat implies no trimming */
  406. I    mov    dl, formCh
  407. I    mov    cx, W0 (strP)
  408. I    xchg    bx, di
  409.     TrimTrailing ();        /* backspaces BX to effect trimming */
  410. I    xchg    di, bx
  411.  
  412. /* Now put in the exponent.  Note that the exponent returned from __xcvt */
  413. /*    is one digit different, since __xcvt places the decimal point at */
  414. /*    the left edge. */
  415.  
  416. Eexp:
  417. I    mov    al, 20h        /* the bit which distinguishes lower case */
  418. I    and    al, formCh    /*    e f g E    G        -- cause */
  419. I    or    al, 'E'        /*    e e e E    E        -- effect */
  420. I    stosb
  421.  
  422. I    mov    ax, 2D2Bh    /* AX = "-+"    load up both possibilities  */
  423. I    dec    bx        /* BX is the exponent returned from __xcvt */
  424. I    jnl    EexpSigned
  425. I    xchg    al, ah        /* It was negative, switch signs */
  426. I    neg    bx        /* Make it positive for later */
  427.  
  428. EexpSigned:
  429. I    stosb
  430. I    xchg    ax, bx
  431.  
  432. /*
  433.     ANSI says that "The exponent always contains AT LEAST two digits".
  434.     If the exponent is bigger than 99 then we will use as many as are 
  435.     required.
  436. */
  437. I    mov    cx, 3030h        /* "00" this'll be handy later */
  438.  
  439. I    cmp    ax, 99
  440. I    jna    EtwoDigits
  441.  
  442. I    cmp    ax, 999
  443. I    jna    EthreeDigits
  444.  
  445. EfourDigits:
  446. I    cwd            /* Sign extend AX through DX */
  447. I    mov    bx, 1000
  448. I    div    bx
  449. I    add    al, cl
  450. I    stosb
  451. I    xchg    ax, dx
  452.  
  453. EthreeDigits:
  454. I    mov    bl, 100
  455. I    div    bl
  456. I    add    al, cl
  457. I    stosb
  458. I    xchg    al, ah
  459. I    cbw
  460.  
  461. EtwoDigits:
  462. I    mov    bl, 10
  463. I    div    bl
  464. I    add    ax, cx        /* '00' */
  465. I    stosw
  466.  
  467. /* Both formats are terminated with \0. */
  468.  
  469. end:
  470. I    xor    al,al
  471. I    stosb
  472.  
  473. I    pop    ES
  474.  
  475.     return;
  476. }
  477. #pragma warn .use
  478.  
  479.  
  480. /*--------------------------------------------------------------------------*
  481.  
  482. Description    __realcvt is essentially used by printf family functions. In
  483.         order  to  include  the  real  conversion  function only if
  484.         needed (using %f, %e or  %g format), __realcvt is not called
  485.         directly but via a pointer to function which is declared in
  486.         the startup code  in an extra segment _CVTSEG.
  487.  
  488.         This module  is included at link  time only if there  is at
  489.         least one floating point number, because in that case there
  490.         are external  references to the  fixups FI?RQQ which  force
  491.         the emulator to be linked  in. The emulator has an external
  492.         reference to the value  __turboCvt which forces this module
  493.         to be linked in.
  494.  
  495. *---------------------------------------------------------------------------*/
  496.  
  497. #define CodeSeg _TEXT
  498. #define cPtr    dw
  499.  
  500. #pragma warn -rvl
  501. #pragma warn -use
  502. #pragma warn -asm
  503. static void near turboCvt(void)
  504. {
  505. I    CodeSeg ENDS
  506. I    PUBLIC  __turboCvt
  507. I    __turboCvt    equ    0
  508. I    _CVTSEG SEGMENT PUBLIC WORD 'DATA'
  509. I    cPtr    __realcvt
  510. I    _CVTSEG ENDS
  511. I    CodeSeg SEGMENT
  512. }
  513.