235 lines
7.7 KiB
Factor
235 lines
7.7 KiB
Factor
|
! Copyright (C) 2018 Alexander Ilin.
|
||
|
! See http://factorcode.org/license.txt for BSD license.
|
||
|
USING: formatting kernel locals math math.bitwise math.functions
|
||
|
math.order ryu.data sequences shuffle strings vectors ;
|
||
|
|
||
|
IN: ryu
|
||
|
|
||
|
<PRIVATE
|
||
|
|
||
|
: mul-shift ( x mul shift -- y )
|
||
|
[ first2 rot [ * ] keep swapd * -64 shift + ] [ 64 - neg ] bi* shift ;
|
||
|
|
||
|
: mul-shift-all ( mmShift m mul shift -- vm vp vr )
|
||
|
[ 4 * ] 2dip
|
||
|
[ [ 1 - swap - ] 2dip mul-shift ]
|
||
|
[ [ 2 + ] 2dip mul-shift ]
|
||
|
[ mul-shift ] 3tri ;
|
||
|
|
||
|
:: pow-5-factor ( x -- y )
|
||
|
x :> value!
|
||
|
f 0 [ 2dup x <= swap not and ] [
|
||
|
value 5 /mod zero? [ value! 1 + ] [ nipd swap ] if
|
||
|
] while nip ; inline
|
||
|
|
||
|
: multiple-of-power-of-5 ( p value -- ? )
|
||
|
pow-5-factor <= ;
|
||
|
|
||
|
: double-pow-5-bits ( n -- m )
|
||
|
[ 1 ] [
|
||
|
DOUBLE_LOG2_5_NUMERATOR * DOUBLE_LOG2_5_DENOMINATOR + 1 -
|
||
|
DOUBLE_LOG2_5_DENOMINATOR /i
|
||
|
] if-zero ; inline
|
||
|
|
||
|
: decimal-length ( m -- n )
|
||
|
{
|
||
|
10
|
||
|
100
|
||
|
1000
|
||
|
10000
|
||
|
100000
|
||
|
1000000
|
||
|
10000000
|
||
|
100000000
|
||
|
1000000000
|
||
|
10000000000
|
||
|
100000000000
|
||
|
1000000000000
|
||
|
10000000000000
|
||
|
100000000000000
|
||
|
1000000000000000
|
||
|
10000000000000000
|
||
|
100000000000000000
|
||
|
1000000000000000000
|
||
|
} [ dupd >= ] find-last [ 2 + ] [ drop 1 ] if nip ; inline
|
||
|
|
||
|
CONSTANT: mantissaBits 52
|
||
|
CONSTANT: exponentBits 11
|
||
|
CONSTANT: offset 1023 ! (1 << (exponentBits - 1)) - 1
|
||
|
|
||
|
:: unpack-bits ( value -- e2 m2 acceptBounds ieeeExponent<=1? neg? string/f )
|
||
|
value double>bits
|
||
|
dup mantissaBits exponentBits + bit? :> sign
|
||
|
dup mantissaBits bits :> ieeeMantissa
|
||
|
mantissaBits neg shift exponentBits bits :> ieeeExponent
|
||
|
0 :> m2!
|
||
|
0 :> e2!
|
||
|
exponentBits on-bits ieeeExponent = [
|
||
|
ieeeMantissa zero? [ sign "-Inf" "Inf" ? ] [ "NaN" ] if
|
||
|
] [
|
||
|
ieeeExponent [
|
||
|
ieeeMantissa [ sign "-0e0" "0e0" ? ] [
|
||
|
m2!
|
||
|
-1 offset - mantissaBits - e2!
|
||
|
f
|
||
|
] if-zero
|
||
|
] [
|
||
|
offset - mantissaBits - 2 - e2!
|
||
|
ieeeMantissa mantissaBits set-bit m2!
|
||
|
f
|
||
|
] if-zero
|
||
|
] if [ e2 m2 dup even? ieeeExponent 1 <= sign ] dip ; inline
|
||
|
|
||
|
:: prepare-output ( vp! vplength acceptBounds vmIsTrailingZeros! vrIsTrailingZeros! vr! vm! -- vplength' output )
|
||
|
! vr is converted into the output
|
||
|
0 vplength
|
||
|
! the if has this stack-effect: ( lastRemovedDigit vplength -- lastRemovedDigit' vplength' output )
|
||
|
vmIsTrailingZeros vrIsTrailingZeros or [
|
||
|
! rare
|
||
|
[ vp 10 /i vm 10 /i 2dup > ] [
|
||
|
vm! vp!
|
||
|
vmIsTrailingZeros [ vm 10 divisor? vmIsTrailingZeros! ] when
|
||
|
vrIsTrailingZeros [ over zero? vrIsTrailingZeros! ] when
|
||
|
vr 10 /mod -roll vr! nip ! lastRemovedDigit!
|
||
|
1 - ! vplength!
|
||
|
] while 2drop
|
||
|
vmIsTrailingZeros [
|
||
|
[ vm dup 10 /i dup 10 * swapd = ] [
|
||
|
vm!
|
||
|
vrIsTrailingZeros [ over zero? vrIsTrailingZeros! ] when
|
||
|
vr 10 /mod -roll vr! nip ! lastRemovedDigit!
|
||
|
vp 10 /i vp!
|
||
|
1 - ! vplength!
|
||
|
] while drop ! Drop (vm 10 /i) result from the while condition.
|
||
|
] when
|
||
|
vrIsTrailingZeros [
|
||
|
over 5 = [
|
||
|
vr even? [ 4 -rot nip ] when ! 4 lastRemovedDigit!
|
||
|
] when
|
||
|
] when
|
||
|
vr pick 5 >= [ 1 + ] [
|
||
|
dup vm = [
|
||
|
acceptBounds vmIsTrailingZeros and not [ 1 + ] when
|
||
|
] when
|
||
|
] if
|
||
|
] [
|
||
|
! common
|
||
|
[ vp 10 /i vm 10 /i 2dup > ] [
|
||
|
vm! vp!
|
||
|
vr 10 /mod -roll vr! nip ! lastRemovedDigit!
|
||
|
1 - ! vplength!
|
||
|
] while 2drop
|
||
|
vr dup vm = [ 1 + ] [
|
||
|
pick 5 >= [ 1 + ] when
|
||
|
] if
|
||
|
] if nipd ; inline
|
||
|
|
||
|
: write-char ( index seq char -- index+1 seq' )
|
||
|
-rot [ tuck ] dip [ set-nth 1 + ] keep ; inline
|
||
|
|
||
|
: write-exp ( exp index result -- result' )
|
||
|
CHAR: e write-char
|
||
|
pick neg? [
|
||
|
CHAR: - write-char [ neg ] 2dip
|
||
|
] when
|
||
|
pick dup 100 >= [
|
||
|
100 /i CHAR: 0 + write-char
|
||
|
[ 100 mod 2 * ] 2dip
|
||
|
pick DIGIT_TABLE nth write-char
|
||
|
[ 1 + DIGIT_TABLE nth ] 2dip [ set-nth ] keep
|
||
|
] [
|
||
|
10 >= [
|
||
|
[ 2 * ] 2dip
|
||
|
pick DIGIT_TABLE nth write-char
|
||
|
[ 1 + DIGIT_TABLE nth ] 2dip [ set-nth ] keep
|
||
|
] [
|
||
|
[ CHAR: 0 + ] 2dip [ set-nth ] keep
|
||
|
] if
|
||
|
] if ; inline
|
||
|
|
||
|
:: produce-output ( exp sign olength output2! -- string )
|
||
|
25 <vector> 0 :> ( result i! )
|
||
|
0 sign [ CHAR: - swap result set-nth 1 ] when :> index!
|
||
|
[ output2 10000 >= ] [
|
||
|
output2 dup 10000 /i dup output2! 10000 * - :> c
|
||
|
index olength + i - 1 - :> res-index
|
||
|
c 100 mod 2 *
|
||
|
dup DIGIT_TABLE nth res-index result set-nth
|
||
|
1 + DIGIT_TABLE nth res-index 1 + result set-nth
|
||
|
c 100 /i 2 *
|
||
|
dup DIGIT_TABLE nth res-index 2 - result set-nth
|
||
|
1 + DIGIT_TABLE nth res-index 1 - result set-nth
|
||
|
i 4 + i!
|
||
|
] while
|
||
|
output2 100 >= [
|
||
|
output2 dup 100 /i dup output2! 100 * - 2 * :> c
|
||
|
index olength + i - :> res-index
|
||
|
c DIGIT_TABLE nth res-index 1 - result set-nth
|
||
|
c 1 + DIGIT_TABLE nth res-index result set-nth
|
||
|
i 2 + i!
|
||
|
] when
|
||
|
output2 10 >= [
|
||
|
output2 2 * :> c
|
||
|
index olength + i - :> res-index
|
||
|
c 1 + DIGIT_TABLE nth res-index result set-nth
|
||
|
c DIGIT_TABLE nth index result set-nth
|
||
|
] [ CHAR: 0 output2 + index result set-nth ] if
|
||
|
index 1 + index!
|
||
|
olength 1 > [
|
||
|
CHAR: . index result set-nth
|
||
|
index olength + index!
|
||
|
] when exp index result write-exp >string ; inline
|
||
|
|
||
|
PRIVATE>
|
||
|
|
||
|
:: print-float ( value -- string )
|
||
|
value >float unpack-bits [
|
||
|
[ 5drop ] dip
|
||
|
] [| e2 m2 acceptBounds ieeeExponent<=1 sign |
|
||
|
m2 4 * :> mv
|
||
|
mantissaBits 2^ m2 = not ieeeExponent<=1 or 1 0 ? :> mmShift
|
||
|
f f 0 0 0 :> ( vmIsTrailingZeros! vrIsTrailingZeros! e10! vr! vm! )
|
||
|
! After the following loop vp is left on stack.
|
||
|
e2 0 >= [
|
||
|
e2 DOUBLE_LOG10_2_NUMERATOR * DOUBLE_LOG10_2_DENOMINATOR /i 0 max :> q
|
||
|
q e10!
|
||
|
q double-pow-5-bits DOUBLE_POW5_INV_BITCOUNT + 1 - :> k
|
||
|
q k + e2 - :> i
|
||
|
mmShift m2 q DOUBLE_POW5_INV_SPLIT nth i mul-shift-all vr! swap vm! ! vp on stack
|
||
|
q 21 <= [
|
||
|
mv 5 divisor? [
|
||
|
q mv multiple-of-power-of-5 vrIsTrailingZeros!
|
||
|
] [
|
||
|
acceptBounds [
|
||
|
q mv mmShift - 1 - multiple-of-power-of-5 vmIsTrailingZeros!
|
||
|
] [
|
||
|
q mv 2 + multiple-of-power-of-5 1 0 ? - ! vp!
|
||
|
] if
|
||
|
] if
|
||
|
] when
|
||
|
] [ ! e2 < 0
|
||
|
e2 neg DOUBLE_LOG10_5_NUMERATOR * DOUBLE_LOG10_5_DENOMINATOR /i 1 - 0 max :> q
|
||
|
q e2 + e10!
|
||
|
e2 neg q - :> i
|
||
|
i double-pow-5-bits DOUBLE_POW5_BITCOUNT - :> k
|
||
|
q k - :> j
|
||
|
mmShift m2 i DOUBLE_POW5_SPLIT nth j mul-shift-all vr! swap vm! ! vp on stack
|
||
|
q 1 <= [
|
||
|
mv 1 bitand bitnot q >= vrIsTrailingZeros!
|
||
|
acceptBounds [
|
||
|
mv 1 - mmShift - bitnot 1 bitand q >= vmIsTrailingZeros!
|
||
|
] [ 1 - ] if ! vp!
|
||
|
] [
|
||
|
q 63 < [
|
||
|
q 1 - 2^ 1 - mv bitand zero? vrIsTrailingZeros!
|
||
|
] when
|
||
|
] if
|
||
|
] if
|
||
|
dup decimal-length ! vp vplength
|
||
|
dup e10 + 1 - sign 2swap ! exp and sign for produce-output
|
||
|
acceptBounds vmIsTrailingZeros vrIsTrailingZeros vr vm
|
||
|
prepare-output produce-output
|
||
|
] if* ;
|
||
|
|
||
|
ALIAS: d2s print-float
|