123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748 |
- {
- Copyright (C) 2013 by Max Nazhalov
- This file contains generalized floating point<->ASCII conversion routines.
- It is included by the FLT_CONV.INC after setting-up correct conditional
- definitions, therefore it sholud not be used directly.
- Refer to FLT_CONV.INC for further explanation.
- This library is free software; you can redistribute it and/or modify it
- under the terms of the GNU Library General Public License as published by
- the Free Software Foundation; either version 2 of the License, or (at your
- option) any later version with the following modification:
- As a special exception, the copyright holders of this library give you
- permission to link this library with independent modules to produce an
- executable, regardless of the license terms of these independent modules,
- and to copy and distribute the resulting executable under terms of your
- choice, provided that you also meet, for each linked independent module,
- the terms and conditions of the license of that module. An independent
- module is a module which is not derived from or based on this library.
- If you modify this library, you may extend this exception to your version
- of the library, but you are not obligated to do so. If you do not wish to
- do so, delete this exception statement from your version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- See the GNU Library General Public License for more details.
- You should have received a copy of the GNU Library General Public License
- along with this library; if not, write to the Free Software Foundation,
- Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- ****************************************************************************
- }
- type
- // "Do-It-Yourself Floating Point" structure
- TDIY_FP = record
- {$ifdef VALREAL_128}
- fh : qword;
- {$endif}
- {$ifdef VALREAL_32}
- f : dword;
- {$else}
- f : qword;
- {$endif}
- {$ifdef VALREAL_80}
- fh : dword;
- {$endif}
- e : integer;
- end;
- TDIY_FP_Power_of_10 = record
- c : TDIY_FP;
- e10 : integer;
- end;
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- (****************************************************************************
- * diy_util_add
- *
- * Helper routine: summ of multiword unsigned integers
- *
- * Used by:
- * [80,128] diy_fp_cached_power10
- * [80,128] diy_fp_multiply
- * [80,128] float<->ASCII
- *
- ****************************************************************************)
- {$ifdef VALREAL_80}
- procedure diy_util_add( var xh: dword; var xl: qword; const yh: dword; const yl: qword ); {$ifdef grisu1_inline}inline;{$endif}
- {$else VALREAL_128}
- procedure diy_util_add( var xh, xl: qword; const yh, yl: qword ); {$ifdef grisu1_inline}inline;{$endif}
- {$endif VALREAL_*}
- var
- temp: qword;
- begin
- temp := xl + yl;
- xh := xh + yh + ord( temp < xl );
- xl := temp;
- end;
- (****************************************************************************
- * diy_util_shl
- *
- * Helper routine: left shift of multiword unsigned integer
- *
- * Used by:
- * [80,128] float<->ASCII
- *
- ****************************************************************************)
- {$ifdef VALREAL_80}
- procedure diy_util_shl( var h: dword; var l: qword; const count: integer );
- {$else VALREAL_128}
- procedure diy_util_shl( var h, l: qword; const count: integer );
- {$endif VALREAL_*}
- begin
- if ( count = 0 ) then
- exit;
- {$ifdef grisu1_debug}
- assert( count > 0 );
- {$ifdef VALREAL_80}
- assert( count < 96 );
- {$else VALREAL_128}
- assert( count < 128 );
- {$endif VALREAL_*}
- {$endif grisu1_debug}
- if ( count = 1 ) then
- begin
- diy_util_add( h, l, h, l );
- exit;
- end;
- if ( count >= 64 ) then
- begin
- if ( count > 64 ) then
- h := ( l shl ( count - 64 ) )
- else
- h := l;
- l := 0;
- exit;
- end;
- {$ifdef VALREAL_80}
- if ( count = 32 ) then
- h := hi( l )
- else
- {$endif VALREAL_80}
- if ( count < 32 ) then
- h := ( h shl count ) + ( hi( l ) shr ( 32 - count ) )
- else
- {$ifdef VALREAL_80}
- h := ( l shr ( 64 - count ) );
- {$else VALREAL_128}
- h := ( h shl count ) + ( l shr ( 64 - count ) );
- {$endif VALREAL_*}
- l := ( l shl count );
- end;
- (****************************************************************************
- * diy_util_shr
- *
- * Helper routine: right shift of multiword unsigned integer
- *
- * Used by:
- * [80,128] float<->ASCII
- *
- ****************************************************************************)
- {$ifdef VALREAL_80}
- procedure diy_util_shr( var h: dword; var l: qword; const count: integer );
- {$else VALREAL_128}
- procedure diy_util_shr( var h, l: qword; const count: integer );
- {$endif VALREAL_*}
- begin
- if ( count = 0 ) then
- exit;
- {$ifdef grisu1_debug}
- assert( count > 0 );
- {$endif grisu1_debug}
- if ( count = 1 ) then
- begin
- l := l shr 1;
- if ( lo(h) and 1 <> 0 ) then
- l := l or qword($8000000000000000);
- h := h shr 1;
- exit;
- end;
- if ( count < 64 ) then
- begin
- l := ( qword( h ) shl ( ( - count ) and 63 ) ) or ( l shr count );
- {$ifdef VALREAL_80}
- if ( count >= 32 ) then
- h := 0
- else
- {$endif VALREAL_80}
- h := h shr count;
- exit;
- end;
- {$ifdef VALREAL_80}
- if ( count < 96 ) then
- {$else VALREAL_128}
- if ( count < 128 ) then
- {$endif VALREAL_*}
- l := h shr ( count and 63 )
- else
- l := 0;
- h := 0;
- end;
- {$endif VALREAL_80 | VALREAL_128}
- (****************************************************************************
- * diy_fp_multiply
- *
- * "Do-It-Yourself Floating Point" multiplication routine
- *
- * Simplified implementation:
- * > restricted input:
- * - both operands should be normalized
- * > relaxed output:
- * - rounding is simple [half is rounded-up]
- * - normalization is optional and performed at the very end if requested
- * [at most 1 shift required since both multipliers are normalized]
- *
- * Used by:
- * [all] float<->ASCII
- * [>32] diy_fp_cached_power10
- *
- ****************************************************************************)
- function diy_fp_multiply( const x, y: TDIY_FP; normalize: boolean ): TDIY_FP;
- const
- C_1_SHL_31 = dword($80000000);
- {$ifdef VALREAL_32}
- //***************** 32-bit *********************
- var
- a, b, c, d, ac, bc, ad, bd, t1: dword;
- begin
- a := ( x.f shr 16 );
- b := ( x.f and $0000FFFF );
- c := ( y.f shr 16 );
- d := ( y.f and $0000FFFF );
- ac := a * c;
- bc := b * c;
- ad := a * d;
- bd := b * d;
- t1 := ( bc and $0000FFFF )
- + ( bd shr 16 )
- + ( ad and $0000FFFF )
- + ( 1 shl 15 ); // round
- diy_fp_multiply.f := ac
- + ( ad shr 16 )
- + ( bc shr 16 )
- + ( t1 shr 16 );
- diy_fp_multiply.e := x.e + y.e + 32;
- if normalize then with diy_fp_multiply do
- begin
- if ( f and C_1_SHL_31 = 0 ) then
- begin
- inc( f, f );
- dec( e );
- end;
- {$ifdef grisu1_debug}
- assert( f and C_1_SHL_31 <> 0 );
- {$endif grisu1_debug}
- end;
- end;
- {$else not VALREAL_32}
- (*-------------------------------------------------------
- | u32_mul_u32_to_u64 [local]
- |
- | Local routine of the "diy_fp_multiply"; common to float64..float128:
- | uint32 * uint32 -> uint64
- |
- *-------------------------------------------------------*)
- function u32_mul_u32_to_u64( const a, b: dword ): qword; {$ifdef grisu1_inline}inline;{$endif}
- begin
- // it seems this pattern is very tightly optimized by FPC at least for i386
- u32_mul_u32_to_u64 := qword( a ) * b;
- end;
- {$endif VALREAL_*}
- {$ifdef VALREAL_64}
- //***************** 64-bit *********************
- var
- a, b, c, d: dword;
- ac, bc, ad, bd, t1: qword;
- begin
- a := hi( x.f ); b := x.f;
- c := hi( y.f ); d := y.f;
- ac := u32_mul_u32_to_u64( a, c );
- bc := u32_mul_u32_to_u64( b, c );
- ad := u32_mul_u32_to_u64( a, d );
- bd := u32_mul_u32_to_u64( b, d );
- t1 := qword( C_1_SHL_31 ) // round
- + hi( bd ) + lo( bc ) + lo( ad );
- diy_fp_multiply.f := ac + hi( ad ) + hi( bc ) + hi( t1 );
- diy_fp_multiply.e := x.e + y.e + 64;
- if normalize then with diy_fp_multiply do
- begin
- if ( hi( f ) and C_1_SHL_31 = 0 ) then
- begin
- inc( f, f );
- dec( e );
- end;
- {$ifdef grisu1_debug}
- assert( hi( f ) and C_1_SHL_31 <> 0 );
- {$endif grisu1_debug}
- end;
- end;
- {$endif VALREAL_64}
- {$ifdef VALREAL_80}
- //***************** 96-bit *********************
- var
- a, b, c, u, v, w: dword;
- au, av, aw, bu, bv, bw, cu, cv, cw, t1, t2: qword;
- begin
- a := x.fh; b := hi( x.f ); c := x.f;
- u := y.fh; v := hi( y.f ); w := y.f;
- au := u32_mul_u32_to_u64( a, u );
- bu := u32_mul_u32_to_u64( b, u );
- cu := u32_mul_u32_to_u64( c, u );
- av := u32_mul_u32_to_u64( a, v );
- bv := u32_mul_u32_to_u64( b, v );
- cv := u32_mul_u32_to_u64( c, v );
- aw := u32_mul_u32_to_u64( a, w );
- bw := u32_mul_u32_to_u64( b, w );
- cw := u32_mul_u32_to_u64( c, w );
- t1 := ( cw shr 32 ) + lo( bw ) + lo( cv );
- t1 := qword( C_1_SHL_31 ) // round
- + hi( t1 ) + hi( bw ) + hi( cv ) + lo( aw ) + lo( bv ) + lo( cu );
- t1 := ( t1 shr 32 ) + hi( aw ) + hi( bv ) + hi( cu ) + lo( av ) + lo( bu );
- t2 := au + hi( av ) + hi( bu ) + hi( t1 );
- diy_fp_multiply.f := ( t2 shl 32 ) + lo( t1 );
- diy_fp_multiply.fh := hi( t2 );
- diy_fp_multiply.e := x.e + y.e + 96;
- if normalize then with diy_fp_multiply do
- begin
- if ( fh and C_1_SHL_31 = 0 ) then
- begin
- diy_util_add( fh, f, fh, f );
- dec( e );
- end;
- {$ifdef grisu1_debug}
- assert( fh and C_1_SHL_31 <> 0 );
- {$endif grisu1_debug}
- end;
- end;
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- //***************** 128-bit ********************
- var
- a, b, c, d, u, v, w, z: dword;
- au, av, aw, az, bu, bv, bw, bz, cu, cv, cw, cz, du, dv, dw, dz, t1, t2: qword;
- begin
- a := hi( x.fh ); b := x.fh; c := hi( x.f ); d := x.f;
- u := hi( y.fh ); v := y.fh; w := hi( y.f ); z := y.f;
- au := u32_mul_u32_to_u64( a, u );
- bu := u32_mul_u32_to_u64( b, u );
- cu := u32_mul_u32_to_u64( c, u );
- du := u32_mul_u32_to_u64( d, u );
- av := u32_mul_u32_to_u64( a, v );
- bv := u32_mul_u32_to_u64( b, v );
- cv := u32_mul_u32_to_u64( c, v );
- dv := u32_mul_u32_to_u64( d, v );
- aw := u32_mul_u32_to_u64( a, w );
- bw := u32_mul_u32_to_u64( b, w );
- cw := u32_mul_u32_to_u64( c, w );
- dw := u32_mul_u32_to_u64( d, w );
- az := u32_mul_u32_to_u64( a, z );
- bz := u32_mul_u32_to_u64( b, z );
- cz := u32_mul_u32_to_u64( c, z );
- dz := u32_mul_u32_to_u64( d, z );
- t1 := ( dz shr 32 ) + lo( cz ) + lo( dw );
- t1 := ( t1 shr 32 ) + hi( cz ) + hi( dw ) + lo( bz ) + lo( cw ) + lo( dv );
- t1 := qword( C_1_SHL_31 ) // round
- + hi( t1 ) + hi( bz ) + hi( cw ) + hi( dv ) + lo( az ) + lo( bw ) + lo( cv ) + lo( du );
- t2 := ( t1 shr 32 ) + hi( az ) + hi( bw ) + hi( cv ) + hi( du ) + lo( aw ) + lo( bv ) + lo( cu );
- t1 := ( t2 shr 32 ) + hi( aw ) + hi( bv ) + hi( cu ) + lo( av ) + lo( bu );
- diy_fp_multiply.f := ( t1 shl 32 ) + lo( t2 );
- diy_fp_multiply.fh := au + hi( av ) + hi( bu ) + hi( t1 );
- diy_fp_multiply.e := x.e + y.e + 128;
- if normalize then with diy_fp_multiply do
- begin
- if ( hi( fh ) and C_1_SHL_31 = 0 ) then
- begin
- diy_util_add( fh, f, fh, f );
- dec( e );
- end;
- {$ifdef grisu1_debug}
- assert( hi( fh ) and C_1_SHL_31 <> 0 );
- {$endif grisu1_debug}
- end;
- end;
- {$endif VALREAL_128}
- (****************************************************************************
- * diy_fp_cached_power10
- *
- * The main purpose of this routine is to return normalized correctly rounded
- * DIY-floating-point approximation of the power of 10, which has to be used
- * by the Grisu1 as a scaling factor, intended to shift a binary exponent of
- * the original number into selected [ alpha .. gamma ] range.
- *
- * This routine is also usable as a helper during ASCII -> float conversion,
- * so the range of cached powers is slightly extended beyond the requirements
- * of the Grisu1.
- *
- * Used by:
- * [all] float<->ASCII
- *
- ****************************************************************************)
- procedure diy_fp_cached_power10( exp10: integer; out factor: TDIY_FP_Power_of_10 );
- {$ifdef VALREAL_32}
- const
- // alpha =-29; gamma = 0; step = 1E+8
- cache: array [ 0 .. 13 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: dword($FB158593); e: -218 ); e10: -56 ),
- ( c: ( f: dword($BB127C54); e: -191 ); e10: -48 ),
- ( c: ( f: dword($8B61313C); e: -164 ); e10: -40 ),
- ( c: ( f: dword($CFB11EAD); e: -138 ); e10: -32 ),
- ( c: ( f: dword($9ABE14CD); e: -111 ); e10: -24 ),
- ( c: ( f: dword($E69594BF); e: -85 ); e10: -16 ),
- ( c: ( f: dword($ABCC7712); e: -58 ); e10: -8 ),
- ( c: ( f: dword($80000000); e: -31 ); e10: 0 ),
- ( c: ( f: dword($BEBC2000); e: -5 ); e10: 8 ),
- ( c: ( f: dword($8E1BC9BF); e: 22 ); e10: 16 ),
- ( c: ( f: dword($D3C21BCF); e: 48 ); e10: 24 ),
- ( c: ( f: dword($9DC5ADA8); e: 75 ); e10: 32 ),
- ( c: ( f: dword($EB194F8E); e: 101 ); e10: 40 ),
- ( c: ( f: dword($AF298D05); e: 128 ); e10: 48 )
- );
- var
- i, min10: integer;
- begin
- // find index
- min10 := cache[ low( cache ) ].e10;
- if ( exp10 <= min10 ) then
- i := 0
- else
- begin
- i := ( exp10 - min10 ) div 8;
- if ( i >= high(cache) ) then
- i := high(cache)
- else
- if ( cache[ i ].e10 <> exp10 ) then
- inc( i ); // round-up
- end;
- // generate result
- factor := cache[ i ];
- end;
- {$endif VALREAL_32}
- //**************************************
- {$ifdef VALREAL_64}
- const
- // alpha =-61; gamma = 0
- // full cache: 1E-450 .. 1E+432, step = 1E+18
- // sparse = 1/10
- C_PWR10_DELTA = 18;
- C_PWR10_COUNT = 50;
- base: array [ 0 .. 9 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($825ECC24C8737830); e: -362 ); e10: -90 ),
- ( c: ( f: qword($E2280B6C20DD5232); e: -303 ); e10: -72 ),
- ( c: ( f: qword($C428D05AA4751E4D); e: -243 ); e10: -54 ),
- ( c: ( f: qword($AA242499697392D3); e: -183 ); e10: -36 ),
- ( c: ( f: qword($9392EE8E921D5D07); e: -123 ); e10: -18 ),
- ( c: ( f: qword($8000000000000000); e: -63 ); e10: 0 ),
- ( c: ( f: qword($DE0B6B3A76400000); e: -4 ); e10: 18 ),
- ( c: ( f: qword($C097CE7BC90715B3); e: 56 ); e10: 36 ),
- ( c: ( f: qword($A70C3C40A64E6C52); e: 116 ); e10: 54 ),
- ( c: ( f: qword($90E40FBEEA1D3A4B); e: 176 ); e10: 72 )
- );
- factor_plus: array [ 0 .. 1 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($F6C69A72A3989F5C); e: 534 ); e10: 180 ),
- ( c: ( f: qword($EDE24AE798EC8284); e: 1132 ); e10: 360 )
- );
- factor_minus: array [ 0 .. 1 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($84C8D4DFD2C63F3B); e: -661 ); e10: -180 ),
- ( c: ( f: qword($89BF722840327F82); e: -1259 ); e10: -360 )
- );
- corrector: array [ 0 .. C_PWR10_COUNT - 1 ] of shortint = (
- // extra mantissa correction [ulp; signed]
- 0, 0, 0, 0, 1, 0, 0, 0, 1, -1,
- 0, 1, 1, 1, -1, 0, 0, 1, 0, -1,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- -1, 0, 0, -1, 0, 0, 0, 0, 0, -1,
- 0, 0, 0, 0, 1, 0, 0, 0, -1, 0
- );
- {$endif VALREAL_64}
- //**************************************
- {$ifdef VALREAL_80}
- const
- // alpha =-93; gamma =+30
- // full cache: 1E-5032 .. 1E+4995, step = 1E+37
- // sparse = 1/16
- C_PWR10_DELTA = 37;
- C_PWR10_COUNT = 272;
- base: array [ 0 .. 15 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($07286FAA1AF5AF66); fh: dword($D1476E2C); e: -1079 ); e10: -296 ),
- ( c: ( f: qword($99107C22CB550FB4); fh: dword($C4CE17B3); e: -956 ); e10: -259 ),
- ( c: ( f: qword($99F6858428E2557B); fh: dword($B9131798); e: -833 ); e10: -222 ),
- ( c: ( f: qword($4738705E9624AB51); fh: dword($AE0B158B); e: -710 ); e10: -185 ),
- ( c: ( f: qword($0D5FDAF5C13E60D1); fh: dword($A3AB6658); e: -587 ); e10: -148 ),
- ( c: ( f: qword($163FA42E504BCED2); fh: dword($99EA0196); e: -464 ); e10: -111 ),
- ( c: ( f: qword($483BB9B9B1C6F22B); fh: dword($90BD77F3); e: -341 ); e10: -74 ),
- ( c: ( f: qword($545C75757E50D641); fh: dword($881CEA14); e: -218 ); e10: -37 ),
- ( c: ( f: qword($0000000000000000); fh: dword($80000000); e: -95 ); e10: 0 ),
- ( c: ( f: qword($BB48DB201E86D400); fh: dword($F0BDC21A); e: 27 ); e10: 37 ),
- ( c: ( f: qword($4DCDAB14C696963C); fh: dword($E264589A); e: 150 ); e10: 74 ),
- ( c: ( f: qword($C1D1EA966C9E18AC); fh: dword($D4E5E2CD); e: 273 ); e10: 111 ),
- ( c: ( f: qword($C8965D3D6F928295); fh: dword($C83553C5); e: 396 ); e10: 148 ),
- ( c: ( f: qword($96706114873D5D9F); fh: dword($BC4665B5); e: 519 ); e10: 185 ),
- ( c: ( f: qword($56105DAD7425A83F); fh: dword($B10D8E14); e: 642 ); e10: 222 ),
- ( c: ( f: qword($B84603568A892ABB); fh: dword($A67FF273); e: 765 ); e10: 259 )
- );
- factor_plus: array [ 0 .. 7 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($3576D3D149738BA0); fh: dword($BF87DECC); e: 1871 ); e10: 592 ),
- ( c: ( f: qword($750E83050A40DE03); fh: dword($8F4C0691); e: 3838 ); e10: 1184 ),
- ( c: ( f: qword($727E5D9756BC4BF8); fh: dword($D66B8D68); e: 5804 ); e10: 1776 ),
- ( c: ( f: qword($CE9DB63FD51AF6A3); fh: dword($A06C0BD4); e: 7771 ); e10: 2368 ),
- ( c: ( f: qword($5A7ADBC5B8787D89); fh: dword($F00B82D7); e: 9737 ); e10: 2960 ),
- ( c: ( f: qword($22D732D7AE7EDAA7); fh: dword($B397FD9A); e: 11704 ); e10: 3552 ),
- ( c: ( f: qword($CCD2839E0367500B); fh: dword($865DB7A9); e: 13671 ); e10: 4144 ),
- ( c: ( f: qword($FCBEE713F3BE171A); fh: dword($C90E78C7); e: 15637 ); e10: 4736 )
- );
- factor_minus: array [ 0 .. 7 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($2F85DC7AE66FEACF); fh: dword($AB15B5D2); e: -2062 ); e10: -592 ),
- ( c: ( f: qword($4237088F4C7284FA); fh: dword($E4AC057C); e: -4029 ); e10: -1184 ),
- ( c: ( f: qword($D2DCB34CEC42875C); fh: dword($98D24C2F); e: -5995 ); e10: -1776 ),
- ( c: ( f: qword($B50918191D8106CD); fh: dword($CC42DD5C); e: -7962 ); e10: -2368 ),
- ( c: ( f: qword($10CF24303CA163B8); fh: dword($8881FC6C); e: -9928 ); e10: -2960 ),
- ( c: ( f: qword($BF10EA474FE1E9B1); fh: dword($B674CE73); e: -11895 ); e10: -3552 ),
- ( c: ( f: qword($478E074A0E85FC7F); fh: dword($F3DEFE25); e: -13862 ); e10: -4144 ),
- ( c: ( f: qword($A3BD093CC62364C2); fh: dword($A2FAA242); e: -15828 ); e10: -4736 )
- );
- corrector: array [ 0 .. C_PWR10_COUNT - 1 ] of shortint = (
- // extra mantissa correction [ulp; signed]
- 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, -1, 1, 0, 0,
- 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 1, 2, 2, 0, 1, 1, 0, 0, -2, 0,
- 2, 0, 1, 1, 1, 1, 1, 2, 0, 0, 2, 1, 0, 1, 0, 0,
- 0, 0, 1, -1, 0, 0, 1, 1, 0, 0, 1, 0, -1, 0, -1, 0,
- 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, -1, 0, -1, 1,
- 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 0,
- -1, 0, 0, -1, 0, -1, 1, 1, 0, -1, 0, 0, -1, -1, -1, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- -1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0,
- 2, 2, 1, 1, 0, 0, 0, 2, 0, 0, 1, 1, 0, 0, 1, 1,
- 0, 0, 1, 0, 0, 0, 1, 2, 0, 0, 1, 0, 0, 0, -1, 0,
- 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 1, -1, 1, 0, 1,
- 0, 0, 0, -1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0,
- 0, 0, 1, 1, -1, 0, 0, 2, 0, 0, 1, 1, 0, 1, 1, 1,
- -1, -1, 1, -2, 0, 0, 0, -1, 1, -1, 1, -1, -1, -1, 0, 0,
- 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0
- );
- {$endif VALREAL_80}
- //**************************************
- {$ifdef VALREAL_128}
- const
- // alpha =-125; gamma = -2
- // full cache: 1E-5032 .. 1E+4995, step = 1E+37
- // sparse = 1/16
- C_PWR10_DELTA = 37;
- C_PWR10_COUNT = 272;
- base: array [ 0 .. 15 ] of TDIY_FP_Power_of_10 = (
- ( c: ( fh: qword($D1476E2C07286FAA); f: qword($1AF5AF660DB4AEE2); e: -1111 ); e10: -296 ),
- ( c: ( fh: qword($C4CE17B399107C22); f: qword($CB550FB4384D21D4); e: -988 ); e10: -259 ),
- ( c: ( fh: qword($B913179899F68584); f: qword($28E2557B59846E3F); e: -865 ); e10: -222 ),
- ( c: ( fh: qword($AE0B158B4738705E); f: qword($9624AB50B148D446); e: -742 ); e10: -185 ),
- ( c: ( fh: qword($A3AB66580D5FDAF5); f: qword($C13E60D0D2E0EBBA); e: -619 ); e10: -148 ),
- ( c: ( fh: qword($99EA0196163FA42E); f: qword($504BCED1BF8E4E46); e: -496 ); e10: -111 ),
- ( c: ( fh: qword($90BD77F3483BB9B9); f: qword($B1C6F22B5E6F48C3); e: -373 ); e10: -74 ),
- ( c: ( fh: qword($881CEA14545C7575); f: qword($7E50D64177DA2E55); e: -250 ); e10: -37 ),
- ( c: ( fh: qword($8000000000000000); f: qword($0000000000000000); e: -127 ); e10: 0 ),
- ( c: ( fh: qword($F0BDC21ABB48DB20); f: qword($1E86D40000000000); e: -5 ); e10: 37 ),
- ( c: ( fh: qword($E264589A4DCDAB14); f: qword($C696963C7EED2DD2); e: 118 ); e10: 74 ),
- ( c: ( fh: qword($D4E5E2CDC1D1EA96); f: qword($6C9E18AC7007C91A); e: 241 ); e10: 111 ),
- ( c: ( fh: qword($C83553C5C8965D3D); f: qword($6F92829494E5ACC7); e: 364 ); e10: 148 ),
- ( c: ( fh: qword($BC4665B596706114); f: qword($873D5D9F0DDE1FEF); e: 487 ); e10: 185 ),
- ( c: ( fh: qword($B10D8E1456105DAD); f: qword($7425A83E872C5F47); e: 610 ); e10: 222 ),
- ( c: ( fh: qword($A67FF273B8460356); f: qword($8A892ABAF368F137); e: 733 ); e10: 259 )
- );
- factor_plus: array [ 0 .. 7 ] of TDIY_FP_Power_of_10 = (
- ( c: ( fh: qword($BF87DECC3576D3D1); f: qword($49738B9F99B4642D); e: 1839 ); e10: 592 ),
- ( c: ( fh: qword($8F4C0691750E8305); f: qword($0A40DE037C9AD730); e: 3806 ); e10: 1184 ),
- ( c: ( fh: qword($D66B8D68727E5D97); f: qword($56BC4BF837B34968); e: 5772 ); e10: 1776 ),
- ( c: ( fh: qword($A06C0BD4CE9DB63F); f: qword($D51AF6A3244A6983); e: 7739 ); e10: 2368 ),
- ( c: ( fh: qword($F00B82D75A7ADBC5); f: qword($B8787D891AB45D5B); e: 9705 ); e10: 2960 ),
- ( c: ( fh: qword($B397FD9A22D732D7); f: qword($AE7EDAA76FBBD923); e: 11672 ); e10: 3552 ),
- ( c: ( fh: qword($865DB7A9CCD2839E); f: qword($0367500A8E9A1790); e: 13639 ); e10: 4144 ),
- ( c: ( fh: qword($C90E78C7FCBEE713); f: qword($F3BE171A27BF81DB); e: 15605 ); e10: 4736 )
- );
- factor_minus: array [ 0 .. 7 ] of TDIY_FP_Power_of_10 = (
- ( c: ( fh: qword($AB15B5D22F85DC7A); f: qword($E66FEACEB7836F69); e: -2094 ); e10: -592 ),
- ( c: ( fh: qword($E4AC057C4237088F); f: qword($4C7284F9EDDA793D); e: -4061 ); e10: -1184 ),
- ( c: ( fh: qword($98D24C2FD2DCB34C); f: qword($EC42875C0B22B986); e: -6027 ); e10: -1776 ),
- ( c: ( fh: qword($CC42DD5CB5091819); f: qword($1D8106CCF8EE85B4); e: -7994 ); e10: -2368 ),
- ( c: ( fh: qword($8881FC6C10CF2430); f: qword($3CA163B873AA88A6); e: -9960 ); e10: -2960 ),
- ( c: ( fh: qword($B674CE73BF10EA47); f: qword($4FE1E9B0FCDF7B3D); e: -11927 ); e10: -3552 ),
- ( c: ( fh: qword($F3DEFE25478E074A); f: qword($0E85FC7F4EDBD3CB); e: -13894 ); e10: -4144 ),
- ( c: ( fh: qword($A2FAA242A3BD093C); f: qword($C62364C260A887E2); e: -15860 ); e10: -4736 )
- );
- corrector: array [ 0 .. C_PWR10_COUNT - 1 ] of shortint = (
- // extra mantissa correction [ulp; signed]
- 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0,
- -1, -1, 0, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0,
- 1, 0, 0, 0, 1, -1, 0, -1, -1, 1, 0, 1, 0, 0, 1, 1,
- 0, -2, 0, 0, 0, -1, 0, 0, 0, 0, -2, 0, 0, 0, 0, 0,
- 0, -1, 1, 0, 1, 0, 0, -1, 0, 1, 0, 0, 1, 0, 1, 1,
- 1, -1, 0, 0, 1, -1, 0, 0, 0, 1, 0, 1, 1, -1, 1, 1,
- 0, 0, 1, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1,
- 0, 0, 2, 1, 0, -1, -1, -1, -1, 0, -1, 1, 0, -1, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, -1, 1, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0, 0,
- 1, -1, 2, 1, 2, 0, -1, 1, 0, 0, 0, 1, 2, 0, 1, 1,
- 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1,
- 0, 0, 0, 1, 0, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1,
- -1, -1, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0,
- 0, 0, 1, 0, -1, 0, 0, 0, -1, 0, -1, 0, 1, 0, 0, -1,
- 0, -1, 1, -1, 1, -1, 0, -1, 0, 1, -1, 0, 1, 1, 1, 1,
- 0, -1, 1, -1, 0, -2, 0, -1, -1, 0, -1, 0, 0, -1, 0, 0
- );
- {$endif VALREAL_128}
- //**************************************
- {$ifndef VALREAL_32} // common for float64..float128
- var
- i, xmul, inod, min10: integer;
- A: TDIY_FP_Power_of_10;
- {$ifdef VALREAL_80}
- ch: dword;
- {$endif}
- {$ifdef VALREAL_128}
- ch: qword;
- {$endif}
- cx: shortint;
- begin
- // find non-sparse index
- min10 := base [ low( base ) ].e10 + factor_minus[ high( factor_minus ) ].e10;
- if ( exp10 <= min10 ) then
- i := 0
- else
- begin
- i := ( exp10 - min10 ) div C_PWR10_DELTA;
- if ( i * C_PWR10_DELTA + min10 <> exp10 ) then
- inc( i ); // round-up
- if ( i > C_PWR10_COUNT - 1 ) then
- i := C_PWR10_COUNT - 1;
- end;
- // generate result
- inod := i mod length( base );
- xmul := ( i div length( base ) ) - length( factor_minus );
- if ( xmul = 0 ) then
- begin
- // base
- factor := base[ inod ];
- exit;
- end;
- // surrogate
- A := base[ inod ];
- if ( xmul > 0 ) then
- begin
- dec( xmul );
- factor.e10 := A.e10 + factor_plus[ xmul ].e10;
- if ( A.e10 <> 0 ) then
- factor.c := diy_fp_multiply( A.c, factor_plus[ xmul ].c, TRUE )
- else
- begin
- // exact
- factor.c := factor_plus[ xmul ].c;
- exit;
- end;
- end
- else
- begin
- xmul := - ( xmul + 1 );
- factor.e10 := A.e10 + factor_minus[ xmul ].e10;
- if ( A.e10 <> 0 ) then
- factor.c := diy_fp_multiply( A.c, factor_minus[ xmul ].c, TRUE )
- else
- begin
- // exact
- factor.c := factor_minus[ xmul ].c;
- exit;
- end;
- end;
- // adjust mantissa
- cx := corrector[ i ];
- if ( cx <> 0 ) then
- {$ifdef VALREAL_64}
- inc( factor.c.f, int64( cx ) );
- {$else VALREAL_80 | VALREAL_128}
- begin
- ch := 0;
- if ( cx < 0 ) then
- dec( ch );
- diy_util_add( factor.c.fh, factor.c.f, ch, int64( cx ) );
- end;
- {$endif VALREAL_*}
- //
- end;
- {$endif VALREAL_64..VALREAL_128}
- (*==========================================================================*
- * *
- * Float -> ASCII *
- * *
- *==========================================================================*)
- procedure str_real( min_width, frac_digits: integer; const v: ValReal; real_type: TReal_Type; out str: shortstring );
- {$undef VALREAL_PACK}
- {$i flt_pack.inc}
- const
- {$ifdef VALREAL_32}
- C_FRAC2_BITS = 23;
- C_EXP2_BIAS = 127;
- C_DIY_FP_Q = 32;
- C_GRISU_ALPHA =-29;
- C_GRISU_GAMMA = 0;
- RT_NATIVE = RT_S32REAL;
- {$endif VALREAL_32}
- {$ifdef VALREAL_64}
- C_FRAC2_BITS = 52;
- C_EXP2_BIAS = 1023;
- C_DIY_FP_Q = 64;
- C_GRISU_ALPHA =-61;
- C_GRISU_GAMMA = 0;
- RT_NATIVE = RT_S64REAL;
- {$endif VALREAL_64}
- {$ifdef VALREAL_80}
- C_FRAC2_BITS = 63;
- C_EXP2_BIAS = 16383;
- C_DIY_FP_Q = 96;
- C_GRISU_ALPHA =-93;
- C_GRISU_GAMMA = 30;
- RT_NATIVE = RT_S80REAL;
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- C_FRAC2_BITS = 112;
- C_EXP2_BIAS = 16383;
- C_DIY_FP_Q = 128;
- C_GRISU_ALPHA =-125;
- C_GRISU_GAMMA =-2;
- RT_NATIVE = RT_S128REAL;
- {$endif VALREAL_128}
- (****************************************************************************)
- // handy const
- C_EXP2_SPECIAL = C_EXP2_BIAS * 2 + 1;
- {$ifdef VALREAL_32}
- C_MANT2_INTEGER = dword(1) shl C_FRAC2_BITS;
- {$endif VALREAL_32}
- {$if defined(VALREAL_64) or defined(VALREAL_80)}
- C_MANT2_INTEGER = qword(1) shl C_FRAC2_BITS;
- {$endif VALREAL_64 | VALREAL_80}
- {$ifdef VALREAL_128}
- C_MANT2_INTEGER_H = qword(1) shl ( C_FRAC2_BITS - 64 );
- {$endif VALREAL_128}
- C_MAX_WIDTH = 255; // shortstring
- {$ifdef fpc_softfpu_implementation}
- C_NO_MIN_WIDTH = -1; // just for convenience
- {$else}
- C_NO_MIN_WIDTH = -32767; // this value is the one used internally by FPC
- {$endif}
- type
- TAsciiDigits = array [ 0 .. 39 ] of byte;
- (*-------------------------------------------------------
- | gen_digits_32 [local]
- | gen_digits_64 [local] (used only for float64..float128 -> ASCII)
- |
- | These routines perform conversion of 32-bit/64-bit unsigned integer
- | to the sequence of byte-sized decimal digits.
- |
- *-------------------------------------------------------*)
- function gen_digits_32( var buf: TAsciiDigits; pos: integer; x: dword; pad_9zero: boolean = false ): integer;
- const
- digits: array [ 0 .. 9 ] of dword = (
- 0,
- 10,
- 100,
- 1000,
- 10000,
- 100000,
- 1000000,
- 10000000,
- 100000000,
- 1000000000
- );
- var
- n: integer;
- m, z: dword;
- begin
- // Calculate amount of digits
- if ( x = 0 ) then
- // emit nothing if padding is not required
- n := 0
- else
- begin
- n :=( ( BSRdword( x ) + 1 ) * 1233 ) shr 12;
- if ( x >= digits[ n ] ) then
- inc( n );
- end;
- if pad_9zero and ( n < 9 ) then
- n := 9;
- gen_digits_32 := n;
- // Emit digits
- m := x;
- while ( n > 0 ) do
- begin
- dec( n );
- if ( m <> 0 ) then
- begin
- z := m div 10;
- buf[ pos + n ] := m - z * 10;
- m := z;
- end
- else
- buf[ pos + n ] := 0;
- end;
- end;
- {$ifndef VALREAL_32}
- function gen_digits_64( var buf: TAsciiDigits; pos: integer; const x: qword; pad_19zero: boolean = false ): integer;
- var
- n_digits: integer;
- temp: qword;
- splitl, splitm, splith: dword;
- begin
- // Split X into 3 unsigned 32-bit integers; lower two should be less than 10 digits long
- if ( x < 1000000000 ) then
- begin
- splith := 0;
- splitm := 0;
- splitl := x;
- end
- else
- begin
- temp := x div 1000000000;
- splitl := x - temp * 1000000000;
- if ( temp < 1000000000 ) then
- begin
- splith := 0;
- splitm := temp;
- end
- else
- begin
- splith := temp div 1000000000;
- splitm := lo( temp ) - splith * 1000000000;
- end;
- end;
- // Generate digits
- n_digits := gen_digits_32( buf, pos, splith, false );
- if pad_19zero and ( n_digits = 0 ) then
- begin
- // at most 18 digits expected from splitm and splitl, so add one more
- buf[ pos ] := 0;
- n_digits := 1;
- end;
- inc( n_digits, gen_digits_32( buf, pos + n_digits, splitm, n_digits <> 0 ) );
- inc( n_digits, gen_digits_32( buf, pos + n_digits, splitl, n_digits <> 0 ) );
- gen_digits_64 := n_digits;
- end;
- {$endif VALREAL_64..VALREAL_128}
- (*-------------------------------------------------------
- | round_digits [local]
- |
- | Performs digit sequence rounding, returns decimal point correction.
- |
- *-------------------------------------------------------*)
- function round_digits( var buf: TAsciiDigits; var n_current: integer; n_max: integer; half_round_to_even: boolean = true ): integer;
- var
- n: integer;
- dig_round, dig_sticky: byte;
- {$ifdef GRISU1_F2A_AGRESSIVE_ROUNDUP}
- i: integer;
- {$endif}
- begin
- round_digits := 0;
- n := n_current;
- {$ifdef grisu1_debug}
- assert( n_max >= 0 );
- assert( n_max < n );
- {$endif grisu1_debug}
- n_current := n_max;
- // Get round digit
- dig_round := buf[n_max];
- {$ifdef GRISU1_F2A_AGRESSIVE_ROUNDUP}
- // Detect if rounding-up the second last digit turns the "dig_round"
- // into "5"; also make sure we have at least 1 digit between "dig_round"
- // and the second last.
- if not half_round_to_even then
- if ( dig_round = 4 ) and ( n_max < n - 3 ) then
- if ( buf[ n - 2 ] >= 8 ) then // somewhat arbitrary..
- begin
- // check for only "9" are in between
- i := n - 2;
- repeat
- dec( i );
- until ( i = n_max ) or ( buf[i] <> 9 );
- if ( i = n_max ) then
- // force round-up
- dig_round := 9; // any value ">=5"
- end;
- {$endif}
- if ( dig_round < 5 ) then
- exit;
- // Handle "round half to even" case
- if ( dig_round = 5 ) and half_round_to_even and ( ( n_max = 0 ) or ( buf[ n_max - 1 ] and 1 = 0 ) ) then
- begin
- // even and a half: check if exactly the half
- dig_sticky := 0;
- while ( n > n_max + 1 ) and ( dig_sticky = 0 ) do
- begin
- dec( n );
- dig_sticky := buf[n];
- end;
- if ( dig_sticky = 0 ) then
- exit; // exactly a half -> no rounding is required
- end;
- // Round-up
- while ( n_max > 0 ) do
- begin
- dec( n_max );
- inc( buf[n_max] );
- if ( buf[n_max] < 10 ) then
- begin
- // no more overflow: stop now
- n_current := n_max + 1;
- exit;
- end;
- // continue rounding
- end;
- // Overflow out of the 1st digit, all n_max digits became 0
- buf[0] := 1;
- n_current := 1;
- round_digits := 1;
- end;
- (*-------------------------------------------------------
- | do_fillchar [local]
- |
- | Fills string region with certain character.
- |
- *-------------------------------------------------------*)
- {$ifdef cpujvm}
- procedure do_fillchar( var str: shortstring; pos, count: integer; c: char );
- begin
- while count>0 do
- begin
- str[pos]:=c;
- inc(pos);
- dec(count);
- end;
- end;
- {$else not cpujvm}
- procedure do_fillchar( var str: shortstring; pos, count: integer; c: char ); {$ifdef grisu1_inline}inline;{$endif}
- begin
- fillchar( str[pos], count, c );
- end;
- {$endif cpujvm}
- (*-------------------------------------------------------
- | try_return_fixed [local]
- |
- | This routine tries to format the number in the fixed-point representation.
- | If the resulting string is estimated to be too long to fit into shortstring,
- | routine returns FALSE giving the caller a chance to return the exponential
- | representation.
- | Otherwise, it returns TRUE.
- |
- | Not implemented [and why to do it at all?]:
- | Here also a good place to limit the fixed point formatting by exponent
- | range, falling back to exponential notation (just return FALSE).
- |
- *-------------------------------------------------------*)
- function try_return_fixed( out str: shortstring; minus: boolean; const digits: TAsciiDigits; n_digits_have, fixed_dot_pos, min_width, frac_digits: integer ): boolean;
- var
- rounded: boolean;
- temp_round: TAsciiDigits;
- i, j, len, cut_digits_at: integer;
- n_spaces, n_spaces_max, n_before_dot, n_before_dot_pad0, n_after_dot_pad0, n_after_dot, n_tail_pad0: integer;
- begin
- try_return_fixed := false;
- {$ifdef grisu1_debug}
- assert( n_digits_have >= 0 );
- assert( min_width <= C_MAX_WIDTH );
- assert( frac_digits >= 0 );
- assert( frac_digits <= C_MAX_WIDTH - 3 );
- {$endif grisu1_debug}
- // Round digits if necessary
- rounded := false;
- cut_digits_at := fixed_dot_pos + frac_digits;
- if ( cut_digits_at < 0 ) then
- // zero
- n_digits_have := 0
- else
- if ( cut_digits_at < n_digits_have ) then
- begin
- // round digits
- {$ifdef cpujvm}
- temp_round := digits;
- {$else not cpujvm}
- if ( n_digits_have > 0 ) then
- move( digits, temp_round, n_digits_have * sizeof( digits[0] ) );
- {$endif cpujvm}
- inc( fixed_dot_pos, round_digits( temp_round, n_digits_have, cut_digits_at {$ifdef GRISU1_F2A_HALF_ROUNDUP}, false {$endif} ) );
- rounded := true;
- end;
- // Before dot: digits, pad0
- if ( fixed_dot_pos <= 0 ) or ( n_digits_have = 0 ) then
- begin
- n_before_dot := 0;
- n_before_dot_pad0 := 1;
- end
- else
- if ( fixed_dot_pos > n_digits_have ) then
- begin
- n_before_dot := n_digits_have;
- n_before_dot_pad0 := fixed_dot_pos - n_digits_have;
- end
- else
- begin
- n_before_dot := fixed_dot_pos;
- n_before_dot_pad0 := 0;
- end;
- // After dot: pad0, digits, pad0
- if ( fixed_dot_pos < 0 ) then
- n_after_dot_pad0 := - fixed_dot_pos
- else
- n_after_dot_pad0 := 0;
- if ( n_after_dot_pad0 > frac_digits ) then
- n_after_dot_pad0 := frac_digits;
- n_after_dot := n_digits_have - n_before_dot;
- n_tail_pad0 := frac_digits - n_after_dot - n_after_dot_pad0;
- {$ifdef grisu1_debug}
- assert( n_tail_pad0 >= 0 );
- {$endif grisu1_debug}
- // Estimate string length
- len := ord( minus ) + n_before_dot + n_before_dot_pad0;
- if ( frac_digits > 0 ) then
- inc( len, n_after_dot_pad0 + n_after_dot + n_tail_pad0 + 1 );
- n_spaces_max := C_MAX_WIDTH - len;
- if ( n_spaces_max < 0 ) then
- exit;
- // Calculate space-padding length
- n_spaces := min_width - len;
- if ( n_spaces > n_spaces_max ) then
- n_spaces := n_spaces_max;
- if ( n_spaces > 0 ) then
- inc( len, n_spaces );
- // Allocate storage
- SetLength( str, len );
- i := 1;
- // Leading spaces
- if ( n_spaces > 0 ) then
- begin
- do_fillchar( str, i, n_spaces, ' ' );
- inc( i, n_spaces );
- end;
- // Sign
- if minus then
- begin
- str[i] := '-';
- inc( i );
- end;
- // Integer significant digits
- j := 0;
- if rounded then
- while ( n_before_dot > 0 ) do
- begin
- str[i] := char( temp_round[j] + ord('0') );
- inc( i );
- inc( j );
- dec( n_before_dot );
- end
- else
- while ( n_before_dot > 0 ) do
- begin
- str[i] := char( digits[j] + ord('0') );
- inc( i );
- inc( j );
- dec( n_before_dot );
- end;
- // Integer 0-padding
- if ( n_before_dot_pad0 > 0 ) then
- begin
- do_fillchar( str, i, n_before_dot_pad0, '0' );
- inc( i, n_before_dot_pad0 );
- end;
- //
- if ( frac_digits <> 0 ) then
- begin
- // Dot
- str[i] := '.';
- inc( i );
- // Pre-fraction 0-padding
- if ( n_after_dot_pad0 > 0 ) then
- begin
- do_fillchar( str, i, n_after_dot_pad0, '0' );
- inc( i, n_after_dot_pad0 );
- end;
- // Fraction significant digits
- if rounded then
- while ( n_after_dot > 0 ) do
- begin
- str[i] := char( temp_round[j] + ord('0') );
- inc( i );
- inc( j );
- dec( n_after_dot );
- end
- else
- while ( n_after_dot > 0 ) do
- begin
- str[i] := char( digits[j] + ord('0') );
- inc( i );
- inc( j );
- dec( n_after_dot );
- end;
- // Tail 0-padding
- if ( n_tail_pad0 > 0 ) then
- begin
- do_fillchar( str, i, n_tail_pad0, '0' );
- {$ifdef grisu1_debug}
- inc( i, n_tail_pad0 );
- {$endif grisu1_debug}
- end;
- end;
- //
- {$ifdef grisu1_debug}
- assert( i = len + 1 );
- {$endif grisu1_debug}
- try_return_fixed := true
- end;
- (*-------------------------------------------------------
- | return_exponential [local]
- |
- | Formats the number in the exponential representation.
- |
- *-------------------------------------------------------*)
- procedure return_exponential( out str: shortstring; minus: boolean; const digits: TAsciiDigits; n_digits_have, n_digits_req, d_exp, n_digits_exp, min_width: integer );
- var
- e_minus: boolean;
- i, j, len, n_exp, n_spaces, n_spaces_max: integer;
- buf_exp: TAsciiDigits;
- begin
- {$ifdef grisu1_debug}
- assert( n_digits_have >= 0 );
- assert( n_digits_have <= n_digits_req );
- assert( min_width <= C_MAX_WIDTH );
- {$endif grisu1_debug}
- // Prepare exponent
- e_minus := ( d_exp < 0 );
- if e_minus then
- d_exp := - d_exp;
- n_exp := gen_digits_32( buf_exp, 0, d_exp, false );
- if ( n_exp <= n_digits_exp ) then
- len := n_digits_exp
- else
- len := n_exp;
- // Estimate string length
- inc( len, 1{sign} + n_digits_req + 1{E} + 1{E-sign} );
- if ( n_digits_req > 1 ) then
- inc( len ); // dot
- // Calculate space-padding length
- n_spaces_max := C_MAX_WIDTH - len;
- n_spaces := min_width - len;
- if ( n_spaces > n_spaces_max ) then
- n_spaces := n_spaces_max;
- if ( n_spaces > 0 ) then
- inc( len, n_spaces );
- // Allocate storage
- SetLength( str, len );
- i := 1;
- // Leading spaces
- if ( n_spaces > 0 ) then
- begin
- do_fillchar( str, i, n_spaces, ' ' );
- inc( i, n_spaces );
- end;
- // Sign
- if minus then
- str[i] := '-'
- else
- str[i] := ' ';
- inc( i );
- // Integer part
- if ( n_digits_have > 0 ) then
- str[i] := char( digits[0] + ord('0') )
- else
- str[i] := '0';
- inc( i );
- // Dot
- if ( n_digits_req > 1 ) then
- begin
- str[i] := '.';
- inc( i );
- end;
- // Fraction significant digits
- j := 1;
- while ( j < n_digits_have ) and ( j < n_digits_req ) do
- begin
- str[i] := char( digits[j] + ord('0') );
- inc( i );
- inc( j );
- end;
- // Fraction 0-padding
- j := n_digits_req - j;
- if ( j > 0 ) then
- begin
- do_fillchar( str, i, j, '0' );
- inc( i, j );
- end;
- // Exponent designator
- str[i] := 'E';
- inc( i );
- // Exponent sign
- if e_minus then
- str[i] := '-'
- else
- str[i] := '+';
- inc( i );
- // Exponent 0-padding
- j := n_digits_exp - n_exp;
- if ( j > 0 ) then
- begin
- do_fillchar( str, i, j, '0' );
- inc( i, j );
- end;
- // Exponent digits
- for j := 0 to n_exp - 1 do
- begin
- str[i] := char( buf_exp[j] + ord('0') );
- inc( i );
- end;
- {$ifdef grisu1_debug}
- assert( i = len + 1 );
- {$endif grisu1_debug}
- end;
- (*-------------------------------------------------------
- | return_special [local]
- |
- | This routine formats one of special results.
- |
- *-------------------------------------------------------*)
- procedure return_special( out str: shortstring; sign: integer; const spec: shortstring; min_width: integer );
- var
- i, slen, len, n_spaces, n_spaces_max: integer;
- begin
- slen := length(spec);
- if ( sign = 0 ) then
- len := slen
- else
- len := slen + 1;
- n_spaces_max := C_MAX_WIDTH - len;
- // Calculate space-padding length
- n_spaces := min_width - len;
- if ( n_spaces > n_spaces_max ) then
- n_spaces := n_spaces_max;
- if ( n_spaces > 0 ) then
- inc( len, n_spaces );
- // Allocate storage
- SetLength( str, len );
- i := 1;
- // Leading spaces
- if ( n_spaces > 0 ) then
- begin
- do_fillchar( str, i, n_spaces, ' ' );
- inc( i, n_spaces );
- end;
- // Sign
- if ( sign <> 0 ) then
- begin
- if ( sign > 0 ) then
- str[i] := '+'
- else
- str[i] := '-';
- inc( i );
- end;
- // Special
- while slen>0 do
- begin
- str[i+slen-1] := spec[slen];
- dec(slen);
- end;
- end;
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- (*-------------------------------------------------------
- | u128_div_u64_to_u64 [local]
- |
- | Divides unsigned 128-bit integer by unsigned 64-bit integer.
- | Returns 64-bit quotient and reminder.
- |
- | This routine is used here only for splitting specially prepared unsigned
- | 128-bit integer into two 64-bit ones before converting it to ASCII.
- |
- *-------------------------------------------------------*)
- function u128_div_u64_to_u64( const xh, xl: qword; const y: qword; out quotient, reminder: qword ): boolean;
- var
- b, // Number base
- v, // Norm. divisor
- un1, un0, // Norm. dividend LSD's
- vn1, vn0, // Norm. divisor digits
- q1, q0, // Quotient digits
- un64, un21, un10, // Dividend digit pairs
- rhat: qword; // A remainder
- s: integer; // Shift amount for norm
- begin
- // Overflow check
- if ( xh >= y ) then
- begin
- u128_div_u64_to_u64 := false;
- exit;
- end;
- // Count leading zeros
- s := 63 - BSRqword( y ); // 0 <= s <= 63
- // Normalize divisor
- v := y shl s;
- // Break divisor up into two 32-bit digits
- vn1 := hi(v);
- vn0 := lo(v);
- // Shift dividend left
- un64 := xh shl s;
- if ( s > 0 ) then
- un64 := un64 or ( xl shr ( 64 - s ) );
- un10 := xl shl s;
- // Break right half of dividend into two digits
- un1 := hi(un10);
- un0 := lo(un10);
- // Compute the first quotient digit, q1
- q1 := un64 div vn1;
- rhat := un64 - q1 * vn1;
- b := qword(1) shl 32; // Number base
- while ( q1 >= b ) or ( q1 * vn0 > b * rhat + un1 ) do
- begin
- dec( q1 );
- inc( rhat, vn1 );
- if rhat >= b then
- break;
- end;
- // Multiply and subtract
- un21 := un64 * b + un1 - q1 * v;
- // Compute the second quotient digit, q0
- q0 := un21 div vn1;
- rhat := un21 - q0 * vn1;
- while ( q0 >= b ) or ( q0 * vn0 > b * rhat + un0 ) do
- begin
- dec( q0 );
- inc( rhat, vn1 );
- if ( rhat >= b ) then
- break;
- end;
- // Result
- reminder := ( un21 * b + un0 - q0 * v ) shr s;
- quotient := q1 * b + q0;
- u128_div_u64_to_u64 := true;
- end;
- {$endif VALREAL_80 | VALREAL_128}
- (*-------------------------------------------------------
- | count_leading_zero [local]
- |
- | Counts number of 0-bits at most significant bit position.
- |
- *-------------------------------------------------------*)
- {$ifdef VALREAL_32}
- function count_leading_zero( const X: dword ): integer; {$ifdef grisu1_inline}inline;{$endif}
- begin
- count_leading_zero := 31 - BSRdword( X );
- end;
- {$else not VALREAL_32}
- function count_leading_zero( const X: qword ): integer; {$ifdef grisu1_inline}inline;{$endif}
- begin
- count_leading_zero := 63 - BSRqword( X );
- end;
- {$endif VALREAL_*}
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- (*-------------------------------------------------------
- | make_frac_mask [local]
- |
- | Makes DIY_FP fractional part mask:
- | result := ( 1 shl one.e ) - 1
- |
- *-------------------------------------------------------*)
- {$ifdef VALREAL_80}
- procedure make_frac_mask( out h: dword; out l: qword; one_e: integer ); {$ifdef grisu1_inline}inline;{$endif}
- {$else VALREAL_128}
- procedure make_frac_mask( out h, l: qword; one_e: integer ); {$ifdef grisu1_inline}inline;{$endif}
- {$endif VALREAL_*}
- begin
- {$ifdef grisu1_debug}
- assert( one_e <= 0 );
- assert( one_e > - ( sizeof( l ) + sizeof( h ) ) * 8 );
- {$endif grisu1_debug}
- if ( one_e <= - 64 ) then
- begin
- l := qword( -1 );
- h := ( {$ifdef VALREAL_128} qword {$else} dword {$endif} ( 1 ) shl ( - one_e - 64 ) ) - 1;
- end
- else
- begin
- l := ( qword( 1 ) shl ( - one_e ) ) - 1;
- h := 0;
- end;
- end;
- {$endif VALREAL_80 | VALREAL_128}
- (*-------------------------------------------------------
- | k_comp [local]
- |
- | Calculates the exp10 of a factor required to bring the binary exponent
- | of the original number into selected [ alpha .. gamma ] range:
- | result := ceiling[ ( alpha - e ) * log10(2) ]
- |
- *-------------------------------------------------------*)
- function k_comp( e, alpha{, gamma}: integer ): integer;
- {$ifdef fpc_softfpu_implementation}
- ///////////////
- //
- // Assuming no HardFloat available.
- // Note: using softfpu here significantly slows down overall
- // conversion performance, so we use integers.
- //
- const
- D_LOG10_2: TDIY_FP = // log10(2) = 0.301029995663981195213738894724493027
- {$ifdef VALREAL_32}
- ( f: dword($9A209A85); e: -33 );
- {$endif}
- {$ifdef VALREAL_64}
- ( f: qword($9A209A84FBCFF799); e: -65 );
- {$endif}
- {$ifdef VALREAL_80}
- ( f: qword($FBCFF7988F8959AC); fh: dword($9A209A84); e: -97 );
- {$endif}
- {$ifdef VALREAL_128}
- ( fh: qword($9A209A84FBCFF798); f: qword($8F8959AC0B7C9178); e: -129 );
- {$endif}
- var
- x, n: integer;
- y, z: TDIY_FP;
- {$ifdef VALREAL_32}
- mask_one: dword;
- {$else not VALREAL_32}
- mask_one: qword;
- {$endif}
- {$ifdef VALREAL_80}
- mask_oneh: dword;
- {$endif}
- {$ifdef VALREAL_128}
- mask_oneh: qword;
- {$endif}
- plus, round_up: boolean;
- begin
- x := alpha - e;
- if ( x = 0 ) then
- begin
- k_comp := 0;
- exit;
- end;
- plus := ( x > 0 );
- if plus then
- y.f := x
- else
- y.f := - x;
- round_up := plus;
- n := C_DIY_FP_Q - 1 - BSRdword( y.f );
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- y.f := y.f shl n;
- {$else VALREAL_80 | VALREAL_128}
- y.fh := 0;
- diy_util_shl( y.fh, y.f, n );
- {$endif VALREAL_*}
- y.e := - n;
- z := diy_fp_multiply( y, D_LOG10_2, false );
- if ( z.e <= - C_DIY_FP_Q ) then
- begin
- round_up := plus and ( 0 <>
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- z.f
- {$else VALREAL_80 | VALREAL_128}
- z.f or z.fh
- {$endif}
- );
- n := 0;
- end
- else
- begin
- if plus then
- begin
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- mask_one := ( {$ifdef VALREAL_64} qword {$else} dword {$endif} ( 1 ) shl ( - z.e ) ) - 1;
- round_up := ( z.f and mask_one <> 0 );
- {$else VALREAL_80 | VALREAL_128}
- make_frac_mask( mask_oneh, mask_one, z.e );
- round_up := ( z.f and mask_one <> 0 ) or ( z.fh and mask_oneh <> 0 );
- {$endif VALREAL_*}
- end;
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- n := z.f shr ( - z.e );
- {$else VALREAL_80 | VALREAL_128}
- diy_util_shr( z.fh, z.f, - z.e );
- n := z.f;
- {$endif VALREAL_*}
- end;
- if not plus then
- n := - n;
- if round_up then
- k_comp := n + 1
- else
- k_comp := n;
- end;
- {$else not fpc_softfpu_implementation}
- ///////////////
- //
- // HardFloat implementation
- //
- {$if defined(SUPPORT_SINGLE) and defined(VALREAL_32)}
- // If available, use single math for VALREAL_32
- var
- dexp: single;
- const
- D_LOG10_2: single =
- {$elseif defined(SUPPORT_DOUBLE) and not defined(VALREAL_32)}
- // If available, use double math for all types >VALREAL_32
- var
- dexp: double;
- const
- D_LOG10_2: double =
- {$else}
- // Use native math
- var
- dexp: ValReal;
- const
- D_LOG10_2: ValReal =
- {$endif}
- 0.301029995663981195213738894724493027; // log10(2)
- var
- x, n: integer;
- begin
- x := alpha - e;
- dexp := x * D_LOG10_2;
- // ceil( dexp )
- n := trunc( dexp );
- if ( x > 0 ) then
- if ( dexp <> n ) then
- inc( n ); // round-up
- k_comp := n;
- end;
- {$endif fpc_softfpu_implementation}
- (****************************************************************************)
- var
- w, D: TDIY_FP;
- c_mk: TDIY_FP_Power_of_10;
- n, mk, dot_pos, n_digits_exp, n_digits_need, n_digits_have: integer;
- n_digits_req, n_digits_sci: integer;
- minus: boolean;
- {$ifndef VALREAL_32}
- fl, one_maskl: qword;
- {$endif not VALREAL_32}
- {$ifdef VALREAL_80}
- templ: qword;
- fh, one_maskh, temph: dword;
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- templ: qword;
- fh, one_maskh, temph: qword;
- {$endif VALREAL_128}
- one_e: integer;
- one_mask, f: dword;
- buf: TAsciiDigits;
- begin
- // Limit parameters
- if ( frac_digits > 216 ) then
- frac_digits := 216; // Delphi compatible
- if ( min_width <= C_NO_MIN_WIDTH ) then
- min_width := -1 // no minimal width
- else
- if ( min_width < 0 ) then
- min_width := 0 // minimal width is as short as possible
- else
- if ( min_width > C_MAX_WIDTH ) then
- min_width := C_MAX_WIDTH;
- // Format profile: select "n_digits_need" and "n_digits_exp"
- n_digits_req := float_format[real_type].nDig_mantissa;
- n_digits_exp := float_format[real_type].nDig_exp10;
- // number of digits to be calculated by Grisu
- n_digits_need := float_format[RT_NATIVE].nDig_mantissa;
- if ( n_digits_req < n_digits_need ) then
- n_digits_need := n_digits_req;
- // number of mantissa digits to be printed in exponential notation
- if ( min_width < 0 ) then
- n_digits_sci := n_digits_req
- else
- begin
- n_digits_sci := min_width - 1{sign} - 1{dot} - 1{E} - 1{E-sign} - n_digits_exp;
- if ( n_digits_sci < 2 ) then
- n_digits_sci := 2; // at least 2 digits
- if ( n_digits_sci > n_digits_req ) then
- n_digits_sci := n_digits_req; // at most requested by real_type
- end;
- // Float -> DIY_FP
- w := unpack_float( v, minus );
- // Handle Zero
- if ( w.e = 0 ) and ( w.f {$ifdef VALREAL_128} or w.fh {$endif} = 0 ) then
- begin
- buf[0] := 0; // to avoid "warning: uninitialized"
- if ( frac_digits >= 0 ) then
- if try_return_fixed( str, minus, buf, 0, 1, min_width, frac_digits ) then
- exit
- {$ifdef grisu1_debug}
- else
- assert( FALSE ) // should never fail with these arguments
- {$endif grisu1_debug};
- return_exponential( str, minus, buf, 0, n_digits_sci, 0, n_digits_exp, min_width );
- exit;
- end;
- {$ifdef VALREAL_80}
- // Handle non-normals
- if ( w.e <> 0 ) and ( w.e <> C_EXP2_SPECIAL ) then
- if ( w.f and C_MANT2_INTEGER = 0 ) then
- begin
- // -> QNaN
- w.f := qword(-1);
- w.e := C_EXP2_SPECIAL;
- end;
- {$endif VALREAL_80}
- // Handle specials
- if ( w.e = C_EXP2_SPECIAL ) then
- begin
- if ( min_width < 0 ) then
- // backward compat..
- min_width := float_format[real_type].nDig_mantissa + float_format[real_type].nDig_exp10 + 4;
- n := 1 - ord(minus) * 2; // default special sign [-1|+1]
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- if ( w.f = 0 ) then
- {$endif VALREAL_32 | VALREAL_64}
- {$ifdef VALREAL_80}
- if ( w.f = qword(C_MANT2_INTEGER) ) then
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- if ( w.fh or w.f = 0 ) then
- {$endif VALREAL_128}
- begin
- // Inf
- return_special( str, n, C_STR_INF, min_width );
- end
- else
- begin
- // NaN [also pseudo-NaN, pseudo-Inf, non-normal for floatx80]
- {$ifdef GRISU1_F2A_NAN_SIGNLESS}
- n := 0;
- {$endif}
- {$ifndef GRISU1_F2A_NO_SNAN}
- {$ifdef VALREAL_128}
- if ( w.fh and ( C_MANT2_INTEGER_H shr 1 ) = 0 ) then
- {$else}
- if ( w.f and ( C_MANT2_INTEGER shr 1 ) = 0 ) then
- {$endif}
- return_special( str, n, C_STR_SNAN, min_width )
- else
- {$endif GRISU1_F2A_NO_SNAN}
- return_special( str, n, C_STR_QNAN, min_width );
- end;
- exit;
- end;
- // Handle denormals
- if ( w.e <> 0 ) then
- begin
- // normal
- {$ifdef VALREAL_128}
- w.fh := w.fh or C_MANT2_INTEGER_H;
- {$else not VALREAL_128}
- {$ifndef VALREAL_80}
- w.f := w.f or C_MANT2_INTEGER;
- {$endif not VALREAL_80}
- {$endif VALREAL_*}
- n := C_DIY_FP_Q - C_FRAC2_BITS - 1;
- end
- else
- begin
- // denormal
- {$ifdef VALREAL_128}
- if ( w.fh = 0 ) then
- n := count_leading_zero( w.f ) + 64
- else
- n := count_leading_zero( w.fh );
- {$else not VALREAL_128}
- {$ifdef VALREAL_80}
- // also handle pseudo-denormals
- n := count_leading_zero( w.f ) + 32;
- {$else VALREAL_32 | VALREAL_64}
- n := count_leading_zero( w.f );
- {$endif VALREAL_*}
- {$endif VALREAL_*}
- inc( w.e );
- end;
- // Final normalization
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- w.f := w.f shl n;
- {$else VALREAL_80 | VALREAL_128}
- diy_util_shl( w.fh, w.f, n );
- {$endif VALREAL_*}
- dec( w.e, C_EXP2_BIAS + n + C_FRAC2_BITS );
- //
- // 1. Find the normalized "c_mk = f_c * 2^e_c" such that "alpha <= e_c + e_w + q <= gamma"
- // 2. Define "V = D * 10^k": multiply the input number by "c_mk", do not normalize to land into [ alpha .. gamma ]
- // 3. Generate digits ( n_digits_need + "round" )
- //
- if ( C_GRISU_ALPHA <= w.e ) and ( w.e <= C_GRISU_GAMMA ) then
- begin
- // no scaling required
- D := w;
- c_mk.e10 := 0;
- end
- else
- begin
- mk := k_comp( w.e, C_GRISU_ALPHA{, C_GRISU_GAMMA} );
- diy_fp_cached_power10( mk, c_mk );
- // Let "D = f_D * 2^e_D := w (*) c_mk"
- if c_mk.e10 = 0 then
- D := w
- else
- D := diy_fp_multiply( w, c_mk.c, FALSE );
- end;
- {$ifdef grisu1_debug}
- assert( ( C_GRISU_ALPHA <= D.e ) and ( D.e <= C_GRISU_GAMMA ) );
- {$endif grisu1_debug}
- // Generate digits: integer part
- {$ifdef grisu1_debug}
- {$ifdef VALREAL_80}
- assert( D.e <= 32 );
- {$else not VALREAL_80}
- assert( D.e <= 0 );
- {$endif VALREAL_*}
- {$endif grisu1_debug}
- {$ifdef VALREAL_32}
- n_digits_have := gen_digits_32( buf, 0, D.f shr ( - D.e ) );
- {$endif VALREAL_32}
-
- {$ifdef VALREAL_64}
- n_digits_have := gen_digits_64( buf, 0, D.f shr ( - D.e ) );
- {$endif VALREAL_64}
-
- {$ifdef VALREAL_80}
- fl := D.f;
- fh := D.fh;
- if ( D.e > 0 ) then
- begin
- templ := ( qword(fh) shl D.e ) and qword($FFFFFFFF00000000);
- diy_util_shl( fh, fl, D.e );
- inc( templ, fh );
- end
- else
- begin
- diy_util_shr( fh, fl, - D.e );
- templ := fh;
- end;
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- fl := D.f;
- templ := D.fh;
- diy_util_shr( templ, fl, - D.e );
- {$endif VALREAL_128}
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- if ( templ = 0 ) then
- n_digits_have := gen_digits_64( buf, 0, fl )
- else
- begin
- if not u128_div_u64_to_u64( templ, fl, qword(10000000000000000000), templ, fl ) then
- {$ifdef grisu1_debug}
- assert( FALSE ) // never overflows by design
- {$endif grisu1_debug};
- n_digits_have := gen_digits_64( buf, 0, templ );
- inc( n_digits_have, gen_digits_64( buf, n_digits_have, fl, n_digits_have > 0 ) );
- end;
- {$endif VALREAL_80 | VALREAL_128}
- dot_pos := n_digits_have;
- // Generate digits: fractional part
- f := 0; // "sticky" digit
- if ( D.e < 0 ) then
- repeat
- // MOD by ONE
- one_e := D.e;
- {$ifdef VALREAL_32}
- one_mask := dword( 1 ) shl ( - D.e ) - 1;
- f := D.f and one_mask;
- {$endif VALREAL_32}
- {$ifdef VALREAL_64}
- one_maskl := qword( 1 ) shl ( - D.e ) - 1;
- fl := D.f and one_maskl;
- {$endif VALREAL_64}
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- make_frac_mask( one_maskh, one_maskl, D.e );
- fl := D.f and one_maskl;
- fh := D.fh and one_maskh;
- // 128/96-bit loop
- while ( one_e < -61 ) and ( n_digits_have < n_digits_need + 1 ) and ( fl or fh <> 0 ) do
- begin
- // f := f * 5;
- templ := fl;
- temph := fh;
- diy_util_shl( fh, fl, 2 );
- diy_util_add( fh, fl, temph, templ );
- // one := one / 2
- diy_util_shr( one_maskh, one_maskl, 1 );
- inc( one_e );
- // DIV by one
- templ := fl;
- temph := fh;
- diy_util_shr( temph, templ, - one_e );
- buf[ n_digits_have ] := lo(templ);
- // MOD by one
- fl := fl and one_maskl;
- fh := fh and one_maskh;
- // next
- inc( n_digits_have );
- end;
- if ( n_digits_have >= n_digits_need + 1 ) then
- begin
- // only "sticky" digit remains
- f := ord( fl or fh <> 0 );
- break;
- end;
- {$endif VALREAL_80 | VALREAL_128}
- {$ifndef VALREAL_32}
- // 64-bit loop
- while ( one_e < -29 ) and ( n_digits_have < n_digits_need + 1 ) and ( fl <> 0 ) do
- begin
- // f := f * 5;
- inc( fl, fl shl 2 );
- // one := one / 2
- one_maskl := one_maskl shr 1;
- inc( one_e );
- // DIV by one
- buf[ n_digits_have ] := fl shr ( - one_e );
- // MOD by one
- fl := fl and one_maskl;
- // next
- inc( n_digits_have );
- end;
- if ( n_digits_have >= n_digits_need + 1 ) then
- begin
- // only "sticky" digit remains
- f := ord( fl <> 0 );
- break;
- end;
- one_mask := lo(one_maskl);
- f := lo(fl);
- {$endif not VALREAL_32}
- // 32-bit loop
- while ( n_digits_have < n_digits_need + 1 ) and ( f <> 0 ) do
- begin
- // f := f * 5;
- inc( f, f shl 2 );
- // one := one / 2
- one_mask := one_mask shr 1;
- inc( one_e );
- // DIV by one
- buf[ n_digits_have ] := f shr ( - one_e );
- // MOD by one
- f := f and one_mask;
- // next
- inc( n_digits_have );
- end;
- until true;
- // Append "sticky" digit if any
- if ( f <> 0 ) and ( n_digits_have >= n_digits_need + 1 ) then
- begin
- // single "<>0" digit is enough
- n_digits_have := n_digits_need + 2;
- buf[ n_digits_need + 1 ] := 1;
- end;
- // Round to n_digits_need using "roundTiesToEven"
- if ( n_digits_have > n_digits_need ) then
- inc( dot_pos, round_digits( buf, n_digits_have, n_digits_need ) );
- // Generate output
- if ( frac_digits >= 0 ) then
- if try_return_fixed( str, minus, buf, n_digits_have, dot_pos - c_mk.e10, min_width, frac_digits ) then
- exit;
- if ( n_digits_have > n_digits_sci ) then
- inc( dot_pos, round_digits( buf, n_digits_have, n_digits_sci {$ifdef GRISU1_F2A_HALF_ROUNDUP}, false {$endif} ) );
- return_exponential( str, minus, buf, n_digits_have, n_digits_sci, dot_pos - c_mk.e10 - 1, n_digits_exp, min_width );
- end;
- (****************************************************************************)
- {$ifndef fpc_softfpu_implementation}
- procedure str_real_iso( len, f: longint; d: ValReal; real_type: treal_type; out s: string );
- var
- i: integer;
- begin
- str_real( len, f, d, real_type, s );
- for i := length( s ) downto 1 do
- if ( s[i] ='E' ) then
- begin
- s[i] := 'e';
- break; // only one "E" expected
- end;
- end;
- {$endif not fpc_softfpu_implementation}
- (*==========================================================================*
- * *
- * ASCII -> Float *
- * *
- *==========================================================================*)
- function val_real( const src: shortstring; out err_pos: ValSInt ): ValReal;
- {$define VALREAL_PACK}
- {$i flt_pack.inc}
- { NOTE: C_MAX_DIGITS_ACCEPTED should fit into unsigned integer which forms DIY_FP }
- const
- {$ifdef VALREAL_32}
- C_MAX_DIGITS_ACCEPTED = 9;
- C_EXP10_OVER = 100;
- C_DIY_FP_Q = 32;
- C_FRAC2_BITS = 23;
- C_EXP2_BIAS = 127;
- {$endif VALREAL_32}
- {$ifdef VALREAL_64}
- C_MAX_DIGITS_ACCEPTED = 19;
- C_EXP10_OVER = 1000;
- C_DIY_FP_Q = 64;
- C_FRAC2_BITS = 52;
- C_EXP2_BIAS = 1023;
- {$endif VALREAL_64}
- {$ifdef VALREAL_80}
- C_MAX_DIGITS_ACCEPTED = 28;
- C_EXP10_OVER = 10000;
- C_DIY_FP_Q = 96;
- C_FRAC2_BITS = 63;
- C_EXP2_BIAS = 16383;
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- C_MAX_DIGITS_ACCEPTED = 38;
- C_EXP10_OVER = 10000;
- C_DIY_FP_Q = 128;
- C_FRAC2_BITS = 112;
- C_EXP2_BIAS = 16383;
- {$endif VALREAL_128}
- (****************************************************************************)
- // handy const
- C_EXP2_SPECIAL = C_EXP2_BIAS * 2 + 1;
- C_DIY_SHR_TO_FLT = C_DIY_FP_Q - 1 - C_FRAC2_BITS;
- C_DIY_EXP_TO_FLT = C_DIY_FP_Q - 1 + C_EXP2_BIAS;
- C_DIY_ROUND_BIT = 1 shl ( C_DIY_SHR_TO_FLT - 1 );
- C_DIY_ROUND_MASK = ( C_DIY_ROUND_BIT shl 2 ) - 1;
- {$ifdef VALREAL_128}
- C_FLT_INT_BITh = qword(1) shl ( C_FRAC2_BITS - 64 );
- C_FLT_FRAC_MASKh = C_FLT_INT_BITh - 1;
- {$else not VALREAL_128}
- {$ifdef VALREAL_32}
- C_FLT_INT_BIT = dword(1) shl C_FRAC2_BITS;
- C_FLT_FRAC_MASK = C_FLT_INT_BIT - 1;
- {$else VALREAL_64 | VALREAL_80}
- C_FLT_INT_BIT = qword(1) shl C_FRAC2_BITS;
- C_FLT_FRAC_MASK = qword(C_FLT_INT_BIT) - 1;
- {$endif VALREAL_*}
- {$endif VALREAL_*}
- // specials
- {$ifdef VALREAL_32}
- C_FLT_MANT_INF = dword($00000000);
- {$ifndef GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_SNAN = dword($00200000);
- {$endif GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_QNAN = dword($00400000);
- {$endif VALREAL_32}
- {$ifdef VALREAL_64}
- C_FLT_MANT_INF = qword($0000000000000000);
- {$ifndef GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_SNAN = qword($0004000000000000);
- {$endif GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_QNAN = qword($0008000000000000);
- {$endif VALREAL_64}
- {$ifdef VALREAL_80}
- C_FLT_MANT_INF = qword($8000000000000000);
- {$ifndef GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_SNAN = qword($A000000000000000);
- {$endif GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_QNAN = qword($C000000000000000);
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- C_FLT_MANT_INFh = qword($0000000000000000);
- C_FLT_MANT_INF = qword($0000000000000000);
- {$ifndef GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_SNANh = qword($0000400000000000);
- C_FLT_MANT_SNAN = qword($0000000000000000);
- {$endif GRISU1_A2F_NO_SNAN}
- C_FLT_MANT_QNANh = qword($0000800000000000);
- C_FLT_MANT_QNAN = qword($0000000000000000);
- {$endif VALREAL_128}
- (*-------------------------------------------------------
- | factor_10_inexact [local]
- |
- | Calculates an arbitrary normalized power of 10 required for final scaling.
- | The result of this calculation may be off by several ulp's from exact.
- |
- | Returns an overflow/underflow status:
- | "<0": underflow
- | "=0": ok
- | ">0": overflow
- |
- *-------------------------------------------------------*)
- function factor_10_inexact( const exp10: integer; out C: TDIY_FP_Power_of_10 ): integer;
- const
- {$ifdef VALREAL_32}
- factor: array [ 0 .. 7 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: $80000000; e: -31); e10: 0 ),
- ( c: ( f: $CCCCCCCD; e: -35); e10: -1 ),
- ( c: ( f: $A3D70A3D; e: -38); e10: -2 ),
- ( c: ( f: $83126E98; e: -41); e10: -3 ),
- ( c: ( f: $D1B71759; e: -45); e10: -4 ),
- ( c: ( f: $A7C5AC47; e: -48); e10: -5 ),
- ( c: ( f: $8637BD06; e: -51); e10: -6 ),
- ( c: ( f: $D6BF94D6; e: -55); e10: -7 )
- );
- {$endif VALREAL_32}
- {$ifdef VALREAL_64}
- factor: array [ 0 .. 17 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($8000000000000000); e: -63); e10: 0 ),
- ( c: ( f: qword($CCCCCCCCCCCCCCCD); e: -67); e10: -1 ),
- ( c: ( f: qword($A3D70A3D70A3D70A); e: -70); e10: -2 ),
- ( c: ( f: qword($83126E978D4FDF3B); e: -73); e10: -3 ),
- ( c: ( f: qword($D1B71758E219652C); e: -77); e10: -4 ),
- ( c: ( f: qword($A7C5AC471B478423); e: -80); e10: -5 ),
- ( c: ( f: qword($8637BD05AF6C69B6); e: -83); e10: -6 ),
- ( c: ( f: qword($D6BF94D5E57A42BC); e: -87); e10: -7 ),
- ( c: ( f: qword($ABCC77118461CEFD); e: -90); e10: -8 ),
- ( c: ( f: qword($89705F4136B4A597); e: -93); e10: -9 ),
- ( c: ( f: qword($DBE6FECEBDEDD5BF); e: -97); e10: -10 ),
- ( c: ( f: qword($AFEBFF0BCB24AAFF); e: -100); e10: -11 ),
- ( c: ( f: qword($8CBCCC096F5088CC); e: -103); e10: -12 ),
- ( c: ( f: qword($E12E13424BB40E13); e: -107); e10: -13 ),
- ( c: ( f: qword($B424DC35095CD80F); e: -110); e10: -14 ),
- ( c: ( f: qword($901D7CF73AB0ACD9); e: -113); e10: -15 ),
- ( c: ( f: qword($E69594BEC44DE15B); e: -117); e10: -16 ),
- ( c: ( f: qword($B877AA3236A4B449); e: -120); e10: -17 )
- );
- {$endif VALREAL_64}
- {$ifdef VALREAL_80}
- factor: array [ 0 .. 36 ] of TDIY_FP_Power_of_10 = (
- ( c: ( f: qword($0000000000000000); fh: dword($80000000); e: -95 ); e10: 0 ),
- ( c: ( f: qword($CCCCCCCCCCCCCCCD); fh: dword($CCCCCCCC); e: -99 ); e10: -1 ),
- ( c: ( f: qword($70A3D70A3D70A3D7); fh: dword($A3D70A3D); e: -102 ); e10: -2 ),
- ( c: ( f: qword($8D4FDF3B645A1CAC); fh: dword($83126E97); e: -105 ); e10: -3 ),
- ( c: ( f: qword($E219652BD3C36113); fh: dword($D1B71758); e: -109 ); e10: -4 ),
- ( c: ( f: qword($1B4784230FCF80DC); fh: dword($A7C5AC47); e: -112 ); e10: -5 ),
- ( c: ( f: qword($AF6C69B5A63F9A4A); fh: dword($8637BD05); e: -115 ); e10: -6 ),
- ( c: ( f: qword($E57A42BC3D329076); fh: dword($D6BF94D5); e: -119 ); e10: -7 ),
- ( c: ( f: qword($8461CEFCFDC20D2B); fh: dword($ABCC7711); e: -122 ); e10: -8 ),
- ( c: ( f: qword($36B4A59731680A89); fh: dword($89705F41); e: -125 ); e10: -9 ),
- ( c: ( f: qword($BDEDD5BEB573440E); fh: dword($DBE6FECE); e: -129 ); e10: -10 ),
- ( c: ( f: qword($CB24AAFEF78F69A5); fh: dword($AFEBFF0B); e: -132 ); e10: -11 ),
- ( c: ( f: qword($6F5088CBF93F87B7); fh: dword($8CBCCC09); e: -135 ); e10: -12 ),
- ( c: ( f: qword($4BB40E132865A5F2); fh: dword($E12E1342); e: -139 ); e10: -13 ),
- ( c: ( f: qword($095CD80F538484C2); fh: dword($B424DC35); e: -142 ); e10: -14 ),
- ( c: ( f: qword($3AB0ACD90F9D3701); fh: dword($901D7CF7); e: -145 ); e10: -15 ),
- ( c: ( f: qword($C44DE15B4C2EBE68); fh: dword($E69594BE); e: -149 ); e10: -16 ),
- ( c: ( f: qword($36A4B44909BEFEBA); fh: dword($B877AA32); e: -152 ); e10: -17 ),
- ( c: ( f: qword($921D5D073AFF322E); fh: dword($9392EE8E); e: -155 ); e10: -18 ),
- ( c: ( f: qword($B69561A52B31E9E4); fh: dword($EC1E4A7D); e: -159 ); e10: -19 ),
- ( c: ( f: qword($92111AEA88F4BB1D); fh: dword($BCE50864); e: -162 ); e10: -20 ),
- ( c: ( f: qword($74DA7BEED3F6FC17); fh: dword($971DA050); e: -165 ); e10: -21 ),
- ( c: ( f: qword($BAF72CB15324C68B); fh: dword($F1C90080); e: -169 ); e10: -22 ),
- ( c: ( f: qword($95928A2775B7053C); fh: dword($C16D9A00); e: -172 ); e10: -23 ),
- ( c: ( f: qword($44753B52C4926A96); fh: dword($9ABE14CD); e: -175 ); e10: -24 ),
- ( c: ( f: qword($D3EEC5513A83DDBE); fh: dword($F79687AE); e: -179 ); e10: -25 ),
- ( c: ( f: qword($76589DDA95364AFE); fh: dword($C6120625); e: -182 ); e10: -26 ),
- ( c: ( f: qword($91E07E48775EA265); fh: dword($9E74D1B7); e: -185 ); e10: -27 ),
- ( c: ( f: qword($8300CA0D8BCA9D6E); fh: dword($FD87B5F2); e: -189 ); e10: -28 ),
- ( c: ( f: qword($359A3B3E096EE458); fh: dword($CAD2F7F5); e: -192 ); e10: -29 ),
- ( c: ( f: qword($5E14FC31A125837A); fh: dword($A2425FF7); e: -195 ); e10: -30 ),
- ( c: ( f: qword($4B43FCF480EACF95); fh: dword($81CEB32C); e: -198 ); e10: -31 ),
- ( c: ( f: qword($453994BA67DE18EE); fh: dword($CFB11EAD); e: -202 ); e10: -32 ),
- ( c: ( f: qword($D0FADD61ECB1AD8B); fh: dword($A6274BBD); e: -205 ); e10: -33 ),
- ( c: ( f: qword($DA624AB4BD5AF13C); fh: dword($84EC3C97); e: -208 ); e10: -34 ),
- ( c: ( f: qword($C3D07787955E4EC6); fh: dword($D4AD2DBF); e: -212 ); e10: -35 ),
- ( c: ( f: qword($697392D2DDE50BD2); fh: dword($AA242499); e: -215 ); e10: -36 )
- );
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- factor: array [ 0 .. 36 ] of TDIY_FP_Power_of_10 = (
- ( c: ( fh: qword($8000000000000000); f: qword($0000000000000000); e: -127 ); e10: 0 ),
- ( c: ( fh: qword($CCCCCCCCCCCCCCCC); f: qword($CCCCCCCCCCCCCCCD); e: -131 ); e10: -1 ),
- ( c: ( fh: qword($A3D70A3D70A3D70A); f: qword($3D70A3D70A3D70A4); e: -134 ); e10: -2 ),
- ( c: ( fh: qword($83126E978D4FDF3B); f: qword($645A1CAC083126E9); e: -137 ); e10: -3 ),
- ( c: ( fh: qword($D1B71758E219652B); f: qword($D3C36113404EA4A9); e: -141 ); e10: -4 ),
- ( c: ( fh: qword($A7C5AC471B478423); f: qword($0FCF80DC33721D54); e: -144 ); e10: -5 ),
- ( c: ( fh: qword($8637BD05AF6C69B5); f: qword($A63F9A49C2C1B110); e: -147 ); e10: -6 ),
- ( c: ( fh: qword($D6BF94D5E57A42BC); f: qword($3D32907604691B4D); e: -151 ); e10: -7 ),
- ( c: ( fh: qword($ABCC77118461CEFC); f: qword($FDC20D2B36BA7C3D); e: -154 ); e10: -8 ),
- ( c: ( fh: qword($89705F4136B4A597); f: qword($31680A88F8953031); e: -157 ); e10: -9 ),
- ( c: ( fh: qword($DBE6FECEBDEDD5BE); f: qword($B573440E5A884D1B); e: -161 ); e10: -10 ),
- ( c: ( fh: qword($AFEBFF0BCB24AAFE); f: qword($F78F69A51539D749); e: -164 ); e10: -11 ),
- ( c: ( fh: qword($8CBCCC096F5088CB); f: qword($F93F87B7442E45D4); e: -167 ); e10: -12 ),
- ( c: ( fh: qword($E12E13424BB40E13); f: qword($2865A5F206B06FBA); e: -171 ); e10: -13 ),
- ( c: ( fh: qword($B424DC35095CD80F); f: qword($538484C19EF38C94); e: -174 ); e10: -14 ),
- ( c: ( fh: qword($901D7CF73AB0ACD9); f: qword($0F9D37014BF60A10); e: -177 ); e10: -15 ),
- ( c: ( fh: qword($E69594BEC44DE15B); f: qword($4C2EBE687989A9B4); e: -181 ); e10: -16 ),
- ( c: ( fh: qword($B877AA3236A4B449); f: qword($09BEFEB9FAD487C3); e: -184 ); e10: -17 ),
- ( c: ( fh: qword($9392EE8E921D5D07); f: qword($3AFF322E62439FCF); e: -187 ); e10: -18 ),
- ( c: ( fh: qword($EC1E4A7DB69561A5); f: qword($2B31E9E3D06C32E5); e: -191 ); e10: -19 ),
- ( c: ( fh: qword($BCE5086492111AEA); f: qword($88F4BB1CA6BCF584); e: -194 ); e10: -20 ),
- ( c: ( fh: qword($971DA05074DA7BEE); f: qword($D3F6FC16EBCA5E03); e: -197 ); e10: -21 ),
- ( c: ( fh: qword($F1C90080BAF72CB1); f: qword($5324C68B12DD6338); e: -201 ); e10: -22 ),
- ( c: ( fh: qword($C16D9A0095928A27); f: qword($75B7053C0F178294); e: -204 ); e10: -23 ),
- ( c: ( fh: qword($9ABE14CD44753B52); f: qword($C4926A9672793543); e: -207 ); e10: -24 ),
- ( c: ( fh: qword($F79687AED3EEC551); f: qword($3A83DDBD83F52205); e: -211 ); e10: -25 ),
- ( c: ( fh: qword($C612062576589DDA); f: qword($95364AFE032A819D); e: -214 ); e10: -26 ),
- ( c: ( fh: qword($9E74D1B791E07E48); f: qword($775EA264CF55347E); e: -217 ); e10: -27 ),
- ( c: ( fh: qword($FD87B5F28300CA0D); f: qword($8BCA9D6E188853FC); e: -221 ); e10: -28 ),
- ( c: ( fh: qword($CAD2F7F5359A3B3E); f: qword($096EE45813A04330); e: -224 ); e10: -29 ),
- ( c: ( fh: qword($A2425FF75E14FC31); f: qword($A1258379A94D028D); e: -227 ); e10: -30 ),
- ( c: ( fh: qword($81CEB32C4B43FCF4); f: qword($80EACF948770CED7); e: -230 ); e10: -31 ),
- ( c: ( fh: qword($CFB11EAD453994BA); f: qword($67DE18EDA5814AF2); e: -234 ); e10: -32 ),
- ( c: ( fh: qword($A6274BBDD0FADD61); f: qword($ECB1AD8AEACDD58E); e: -237 ); e10: -33 ),
- ( c: ( fh: qword($84EC3C97DA624AB4); f: qword($BD5AF13BEF0B113F); e: -240 ); e10: -34 ),
- ( c: ( fh: qword($D4AD2DBFC3D07787); f: qword($955E4EC64B44E864); e: -244 ); e10: -35 ),
- ( c: ( fh: qword($AA242499697392D2); f: qword($DDE50BD1D5D0B9EA); e: -247 ); e10: -36 )
- );
- {$endif VALREAL_128}
- var
- i: integer;
- a, b: TDIY_FP_Power_of_10;
- begin
- diy_fp_cached_power10( exp10, a );
- i := a.e10 - exp10;
- if ( i < 0 ) then
- begin
- factor_10_inexact := 1; // overflow
- exit;
- end;
- if ( i > high( factor ) ) then
- begin
- factor_10_inexact := -1; // underflow
- exit;
- end;
- b := factor[i];
- {$ifdef grisu1_debug}
- assert( exp10 = a.e10 + b.e10 );
- {$endif grisu1_debug}
- if ( b.e10 = 0 ) then
- C := a
- else
- if ( a.e10 = 0 ) then
- C := b
- else
- begin
- C.c := diy_fp_multiply( a.c, b.c, TRUE );
- c.e10 := exp10;
- end;
- factor_10_inexact := 0; // no error
- end;
- (*-------------------------------------------------------
- | add_digit [local]
- |
- | This helper routine performs next digit addition:
- | X := X * 10 + digit
- |
- *-------------------------------------------------------*)
- {$ifdef VALREAL_80}
- procedure add_digit( var h: dword; var l: qword; digit: byte ); {$ifdef grisu1_inline}inline;{$endif}
- var
- temp1, temp2: qword;
- begin
- //
- temp1 := lo(l);
- inc( temp1, ( temp1 shl 3 ) + temp1 + digit );
- //
- temp2 := h;
- temp2 := ( temp2 shl 32 ) + hi(l);
- inc( temp2, ( temp2 shl 3 ) + temp2 + hi(temp1) );
- //
- h := hi(temp2);
- l := ( temp2 shl 32 ) + lo(temp1);
- //
- end;
- {$endif VALREAL_80}
- {$ifdef VALREAL_128}
- procedure add_digit( var h, l: qword; digit: byte ); {$ifdef grisu1_inline}inline;{$endif}
- var
- templ, temph, temp1, temp2: qword;
- begin
- templ := l;
- temph := h;
- diy_util_shl( temph, templ, 3 );
- //
- temp1 := lo(l);
- inc( temp1, lo(templ) + temp1 + digit );
- //
- temp2 := hi(l);
- inc( temp2, hi(templ) + temp2 + hi(temp1) );
- //
- inc( h, temph + h + hi(temp2) );
- l := ( temp2 shl 32 ) + lo(temp1);
- //
- end;
- {$endif VALREAL_128}
- (*-------------------------------------------------------
- | count_leading_zero [local] <<<duplicate>>>
- |
- | Counts number of 0-bits at most significant bit position.
- |
- *-------------------------------------------------------*)
- {$if defined(VALREAL_32) or defined(VALREAL_80)}
- function count_leading_zero( const X: dword ): integer; {$ifdef grisu1_inline}inline;{$endif}
- begin
- count_leading_zero := 31 - BSRdword( X );
- end;
- {$endif VALREAL_32 | VALREAL_80}
- {$ifndef VALREAL_32}
- function count_leading_zero( const X: qword ): integer; {$ifdef grisu1_inline}inline;{$endif}
- begin
- count_leading_zero := 63 - BSRqword( X );
- end;
- {$endif not VALREAL_32}
- (*-------------------------------------------------------
- | match_special [local]
- |
- | Routine compares source string tail with the string representing
- | one of special values: Inf | QNaN | SNaN
- |
- *-------------------------------------------------------*)
- function match_special( src_pos: integer; const src, spec: shortstring ): boolean;
- var
- sl, xl: integer;
- begin
- match_special := false;
- xl := length( src );
- sl := length( spec );
- if ( sl <> xl - src_pos + 1 ) then
- exit;
- {$ifdef grisu1_debug}
- assert( sl > 0 );
- {$endif grisu1_debug}
- repeat
- if ( UpCase( src[xl] ) <> UpCase( spec[sl] ) ) then
- exit;
- dec( xl );
- dec( sl );
- until sl <= 0;
- match_special := true;
- end;
- (****************************************************************************)
- var
- a: char;
- mantissa, bit_round, bit_round_mask: {$ifdef VALREAL_32} dword {$else} qword {$endif};
- {$ifdef VALREAL_80}
- mantissa_h: dword;
- {$endif}
- {$ifdef VALREAL_128}
- mantissa_h, bit_round_h, bit_round_mask_h: qword;
- {$endif}
- dig_num, exp10, overflow, n, src_pos, src_len: integer;
- exp_read, exp_temp: longint;
- b, dig_round, dig_sticky: byte;
- minus, exp_minus, special: boolean;
- C: TDIY_FP_Power_of_10;
- D: TDIY_FP;
- begin
- err_pos := 1;
- src_pos := 1;
- src_len := length(src);
- // Pre-initialize result
- {$ifdef GRISU1_A2F_ERROR_RET0}
- pack_float( val_real, false, 0, {$ifdef VALREAL_128} 0, {$endif} 0 );
- {$else}
- {-ifdef GRISU1_A2F_NO_SNAN}
- // "real indefinite"
- pack_float( val_real, true, C_EXP2_SPECIAL, {$ifdef VALREAL_128} C_FLT_MANT_QNANh, {$endif} C_FLT_MANT_QNAN );
- (*{-else}
- // SNaN is preferable for catching uninitialized variables access, but may cause troubles with implicit float type conversions
- pack_float( val_real, false, C_EXP2_SPECIAL, {$ifdef VALREAL_128} C_FLT_MANT_SNANh, {$endif} C_FLT_MANT_SNAN );
- {-endif}*)
- {$endif}
- // search for a sign skipping leading spaces
- minus := false;
- while ( src_pos <= src_len ) do
- begin
- a := src[src_pos];
- case a of
- '+':
- begin
- inc( src_pos );
- break;
- end;
- '-':
- begin
- minus := true;
- inc( src_pos );
- break;
- end;
- else
- if a <> ' ' then
- break;
- end;
- inc( src_pos );
- end;
- if ( src_pos > src_len ) then
- begin
- // syntax: nothing to evaluate
- err_pos := src_pos;
- exit;
- end;
- // handle specials
- case src[src_pos] of
- '0' .. '9', '.', 'E', 'e': special := false;
- else
- special := true;
- end;
- if special then
- begin
- mantissa := C_FLT_MANT_INF;
- {$ifdef VALREAL_128}
- mantissa_h := C_FLT_MANT_INFh;
- {$endif}
- if not match_special( src_pos, src, C_STR_INF ) then
- begin
- {$ifndef GRISU1_A2F_NO_SNAN}
- if match_special( src_pos, src, C_STR_SNAN ) then
- begin
- mantissa := C_FLT_MANT_SNAN;
- {$ifdef VALREAL_128}
- mantissa_h := C_FLT_MANT_SNANh;
- {$endif}
- end
- else
- {$endif GRISU1_A2F_NO_SNAN}
- if match_special( src_pos, src, C_STR_QNAN ) then
- begin
- {$ifdef GRISU1_A2F_QNAN_REAL_INDEFINITE}
- minus := TRUE;
- {$endif}
- mantissa := C_FLT_MANT_QNAN;
- {$ifdef VALREAL_128}
- mantissa_h := C_FLT_MANT_QNANh;
- {$endif}
- end
- else
- special := false;
- end;
- if special then
- begin
- pack_float( val_real, minus, C_EXP2_SPECIAL, {$ifdef VALREAL_128} mantissa_h, {$endif} mantissa );
- src_pos := 0;
- end;
- err_pos := src_pos;
- exit;
- end;
- // consume mantissa digits skipping leading zeroes
- // empty mantissa ("0.E#", ".0E#", ".E#", "E#") is allowed at least in D5
- mantissa := 0;
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- mantissa_h := 0;
- {$endif VALREAL_80 | VALREAL_128}
- dig_num := 0;
- exp10 := 0;
- dig_round := 0;
- dig_sticky := 0;
- // leading zero loop
- while ( src_pos <= src_len ) and ( src[src_pos] = '0' ) do
- inc( src_pos );
- // integer part loop
- while ( src_pos <= src_len ) do
- begin
- a := src[src_pos];
- if ( a < '0' ) or ( a > '9' ) then
- break;
- b := ord(a) - ord('0');
- if ( dig_num < C_MAX_DIGITS_ACCEPTED ) then
- // normal digit
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- inc( mantissa, ( mantissa shl 3 ) + mantissa + b )
- {$else VALREAL_80 | VALREAL_128}
- add_digit( mantissa_h, mantissa, b )
- {$endif VALREAL_*}
- else
- begin
- // over-required digits: use them for rounding later
- if ( dig_num = C_MAX_DIGITS_ACCEPTED ) then
- dig_round := b // main digit for rounding
- else
- dig_sticky := dig_sticky or b; // just "<>0" to judge "round to even" case later
- inc( exp10 ); // move [yet virtual] dot to the right
- end;
- inc( dig_num );
- inc( src_pos );
- end;
- // fraction part
- if ( src_pos <= src_len ) and ( src[src_pos] = '.' ) then
- begin
- // skip dot
- inc( src_pos );
- // leading zero loop, if integer part was 0
- if dig_num = 0 then
- while ( src_pos <= src_len ) and ( src[src_pos] = '0' ) do
- begin
- dec( exp10 ); // move the dot to the left
- inc( src_pos );
- end;
- // fraction part loop
- while ( src_pos <= src_len ) do
- begin
- a := src[src_pos];
- if ( a < '0' ) or ( a > '9' ) then
- break;
- b := ord(a) - ord('0');
- if ( dig_num < C_MAX_DIGITS_ACCEPTED ) then
- begin
- // normal digit
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- inc( mantissa, ( mantissa shl 3 ) + mantissa + b );
- {$else VALREAL_80 | VALREAL_128}
- add_digit( mantissa_h, mantissa, b );
- {$endif VALREAL_*}
- dec( exp10 ); // move the dot to the left
- end
- else
- begin
- // over-required digits: use them for rounding later
- if ( dig_num = C_MAX_DIGITS_ACCEPTED ) then
- dig_round := b // main digit for rounding
- else
- dig_sticky := dig_sticky or b; // just "<>0" to judge "round to even" case later
- end;
- inc( dig_num );
- inc( src_pos );
- end;
- end;
- // round digits
- {$ifndef GRISU1_A2F_HALF_ROUNDUP}
- if ( dig_round = 5 ) and ( dig_sticky = 0 ) and ( mantissa and 1 = 0 ) then
- // need to "round to even", and already even..
- dec( dig_round ); // ..so force no round-up
- {$endif not GRISU1_A2F_HALF_ROUNDUP}
- if ( dig_round >= 5 ) then
- begin
- // round-up
- inc( mantissa );
- {$if defined(VALREAL_80) or defined(VALREAL_128)}
- if ( mantissa = 0 ) then
- inc( mantissa_h );
- {$endif VALREAL_*}
- end;
- // consume exponent digits
- exp_read := 0;
- if ( src_pos <= src_len ) then
- begin
- exp_minus := false;
- case src[src_pos] of
- 'e', 'E':;
- else
- // syntax: "E" expected
- err_pos := src_pos;
- exit;
- end;
- inc( src_pos );
- if ( src_pos > src_len ) then
- begin
- // syntax: empty exponent
- err_pos := src_pos;
- exit;
- end;
- case src[src_pos] of
- '+':
- inc( src_pos );
- '-':
- begin
- exp_minus := true;
- inc( src_pos );
- end;
- end;
- while ( src_pos <= src_len ) do
- begin
- a := src[src_pos];
- if ( a < '0' ) or ( a > '9' ) then
- begin
- // syntax: bad digit
- err_pos := src_pos;
- exit;
- end;
- if ( exp_read < 100000 ) then
- inc( exp_read, ( exp_read shl 3 ) + exp_read + ord(a) - ord('0') );
- // else just syntax check
- inc( src_pos );
- end;
- if exp_minus then
- exp_read := - exp_read;
- end;
- exp_temp := exp_read + exp10;
- if ( exp_read >= 100000 ) or ( exp_temp >= C_EXP10_OVER ) then
- exp10 := C_EXP10_OVER
- else
- if ( exp_read <= - 100000 ) or ( exp_temp <= - C_EXP10_OVER ) then
- exp10 := - C_EXP10_OVER
- else
- exp10 := exp_temp;
- // nothing should remain in the "src" here
- if ( src_pos <= src_len ) then
- begin
- err_pos := src_pos;
- exit;
- end;
- // zero [or negative exponent overflow]
- if ( mantissa {$if defined(VALREAL_80) or defined(VALREAL_128)} or mantissa_h {$endif} = 0 )
- or ( exp10 <= - C_EXP10_OVER ) then
- begin
- pack_float( val_real, minus, 0, {$ifdef VALREAL_128} 0, {$endif} 0 ); // +0|-0
- err_pos := 0;
- exit;
- end;
- if ( exp10 >= C_EXP10_OVER ) then
- // exponent overflowed -> return Inf
- overflow := 1
- else
- begin
- // make DIY_FP
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- n := count_leading_zero( mantissa );
- D.f := mantissa shl n;
- {$else VALREAL_80 | VALREAL_128}
- if ( mantissa_h = 0 ) then
- n := count_leading_zero( mantissa ) + sizeof( mantissa_h ) * 8
- else
- n := count_leading_zero( mantissa_h );
- D.f := mantissa;
- D.fh := mantissa_h;
- diy_util_shl( D.fh, D.f, n );
- {$endif VALREAL_*}
- D.e := - n;
- // get factor
- overflow := factor_10_inexact( exp10, C ); // <>0 -> over/underflow
- end;
- if ( overflow = 0 ) then
- begin
- // multiply
- if ( C.e10 <> 0 ) then
- // C <> 1
- D := diy_fp_multiply( D, C.c, TRUE );
- // calculate round increment
- if ( D.f and C_DIY_ROUND_MASK = C_DIY_ROUND_BIT ) then
- // round to even and already even
- b := 0
- else
- b := ord( D.f and C_DIY_ROUND_BIT <> 0 );
- // shift and round
- {$if defined(VALREAL_32) or defined(VALREAL_64)}
- D.f := ( D.f shr C_DIY_SHR_TO_FLT ) + b;
- // handle round overflow
- if ( D.f and ( C_FLT_INT_BIT shl 1 ) <> 0 ) then
- begin
- D.f := D.f shr 1;
- inc( D.e );
- end;
- {$else VALREAL_80 or VALREAL_128}
- diy_util_shr( D.fh, D.f, C_DIY_SHR_TO_FLT );
- if ( b <> 0 ) then
- diy_util_add( D.fh, D.f, 0, b );
- // handle round overflow
- if ( D.fh {$ifdef VALREAL_128} and ( C_FLT_INT_BITh shl 1 ) {$endif} <> 0 ) then
- begin
- diy_util_shr( D.fh, D.f, 1 );
- inc( D.e );
- end;
- {$if defined(grisu1_debug) and defined(VALREAL_80)}
- assert( D.fh = 0 );
- {$endif grisu1_debug}
- {$endif VALREAL_*}
- // calculate exponent
- D.e := D.e + C_DIY_EXP_TO_FLT;
- if ( D.e >= C_EXP2_SPECIAL ) then
- ///////////////////
- //
- // overflow
- //
- ///////////////////
- overflow := 1
- else
- if ( D.e < - C_FRAC2_BITS ) then
- ///////////////////
- //
- // underflow
- //
- ///////////////////
- overflow := -1
- else
- if ( D.e <= 0 ) then
- begin
- ///////////////////
- //
- // denormal (and also an extreme case of "D.e=-C_FRAC2_BITS", where
- // rounding may produce either the lowest denormal or underflow)
- //
- ///////////////////
- n := 1 - D.e; // SHR amount
- // round bit
- {$ifdef VALREAL_32}
- bit_round := dword(1) shl ( n - 1 );
- {$endif VALREAL_32}
- {$if defined(VALREAL_64) or defined(VALREAL_80)}
- bit_round := qword(1) shl ( n - 1 );
- {$endif VALREAL_64 | VALREAL_80}
- {$ifdef VALREAL_128}
- bit_round := 1;
- bit_round_h := 0;
- diy_util_shl( bit_round_h, bit_round, n - 1 );
- // mask for ulp and all least bits including the round one
- bit_round_mask := bit_round;
- bit_round_mask_h := bit_round_h;
- diy_util_shl( bit_round_mask_h, bit_round_mask, 2 );
- if ( bit_round_mask = 0 ) then
- dec( bit_round_mask_h );
- dec( bit_round_mask );
- {$else not VALREAL_128}
- // mask for ulp and all least bits including the round one
- bit_round_mask := ( bit_round shl 2 ) - 1;
- // Note[floatx80]: works correctly despite the overflow is ignored in extreme case "D.e=-C_FRAC2_BITS"
- {$endif VALREAL_*}
- // round increment
- if ( D.f and bit_round_mask = bit_round ) {$ifdef VALREAL_128} and ( D.fh and bit_round_mask_h = bit_round_h ) {$endif} then
- // round to even and already even -> no round-up
- b := 0
- else
- b := ord( ( D.f and bit_round ) {$ifdef VALREAL_128} or ( D.fh and bit_round_h ) {$endif} <> 0 );
- // shift and round
- if ( D.e = - C_FRAC2_BITS ) then
- begin
- // extreme case: rounding may result in either lowest denormal or zero
- {$ifdef VALREAL_128}
- D.fh := 0;
- {$endif VALREAL_128}
- D.f := b;
- if ( b = 0 ) then
- overflow := -1; // underflow
- end
- else
- {$ifdef VALREAL_128}
- begin
- diy_util_shr( D.fh, D.f, n );
- if ( b <> 0 ) then
- diy_util_add( D.fh, D.f, 0, b );
- end;
- {$else not VALREAL_128}
- D.f := ( D.f shr n ) + b;
- {$endif VALREAL_*}
- D.e := 0;
- {$ifdef grisu1_debug}
- {$ifdef VALREAL_128}
- assert( ( D.f or D.fh <> 0 ) or ( overflow = -1 ) );
- assert( D.fh and not C_FLT_FRAC_MASKh = 0 );
- {$else not VALREAL_128}
- assert( ( D.f <> 0 ) or ( overflow = -1 ) );
- assert( D.f and not C_FLT_FRAC_MASK = 0 );
- {$endif VALREAL_*}
- {$endif grisu1_debug}
- end
- else
- begin
- ///////////////////
- //
- // normal: 0 < D.e < C_EXP2_SPECIAL
- //
- ///////////////////
- {$ifdef grisu1_debug}
- {$ifdef VALREAL_32}
- assert( D.f and not C_FLT_FRAC_MASK = C_FLT_INT_BIT );
- {$endif VALREAL_32}
- {$if defined(VALREAL_64) or defined(VALREAL_80)}
- assert( D.f and not qword(C_FLT_FRAC_MASK) = qword(C_FLT_INT_BIT) );
- {$endif VALREAL_64 | VALREAL_80}
- {$ifdef VALREAL_128}
- assert( D.fh and not C_FLT_FRAC_MASKh = C_FLT_INT_BITh );
- {$endif VALREAL_128}
- {$endif grisu1_debug}
- {$ifndef VALREAL_80}
- // clear the implicit integer bit
- {$ifdef VALREAL_128}
- D.fh := D.fh and C_FLT_FRAC_MASKh;
- {$else not VALREAL_128}
- D.f := D.f and C_FLT_FRAC_MASK;
- {$endif VALREAL_*}
- {$endif not VALREAL_80}
- end;
- end;
- // final result
- if ( overflow < 0 ) then
- begin
- // underflow [+0|-0]
- pack_float( val_real, minus, 0, {$ifdef VALREAL_128} 0, {$endif} 0 );
- end
- else
- if ( overflow > 0 ) then
- begin
- // overflow [+Inf|-Inf]
- pack_float( val_real, minus, C_EXP2_SPECIAL, {$ifdef VALREAL_128} C_FLT_MANT_INFh, {$endif} C_FLT_MANT_INF );
- end
- else
- begin
- // no error
- pack_float( val_real, minus, D.e, {$ifdef VALREAL_128} D.fh, {$endif} D.f );
- end;
- err_pos := 0;
- end;
|