math.parser, don't take infinite time to parse huge exponents
parent
dce2ca1366
commit
09af182db7
|
@ -373,3 +373,32 @@ unit-test
|
||||||
{ 1.0 } [ "0x1" 1000 [ CHAR: 0 ] "" replicate-as append "p-4000" append string>number ] unit-test
|
{ 1.0 } [ "0x1" 1000 [ CHAR: 0 ] "" replicate-as append "p-4000" append string>number ] unit-test
|
||||||
{ 1.0 } [ "0." 3000 [ CHAR: 0 ] "" replicate-as append "1e3001" append string>number ] unit-test
|
{ 1.0 } [ "0." 3000 [ CHAR: 0 ] "" replicate-as append "1e3001" append string>number ] unit-test
|
||||||
{ 1.0 } [ "0x0." 1000 [ CHAR: 0 ] "" replicate-as append "1p4004" append string>number ] unit-test
|
{ 1.0 } [ "0x0." 1000 [ CHAR: 0 ] "" replicate-as append "1p4004" append string>number ] unit-test
|
||||||
|
{ 1.0 } [ "1" 3000 [ CHAR: 0 ] "" replicate-as append "." append
|
||||||
|
3000 [ CHAR: 0 ] "" replicate-as append "e-3000" append string>number ] unit-test
|
||||||
|
|
||||||
|
! We correctly parse the biggest/smallest float correctly
|
||||||
|
! (ie the 1/0. or 0/0. short-circuit optimization doesn't apply)
|
||||||
|
{ 1 } [ "4.9406564584124655e-324" string>number double>bits ] unit-test
|
||||||
|
{ 1 } [ "0x1.0p-1074" string>number double>bits ] unit-test
|
||||||
|
{ 1 } [ "0o1.0p-1074" string>number double>bits ] unit-test
|
||||||
|
{ 1 } [ "0b1.0p-1074" string>number double>bits ] unit-test
|
||||||
|
{ 0x7fefffffffffffff } [ "1.7976931348623157e+308" string>number double>bits ] unit-test
|
||||||
|
{ 0x7fefffffffffffff } [ "0x1.fffffffffffffp1023" string>number double>bits ] unit-test
|
||||||
|
{ 0x7fefffffffffffff } [ "0o1.777777777777777774p1023" string>number double>bits ] unit-test
|
||||||
|
{ 0x7fefffffffffffff } [ "0b1.1111111111111111111111111111111111111111111111111111p1023" string>number double>bits ] unit-test
|
||||||
|
! Actual biggest/smallest parseable floats are a little
|
||||||
|
! larger/smaller than IEE754 values because of rounding
|
||||||
|
{ 0x1.0p-1074 } [ "0x0.fffffffffffffcp-1074" string>number ] unit-test
|
||||||
|
{ 4.94065645841246544e-324 } [ "4.94065645841246517e-324" string>number ] unit-test
|
||||||
|
{ 0x1.fffffffffffffp1023 } [ "0x1.fffffffffffff7ffffffffffffffffp1023" string>number ] unit-test
|
||||||
|
{ 1.79769313486231571e+308 } [ "1.797693134862315807e+308" string>number ] unit-test
|
||||||
|
|
||||||
|
! works with ratios
|
||||||
|
{ 0.25 } [ "1/4" 3000 [ CHAR: 0 ] "" replicate-as append "e-3000" append string>number ] unit-test
|
||||||
|
{ 1.25 } [ "1+1/4" 3000 [ CHAR: 0 ] "" replicate-as append "e-3000" append string>number ] unit-test
|
||||||
|
|
||||||
|
! #1356 #1231
|
||||||
|
{ 1/0. } [ "1e100000" string>number ] unit-test
|
||||||
|
{ 0.0 } [ "1e-100000" string>number ] unit-test
|
||||||
|
{ 1/0. } [ "0x1p300000" string>number ] unit-test
|
||||||
|
{ 0.0 } [ "0x1p-300000" string>number ] unit-test
|
||||||
|
|
|
@ -26,13 +26,19 @@ ERROR: invalid-radix radix ;
|
||||||
|
|
||||||
<PRIVATE
|
<PRIVATE
|
||||||
|
|
||||||
|
! magnitude is used only for floats to avoid
|
||||||
|
! expensive computations when we know that
|
||||||
|
! the result will overflow/underflow.
|
||||||
|
! The computation of magnitude starts in
|
||||||
|
! number-parse and continues in float-parse.
|
||||||
TUPLE: number-parse
|
TUPLE: number-parse
|
||||||
{ str read-only }
|
{ str read-only }
|
||||||
{ length fixnum read-only }
|
{ length fixnum read-only }
|
||||||
{ radix fixnum } ;
|
{ radix fixnum }
|
||||||
|
{ magnitude fixnum } ;
|
||||||
|
|
||||||
: <number-parse> ( str radix -- i number-parse n )
|
: <number-parse> ( str radix -- i number-parse n )
|
||||||
[ 0 ] 2dip [ dup length ] dip number-parse boa 0 ; inline
|
[ 0 ] 2dip [ dup length ] dip 0 number-parse boa 0 ; inline
|
||||||
|
|
||||||
: (next-digit) ( i number-parse n digit-quot end-quot -- n/f )
|
: (next-digit) ( i number-parse n digit-quot end-quot -- n/f )
|
||||||
[ 2over length>> < ] 2dip
|
[ 2over length>> < ] 2dip
|
||||||
|
@ -44,8 +50,20 @@ TUPLE: number-parse
|
||||||
: next-digit ( i number-parse n quot -- n/f )
|
: next-digit ( i number-parse n quot -- n/f )
|
||||||
[ 2nip ] (next-digit) ; inline
|
[ 2nip ] (next-digit) ; inline
|
||||||
|
|
||||||
|
: inc-magnitude ( number-parse -- number-parse' )
|
||||||
|
[ 1 + ] change-magnitude ; inline
|
||||||
|
|
||||||
|
: ?inc-magnitude ( number-parse n -- number-parse' )
|
||||||
|
zero? [ inc-magnitude ] unless ; inline
|
||||||
|
|
||||||
|
: (add-digit) ( number-parse n digit -- number-parse n' )
|
||||||
|
[ dup radix>> ] [ * ] [ + ] tri* ;
|
||||||
|
|
||||||
: add-digit ( i number-parse n digit quot -- n/f )
|
: add-digit ( i number-parse n digit quot -- n/f )
|
||||||
[ [ dup radix>> ] [ * ] [ + ] tri* ] dip next-digit ; inline
|
[ (add-digit) [ ?inc-magnitude ] keep ] dip next-digit ; inline
|
||||||
|
|
||||||
|
: add-exponent-digit ( i number-parse n digit quot -- n/f )
|
||||||
|
[ (add-digit) ] dip next-digit ; inline
|
||||||
|
|
||||||
: digit-in-radix ( number-parse n char -- number-parse n digit ? )
|
: digit-in-radix ( number-parse n char -- number-parse n digit ? )
|
||||||
digit> pick radix>> over > ; inline
|
digit> pick radix>> over > ; inline
|
||||||
|
@ -56,9 +74,10 @@ TUPLE: number-parse
|
||||||
TUPLE: float-parse
|
TUPLE: float-parse
|
||||||
{ radix fixnum }
|
{ radix fixnum }
|
||||||
{ point }
|
{ point }
|
||||||
{ exponent } ;
|
{ exponent }
|
||||||
|
{ magnitude } ;
|
||||||
: inc-point ( float-parse -- float-parse' )
|
: inc-point-?dec-magnitude ( float-parse n -- float-parse' )
|
||||||
|
zero? [ [ 1 - ] change-magnitude ] when
|
||||||
[ 1 + ] change-point ; inline
|
[ 1 + ] change-point ; inline
|
||||||
|
|
||||||
: store-exponent ( float-parse n expt -- float-parse' n )
|
: store-exponent ( float-parse n expt -- float-parse' n )
|
||||||
|
@ -79,10 +98,37 @@ TUPLE: float-parse
|
||||||
dup 0 >= [ ((pow)) ] [ [ recip ] [ neg ] bi* ((pow)) ] if ; inline
|
dup 0 >= [ ((pow)) ] [ [ recip ] [ neg ] bi* ((pow)) ] if ; inline
|
||||||
|
|
||||||
: add-mantissa-digit ( float-parse i number-parse n digit quot -- float-parse' n/f )
|
: add-mantissa-digit ( float-parse i number-parse n digit quot -- float-parse' n/f )
|
||||||
[ [ inc-point ] 4dip ] dip add-digit ; inline
|
[ (add-digit)
|
||||||
|
dup [ inc-point-?dec-magnitude ] curry 3dip
|
||||||
|
] dip next-digit ; inline
|
||||||
|
|
||||||
|
! IEE754 doubles are in the range ]10^309,10^-324[,
|
||||||
|
! or expressed in base 2, ]2^1024, 2^-1074].
|
||||||
|
! We don't need those ranges to be accurate as long as we are
|
||||||
|
! excluding all the floats because they are used only to
|
||||||
|
! optimize when we know there will be an overflow/underflow
|
||||||
|
! We compare these numbers to the magnitude slot of float-parse,
|
||||||
|
! which has the following behavior:
|
||||||
|
! ... ; 0.0xxx -> -1; 0.xxx -> 0; x.xxx -> 1; xx.xxx -> 2; ...;
|
||||||
|
! Also, take some margin as the current float parsing algorithm
|
||||||
|
! does some rounding; For example,
|
||||||
|
! 0x1.0p-1074 is the smallest IE754 double, but floats down to
|
||||||
|
! 0x0.fffffffffffffcp-1074 are parsed as 0x1.0p-1074
|
||||||
|
CONSTANT: max-magnitude-10 309
|
||||||
|
CONSTANT: min-magnitude-10 -323
|
||||||
|
CONSTANT: max-magnitude-2 1027
|
||||||
|
CONSTANT: min-magnitude-2 -1074
|
||||||
|
|
||||||
: make-float-dec-exponent ( float-parse n/f -- float/f )
|
: make-float-dec-exponent ( float-parse n/f -- float/f )
|
||||||
[ [ radix>> ] [ point>> ] [ exponent>> ] tri - (pow) ] [ swap /f ] bi* ; inline
|
over [ exponent>> ] [ magnitude>> ] bi +
|
||||||
|
{
|
||||||
|
{ [ dup max-magnitude-10 > ] [ 3drop 1/0. ] }
|
||||||
|
{ [ dup min-magnitude-10 < ] [ 3drop 0.0 ] }
|
||||||
|
[ drop
|
||||||
|
[ [ radix>> ] [ point>> ] [ exponent>> ] tri - (pow) ]
|
||||||
|
[ swap /f ] bi*
|
||||||
|
]
|
||||||
|
} cond ; inline
|
||||||
|
|
||||||
: base2-digits ( digits radix -- digits' )
|
: base2-digits ( digits radix -- digits' )
|
||||||
{
|
{
|
||||||
|
@ -94,8 +140,19 @@ TUPLE: float-parse
|
||||||
: base2-point ( float-parse -- point )
|
: base2-point ( float-parse -- point )
|
||||||
[ point>> ] [ radix>> ] bi base2-digits ; inline
|
[ point>> ] [ radix>> ] bi base2-digits ; inline
|
||||||
|
|
||||||
|
: base2-magnitude ( float-parse -- point )
|
||||||
|
[ magnitude>> ] [ radix>> ] bi base2-digits ; inline
|
||||||
|
|
||||||
: make-float-bin-exponent ( float-parse n/f -- float/f )
|
: make-float-bin-exponent ( float-parse n/f -- float/f )
|
||||||
[ [ drop 2 ] [ base2-point ] [ exponent>> ] tri - (pow) ] [ swap /f ] bi* ; inline
|
over [ exponent>> ] [ base2-magnitude ] bi +
|
||||||
|
{
|
||||||
|
{ [ dup max-magnitude-2 > ] [ 3drop 1/0. ] }
|
||||||
|
{ [ dup min-magnitude-2 < ] [ 3drop 0.0 ] }
|
||||||
|
[ drop
|
||||||
|
[ [ drop 2 ] [ base2-point ] [ exponent>> ] tri - (pow) ]
|
||||||
|
[ swap /f ] bi*
|
||||||
|
]
|
||||||
|
} cond ; inline
|
||||||
|
|
||||||
: ?default-exponent ( float-parse n/f -- float-parse' n/f' )
|
: ?default-exponent ( float-parse n/f -- float-parse' n/f' )
|
||||||
over exponent>> [
|
over exponent>> [
|
||||||
|
@ -125,14 +182,14 @@ TUPLE: float-parse
|
||||||
: @abort ( i number-parse n x -- f )
|
: @abort ( i number-parse n x -- f )
|
||||||
4drop f ; inline
|
4drop f ; inline
|
||||||
|
|
||||||
: @split ( i number-parse n -- n i number-parse n' )
|
: @split ( i number-parse n -- n i number-parse' n' )
|
||||||
-rot 0 ; inline
|
-rot 0 >>magnitude 0 ; inline
|
||||||
|
|
||||||
: @split-exponent ( i number-parse n -- n i number-parse' n' )
|
: @split-exponent ( i number-parse n -- n i number-parse' n' )
|
||||||
-rot 10 >>radix 0 ; inline
|
-rot 10 >>radix 0 ; inline
|
||||||
|
|
||||||
: <float-parse> ( i number-parse n -- float-parse i number-parse n )
|
: <float-parse> ( i number-parse n -- float-parse i number-parse n )
|
||||||
[ drop nip radix>> 0 f float-parse boa ] 3keep ; inline
|
[ drop nip [ radix>> ] [ magnitude>> ] bi [ 0 f ] dip float-parse boa ] 3keep ; inline
|
||||||
|
|
||||||
DEFER: @exponent-digit
|
DEFER: @exponent-digit
|
||||||
DEFER: @mantissa-digit
|
DEFER: @mantissa-digit
|
||||||
|
@ -149,7 +206,7 @@ DEFER: @neg-digit
|
||||||
|
|
||||||
: @exponent-digit ( float-parse i number-parse n char -- float-parse n/f )
|
: @exponent-digit ( float-parse i number-parse n char -- float-parse n/f )
|
||||||
{ float-parse fixnum number-parse integer fixnum } declare
|
{ float-parse fixnum number-parse integer fixnum } declare
|
||||||
digit-in-radix [ [ @exponent-digit-or-punc ] add-digit ] [ @abort ] if ;
|
digit-in-radix [ [ @exponent-digit-or-punc ] add-exponent-digit ] [ @abort ] if ;
|
||||||
|
|
||||||
: @exponent-first-char ( float-parse i number-parse n char -- float-parse n/f )
|
: @exponent-first-char ( float-parse i number-parse n char -- float-parse n/f )
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue