>FLOAT - A Forth-94 Compliant Implementation

2009-05-07

Background

>FLOAT is the Forth-94 string-to-float conversion primitive. While most implementations seek to follow the specification given by the Standard, few are strictly compliant. A common deficit is the failure to detect certain illegal input.

The implementation presented here seeks to avoid the defects observed elsewhere while keeping code size to a minimum. A test is included to check against common implementation errors.

Should a defect be discovered in this implementation, or it is found to accept input not specified by Forth-94, the author would be pleased to hear of it.

Implementation

\ FINPUT.F  version 0.1  2009-05-07
\
\ A minimum yet compliant Forth-94 implementation of
\ >FLOAT. Works with separate or common stack float
\ models.
\
\ F# demonstrates the conditioning required to be
\ applied to >FLOAT to achieve compliance for forth
\ text input.
\
\ The code is intended as a model only. No particular
\ effort has been made to optimize for speed or
\ accuracy.
\
\ Assumptions:
\   2's complement arithmetic
\   1 char = 1 addr unit
\   -ROT    ( : -ROT  ROT ROT ; )
\   <>      ( : <>  - 0<> ; )
\
\ This code is PUBLIC DOMAIN.  Use at your own risk.
\
\ History:
\
\ 0.1  Replaced .1E F* with 10E F/ for better accuracy.
\      Added conditional to allow leading decimal point
\      on forth text input.
\

( EMPTY ) FORTH DEFINITIONS DECIMAL

CR .( Loading FINPUT v0.1  2009-05-07 ... )

1 CONSTANT STRICT  \ 0 = compiles shorter but less
                   \ strict code

\ *****  MODIFY THESE AS REQUIRED  *****

HEX
: UPCHAR ( c -- C )  \ make char uppercase
  DUP [CHAR] a [CHAR] z 1+ WITHIN IF 20 XOR THEN ;
DECIMAL

\ Implement this to allow 'E' or 'e' on forth input
: CAPS-SEARCH  SEARCH ;  \ case-insensitive SEARCH

\ *****  END  *****

STRICT [IF] .( strict ) [ELSE] .( short ) [THEN]
.( version ) CR

HERE

VARIABLE exp  \ exponent
VARIABLE dpf  \ decimal point

FVARIABLE tmp

10 0 D>F FCONSTANT F10.0  \ 10.E0

: getc ( a u -- a' u' c )
  1 /STRING  OVER 1- C@ ;

\ get sign
: gets ( a u -- a' u' n|0 )
  DUP IF
    getc  DUP [CHAR] - = IF EXIT THEN
              [CHAR] + <> /STRING
  THEN 0 ;

: getdigs ( a u -- a' u' )
  BEGIN  DUP  WHILE
    getc  [CHAR] 0 -  DUP 9 U> IF
      DROP  -1 /STRING  EXIT
    THEN
    0 D>F  tmp F@  F10.0 F*  F+  tmp F!
    dpf @  exp +!
  REPEAT ;

: getmant ( a u -- a' u' flag )
  [ STRICT ] [IF] TUCK [THEN]
  getdigs  DUP IF
    OVER C@ [CHAR] . = IF
      -1 dpf !  1 /STRING  getdigs
    THEN
  THEN
  [ STRICT ] [IF] ROT OVER - dpf @ + [THEN] ;

: getexp ( a u -- a' u' )
  DUP IF
    OVER C@ UPCHAR  DUP [CHAR] E =
    SWAP [CHAR] D = OR  1 AND /STRING
  THEN
  gets >R  0 0 2SWAP >NUMBER 2SWAP D>S
  R> IF NEGATE THEN  exp @ +
  BEGIN  ?DUP WHILE  DUP 0<
    IF    1+  tmp F@  F10.0  F/
    ELSE  1-  tmp F@  F10.0  F*  THEN  tmp F!
  REPEAT ;

STRICT [IF]

\ Forth-94 function (strict)
: >FLOAT ( c-addr u -- r true | false )
  [ 0 0 D>F ] FLITERAL tmp F!  0 exp !  0 dpf !
  2DUP  -TRAILING  NIP 0<> AND DUP IF
    gets >R  getmant IF
      getexp DUP WHILE
    THEN
    2DROP  R> DROP  0 EXIT  THEN
  ELSE  0 >R
  THEN  2DROP  tmp F@  R> IF FNEGATE THEN  TRUE ;

[ELSE]

\ Forth-94 function (non-strict)
: >FLOAT ( a u -- r true | false )
  [ 0 0 D>F ] FLITERAL tmp F!  0 exp !  0 dpf !
  -TRAILING
  gets >R  getmant
  getexp IF  R> 2DROP 0 EXIT  THEN
  DROP  tmp F@  R> IF FNEGATE THEN  TRUE ;

[THEN]

HERE SWAP - CR . .( bytes )

\ Forth float input. Floating-point numbers can be
\ entered via F# .

1 [IF] ( standard behaviour )

: fnumber ( c-addr u -- [r] flag )
  DUP 1 > IF ( at least 2 chars )
    OVER  DUP C@ [CHAR] . < -  ( skip sign)
    C@ [CHAR] . >  >R          ( 1st char can't be .)
    2DUP  S" E" CAPS-SEARCH >R  2DROP
    2R> AND  BASE @ 10 = AND  0= WHILE
  THEN  2DROP 0  ELSE  >FLOAT  THEN
  DUP >R  STATE @ AND IF  POSTPONE FLITERAL  THEN R> ;

[ELSE] ( allow leading decimal point )

: fnumber ( c-addr u -- [r] flag )
  2DUP  S" E" CAPS-SEARCH -ROT  2DROP
  BASE @ 10 = AND IF  >FLOAT  ELSE  2DROP 0  THEN
  DUP >R  STATE @ AND IF  POSTPONE FLITERAL  THEN R> ;

[THEN]

: F# ( <number> )
  BL WORD COUNT fnumber 0= ABORT" bad float" ; IMMEDIATE

1 [IF]

\ Test Forth-94 compliance for >FLOAT

: CHK ( addr len flag )
  >R CR [CHAR] " EMIT 2DUP TYPE [CHAR] " EMIT
  8 OVER - SPACES  >FLOAT DUP >R IF FDROP THEN R>
  ." --> " DUP IF ." TRUE " ELSE ." FALSE" THEN
  R> - IF ."   *fail* " ELSE ."   pass " THEN ;

: TEST ( -- )
  CR ." Checking >FLOAT Forth-94 compliance ..." CR
  S" ."    FALSE CHK
  S" E"    FALSE CHK
  S" .E"   FALSE CHK
  S" .E-"  FALSE CHK
  S" +"    FALSE CHK
  S" -"    FALSE CHK
  S"  9"   FALSE CHK
  S" 9 "   FALSE CHK
  S" "     TRUE CHK
  S"    "  TRUE CHK
  S" 1+1"  TRUE CHK
  S" 1-1"  TRUE CHK
  S" 9"    TRUE CHK
  S" 9."   TRUE CHK
  S" .9"   TRUE CHK
  S" 9E"   TRUE CHK
  S" 9e+"  TRUE CHK
  S" 9d-"  TRUE CHK
;

TEST

[THEN]

\ end
Top    Home    Forth

em.gif (457 bytes)


counter on blogger

Page updated: 07 May 2009