diff --git a/CMakeLists.txt b/CMakeLists.txt index 69c39ce16..f5858e064 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -474,6 +474,10 @@ add_executable(unicode_gen EXCLUDE_FROM_ALL ) target_compile_definitions(unicode_gen PRIVATE ${qjs_defines}) +add_executable(ryu_gen EXCLUDE_FROM_ALL + ryu_gen.c +) + add_executable(function_source gen/function_source.c ) diff --git a/Makefile b/Makefile index d1131c5c6..ce1b10428 100644 --- a/Makefile +++ b/Makefile @@ -146,4 +146,10 @@ unicode_gen: $(BUILD_DIR) libunicode-table.h: unicode_gen $(BUILD_DIR)/unicode_gen unicode $@ -.PHONY: all amalgam ctest cxxtest debug fuzz jscheck install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen $(QJS) $(QJSC) +ryu_gen: $(BUILD_DIR) + cmake --build $(BUILD_DIR) --target ryu_gen + +dtoa-ryu-table.h: ryu_gen + $(BUILD_DIR)/ryu_gen $@ + +.PHONY: all amalgam ctest cxxtest debug fuzz jscheck install clean codegen distclean stats test test262 test262-update test262-check microbench unicode_gen ryu_gen $(QJS) $(QJSC) diff --git a/amalgam.js b/amalgam.js index 37492395b..f6ab3b1bf 100644 --- a/amalgam.js +++ b/amalgam.js @@ -3,6 +3,7 @@ import {loadFile, writeFile} from "qjs:std" const cutils_h = loadFile("cutils.h") const dtoa_c = loadFile("dtoa.c") const dtoa_h = loadFile("dtoa.h") +const dtoa_ryu_table_h = loadFile("dtoa-ryu-table.h") const libregexp_c = loadFile("libregexp.c") const libregexp_h = loadFile("libregexp.h") const libregexp_opcode_h = loadFile("libregexp-opcode.h") @@ -43,6 +44,7 @@ let source = "#if defined(QJS_BUILD_LIBC) && defined(__linux__) && !defined(_GNU source = source.replace(/#include "quickjs-atom.h"/g, quickjs_atom_h) source = source.replace(/#include "quickjs-opcode.h"/g, quickjs_opcode_h) source = source.replace(/#include "libregexp-opcode.h"/g, libregexp_opcode_h) +source = source.replace(/#include "dtoa-ryu-table.h"/g, dtoa_ryu_table_h) source = source.replace(/#include "builtin-array-fromasync.h"/g, gen_builtin_array_fromasync_h) source = source.replace(/#include "builtin-iterator-zip.h"/g, diff --git a/dtoa-ryu-table.h b/dtoa-ryu-table.h new file mode 100644 index 000000000..9d2ba0950 --- /dev/null +++ b/dtoa-ryu-table.h @@ -0,0 +1,363 @@ +/* Ryu double->shortest-decimal lookup tables. + * + * Automatically generated by ryu_gen.c; see dtoa.c for the algorithm. + * + * Copyright 2018 Ulf Adams + * + * Licensed under the Apache License, Version 2.0 (Apache-2.0) or, at your + * option, the Boost Software License, Version 1.0 (BSL-1.0). + */ +#ifndef DTOA_RYU_TABLE_H +#define DTOA_RYU_TABLE_H + +#include + +// These tables are generated by ryu_gen.c. +#define DOUBLE_POW5_INV_BITCOUNT 125 +#define DOUBLE_POW5_BITCOUNT 125 + +#define DOUBLE_POW5_INV_TABLE_SIZE 342 +#define DOUBLE_POW5_TABLE_SIZE 326 + +static const uint64_t DOUBLE_POW5_INV_SPLIT[DOUBLE_POW5_INV_TABLE_SIZE][2] = { + { 1u, 2305843009213693952u }, { 11068046444225730970u, 1844674407370955161u }, + { 5165088340638674453u, 1475739525896764129u }, { 7821419487252849886u, 1180591620717411303u }, + { 8824922364862649494u, 1888946593147858085u }, { 7059937891890119595u, 1511157274518286468u }, + { 13026647942995916322u, 1208925819614629174u }, { 9774590264567735146u, 1934281311383406679u }, + { 11509021026396098440u, 1547425049106725343u }, { 16585914450600699399u, 1237940039285380274u }, + { 15469416676735388068u, 1980704062856608439u }, { 16064882156130220778u, 1584563250285286751u }, + { 9162556910162266299u, 1267650600228229401u }, { 7281393426775805432u, 2028240960365167042u }, + { 16893161185646375315u, 1622592768292133633u }, { 2446482504291369283u, 1298074214633706907u }, + { 7603720821608101175u, 2076918743413931051u }, { 2393627842544570617u, 1661534994731144841u }, + { 16672297533003297786u, 1329227995784915872u }, { 11918280793837635165u, 2126764793255865396u }, + { 5845275820328197809u, 1701411834604692317u }, { 15744267100488289217u, 1361129467683753853u }, + { 3054734472329800808u, 2177807148294006166u }, { 17201182836831481939u, 1742245718635204932u }, + { 6382248639981364905u, 1393796574908163946u }, { 2832900194486363201u, 2230074519853062314u }, + { 5955668970331000884u, 1784059615882449851u }, { 1075186361522890384u, 1427247692705959881u }, + { 12788344622662355584u, 2283596308329535809u }, { 13920024512871794791u, 1826877046663628647u }, + { 3757321980813615186u, 1461501637330902918u }, { 10384555214134712795u, 1169201309864722334u }, + { 5547241898389809503u, 1870722095783555735u }, { 4437793518711847602u, 1496577676626844588u }, + { 10928932444453298728u, 1197262141301475670u }, { 17486291911125277965u, 1915619426082361072u }, + { 6610335899416401726u, 1532495540865888858u }, { 12666966349016942027u, 1225996432692711086u }, + { 12888448528943286597u, 1961594292308337738u }, { 17689456452638449924u, 1569275433846670190u }, + { 14151565162110759939u, 1255420347077336152u }, { 7885109000409574610u, 2008672555323737844u }, + { 9997436015069570011u, 1606938044258990275u }, { 7997948812055656009u, 1285550435407192220u }, + { 12796718099289049614u, 2056880696651507552u }, { 2858676849947419045u, 1645504557321206042u }, + { 13354987924183666206u, 1316403645856964833u }, { 17678631863951955605u, 2106245833371143733u }, + { 3074859046935833515u, 1684996666696914987u }, { 13527933681774397782u, 1347997333357531989u }, + { 10576647446613305481u, 2156795733372051183u }, { 15840015586774465031u, 1725436586697640946u }, + { 8982663654677661702u, 1380349269358112757u }, { 18061610662226169046u, 2208558830972980411u }, + { 10759939715039024913u, 1766847064778384329u }, { 12297300586773130254u, 1413477651822707463u }, + { 15986332124095098083u, 2261564242916331941u }, { 9099716884534168143u, 1809251394333065553u }, + { 14658471137111155161u, 1447401115466452442u }, { 4348079280205103483u, 1157920892373161954u }, + { 14335624477811986218u, 1852673427797059126u }, { 7779150767507678651u, 1482138742237647301u }, + { 2533971799264232598u, 1185710993790117841u }, { 15122401323048503126u, 1897137590064188545u }, + { 12097921058438802501u, 1517710072051350836u }, { 5988988032009131678u, 1214168057641080669u }, + { 16961078480698431330u, 1942668892225729070u }, { 13568862784558745064u, 1554135113780583256u }, + { 7165741412905085728u, 1243308091024466605u }, { 11465186260648137165u, 1989292945639146568u }, + { 16550846638002330379u, 1591434356511317254u }, { 16930026125143774626u, 1273147485209053803u }, + { 4951948911778577463u, 2037035976334486086u }, { 272210314680951647u, 1629628781067588869u }, + { 3907117066486671641u, 1303703024854071095u }, { 6251387306378674625u, 2085924839766513752u }, + { 16069156289328670670u, 1668739871813211001u }, { 9165976216721026213u, 1334991897450568801u }, + { 7286864317269821294u, 2135987035920910082u }, { 16897537898041588005u, 1708789628736728065u }, + { 13518030318433270404u, 1367031702989382452u }, { 6871453250525591353u, 2187250724783011924u }, + { 9186511415162383406u, 1749800579826409539u }, { 11038557946871817048u, 1399840463861127631u }, + { 10282995085511086630u, 2239744742177804210u }, { 8226396068408869304u, 1791795793742243368u }, + { 13959814484210916090u, 1433436634993794694u }, { 11267656730511734774u, 2293498615990071511u }, + { 5324776569667477496u, 1834798892792057209u }, { 7949170070475892320u, 1467839114233645767u }, + { 17427382500606444826u, 1174271291386916613u }, { 5747719112518849781u, 1878834066219066582u }, + { 15666221734240810795u, 1503067252975253265u }, { 12532977387392648636u, 1202453802380202612u }, + { 5295368560860596524u, 1923926083808324180u }, { 4236294848688477220u, 1539140867046659344u }, + { 7078384693692692099u, 1231312693637327475u }, { 11325415509908307358u, 1970100309819723960u }, + { 9060332407926645887u, 1576080247855779168u }, { 14626963555825137356u, 1260864198284623334u }, + { 12335095245094488799u, 2017382717255397335u }, { 9868076196075591040u, 1613906173804317868u }, + { 15273158586344293478u, 1291124939043454294u }, { 13369007293925138595u, 2065799902469526871u }, + { 7005857020398200553u, 1652639921975621497u }, { 16672732060544291412u, 1322111937580497197u }, + { 11918976037903224966u, 2115379100128795516u }, { 5845832015580669650u, 1692303280103036413u }, + { 12055363241948356366u, 1353842624082429130u }, { 841837113407818570u, 2166148198531886609u }, + { 4362818505468165179u, 1732918558825509287u }, { 14558301248600263113u, 1386334847060407429u }, + { 12225235553534690011u, 2218135755296651887u }, { 2401490813343931363u, 1774508604237321510u }, + { 1921192650675145090u, 1419606883389857208u }, { 17831303500047873437u, 2271371013423771532u }, + { 6886345170554478103u, 1817096810739017226u }, { 1819727321701672159u, 1453677448591213781u }, + { 16213177116328979020u, 1162941958872971024u }, { 14873036941900635463u, 1860707134196753639u }, + { 15587778368262418694u, 1488565707357402911u }, { 8780873879868024632u, 1190852565885922329u }, + { 2981351763563108441u, 1905364105417475727u }, { 13453127855076217722u, 1524291284333980581u }, + { 7073153469319063855u, 1219433027467184465u }, { 11317045550910502167u, 1951092843947495144u }, + { 12742985255470312057u, 1560874275157996115u }, { 10194388204376249646u, 1248699420126396892u }, + { 1553625868034358140u, 1997919072202235028u }, { 8621598323911307159u, 1598335257761788022u }, + { 17965325103354776697u, 1278668206209430417u }, { 13987124906400001422u, 2045869129935088668u }, + { 121653480894270168u, 1636695303948070935u }, { 97322784715416134u, 1309356243158456748u }, + { 14913111714512307107u, 2094969989053530796u }, { 8241140556867935363u, 1675975991242824637u }, + { 17660958889720079260u, 1340780792994259709u }, { 17189487779326395846u, 2145249268790815535u }, + { 13751590223461116677u, 1716199415032652428u }, { 18379969808252713988u, 1372959532026121942u }, + { 14650556434236701088u, 2196735251241795108u }, { 652398703163629901u, 1757388200993436087u }, + { 11589965406756634890u, 1405910560794748869u }, { 7475898206584884855u, 2249456897271598191u }, + { 2291369750525997561u, 1799565517817278553u }, { 9211793429904618695u, 1439652414253822842u }, + { 18428218302589300235u, 2303443862806116547u }, { 7363877012587619542u, 1842755090244893238u }, + { 13269799239553916280u, 1474204072195914590u }, { 10615839391643133024u, 1179363257756731672u }, + { 2227947767661371545u, 1886981212410770676u }, { 16539753473096738529u, 1509584969928616540u }, + { 13231802778477390823u, 1207667975942893232u }, { 6413489186596184024u, 1932268761508629172u }, + { 16198837793502678189u, 1545815009206903337u }, { 5580372605318321905u, 1236652007365522670u }, + { 8928596168509315048u, 1978643211784836272u }, { 18210923379033183008u, 1582914569427869017u }, + { 7190041073742725760u, 1266331655542295214u }, { 436019273762630246u, 2026130648867672343u }, + { 7727513048493924843u, 1620904519094137874u }, { 9871359253537050198u, 1296723615275310299u }, + { 4726128361433549347u, 2074757784440496479u }, { 7470251503888749801u, 1659806227552397183u }, + { 13354898832594820487u, 1327844982041917746u }, { 13989140502667892133u, 2124551971267068394u }, + { 14880661216876224029u, 1699641577013654715u }, { 11904528973500979224u, 1359713261610923772u }, + { 4289851098633925465u, 2175541218577478036u }, { 18189276137874781665u, 1740432974861982428u }, + { 3483374466074094362u, 1392346379889585943u }, { 1884050330976640656u, 2227754207823337509u }, + { 5196589079523222848u, 1782203366258670007u }, { 15225317707844309248u, 1425762693006936005u }, + { 5913764258841343181u, 2281220308811097609u }, { 8420360221814984868u, 1824976247048878087u }, + { 17804334621677718864u, 1459980997639102469u }, { 17932816512084085415u, 1167984798111281975u }, + { 10245762345624985047u, 1868775676978051161u }, { 4507261061758077715u, 1495020541582440929u }, + { 7295157664148372495u, 1196016433265952743u }, { 7982903447895485668u, 1913626293225524389u }, + { 10075671573058298858u, 1530901034580419511u }, { 4371188443704728763u, 1224720827664335609u }, + { 14372599139411386667u, 1959553324262936974u }, { 15187428126271019657u, 1567642659410349579u }, + { 15839291315758726049u, 1254114127528279663u }, { 3206773216762499739u, 2006582604045247462u }, + { 13633465017635730761u, 1605266083236197969u }, { 14596120828850494932u, 1284212866588958375u }, + { 4907049252451240275u, 2054740586542333401u }, { 236290587219081897u, 1643792469233866721u }, + { 14946427728742906810u, 1315033975387093376u }, { 16535586736504830250u, 2104054360619349402u }, + { 5849771759720043554u, 1683243488495479522u }, { 15747863852001765813u, 1346594790796383617u }, + { 10439186904235184007u, 2154551665274213788u }, { 15730047152871967852u, 1723641332219371030u }, + { 12584037722297574282u, 1378913065775496824u }, { 9066413911450387881u, 2206260905240794919u }, + { 10942479943902220628u, 1765008724192635935u }, { 8753983955121776503u, 1412006979354108748u }, + { 10317025513452932081u, 2259211166966573997u }, { 874922781278525018u, 1807368933573259198u }, + { 8078635854506640661u, 1445895146858607358u }, { 13841606313089133175u, 1156716117486885886u }, + { 14767872471458792434u, 1850745787979017418u }, { 746251532941302978u, 1480596630383213935u }, + { 597001226353042382u, 1184477304306571148u }, { 15712597221132509104u, 1895163686890513836u }, + { 8880728962164096960u, 1516130949512411069u }, { 10793931984473187891u, 1212904759609928855u }, + { 17270291175157100626u, 1940647615375886168u }, { 2748186495899949531u, 1552518092300708935u }, + { 2198549196719959625u, 1242014473840567148u }, { 18275073973719576693u, 1987223158144907436u }, + { 10930710364233751031u, 1589778526515925949u }, { 12433917106128911148u, 1271822821212740759u }, + { 8826220925580526867u, 2034916513940385215u }, { 7060976740464421494u, 1627933211152308172u }, + { 16716827836597268165u, 1302346568921846537u }, { 11989529279587987770u, 2083754510274954460u }, + { 9591623423670390216u, 1667003608219963568u }, { 15051996368420132820u, 1333602886575970854u }, + { 13015147745246481542u, 2133764618521553367u }, { 3033420566713364587u, 1707011694817242694u }, + { 6116085268112601993u, 1365609355853794155u }, { 9785736428980163188u, 2184974969366070648u }, + { 15207286772667951197u, 1747979975492856518u }, { 1097782973908629988u, 1398383980394285215u }, + { 1756452758253807981u, 2237414368630856344u }, { 5094511021344956708u, 1789931494904685075u }, + { 4075608817075965366u, 1431945195923748060u }, { 6520974107321544586u, 2291112313477996896u }, + { 1527430471115325346u, 1832889850782397517u }, { 12289990821117991246u, 1466311880625918013u }, + { 17210690286378213644u, 1173049504500734410u }, { 9090360384495590213u, 1876879207201175057u }, + { 18340334751822203140u, 1501503365760940045u }, { 14672267801457762512u, 1201202692608752036u }, + { 16096930852848599373u, 1921924308174003258u }, { 1809498238053148529u, 1537539446539202607u }, + { 12515645034668249793u, 1230031557231362085u }, { 1578287981759648052u, 1968050491570179337u }, + { 12330676829633449412u, 1574440393256143469u }, { 13553890278448669853u, 1259552314604914775u }, + { 3239480371808320148u, 2015283703367863641u }, { 17348979556414297411u, 1612226962694290912u }, + { 6500486015647617283u, 1289781570155432730u }, { 10400777625036187652u, 2063650512248692368u }, + { 15699319729512770768u, 1650920409798953894u }, { 16248804598352126938u, 1320736327839163115u }, + { 7551343283653851484u, 2113178124542660985u }, { 6041074626923081187u, 1690542499634128788u }, + { 12211557331022285596u, 1352433999707303030u }, { 1091747655926105338u, 2163894399531684849u }, + { 4562746939482794594u, 1731115519625347879u }, { 7339546366328145998u, 1384892415700278303u }, + { 8053925371383123274u, 2215827865120445285u }, { 6443140297106498619u, 1772662292096356228u }, + { 12533209867169019542u, 1418129833677084982u }, { 5295740528502789974u, 2269007733883335972u }, + { 15304638867027962949u, 1815206187106668777u }, { 4865013464138549713u, 1452164949685335022u }, + { 14960057215536570740u, 1161731959748268017u }, { 9178696285890871890u, 1858771135597228828u }, + { 14721654658196518159u, 1487016908477783062u }, { 4398626097073393881u, 1189613526782226450u }, + { 7037801755317430209u, 1903381642851562320u }, { 5630241404253944167u, 1522705314281249856u }, + { 814844308661245011u, 1218164251424999885u }, { 1303750893857992017u, 1949062802279999816u }, + { 15800395974054034906u, 1559250241823999852u }, { 5261619149759407279u, 1247400193459199882u }, + { 12107939454356961969u, 1995840309534719811u }, { 5997002748743659252u, 1596672247627775849u }, + { 8486951013736837725u, 1277337798102220679u }, { 2511075177753209390u, 2043740476963553087u }, + { 13076906586428298482u, 1634992381570842469u }, { 14150874083884549109u, 1307993905256673975u }, + { 4194654460505726958u, 2092790248410678361u }, { 18113118827372222859u, 1674232198728542688u }, + { 3422448617672047318u, 1339385758982834151u }, { 16543964232501006678u, 2143017214372534641u }, + { 9545822571258895019u, 1714413771498027713u }, { 15015355686490936662u, 1371531017198422170u }, + { 5577825024675947042u, 2194449627517475473u }, { 11840957649224578280u, 1755559702013980378u }, + { 16851463748863483271u, 1404447761611184302u }, { 12204946739213931940u, 2247116418577894884u }, + { 13453306206113055875u, 1797693134862315907u }, { 3383947335406624054u, 1438154507889852726u }, + { 16482362180876329456u, 2301047212623764361u }, { 9496540929959153242u, 1840837770099011489u }, + { 11286581558709232917u, 1472670216079209191u }, { 5339916432225476010u, 1178136172863367353u }, + { 4854517476818851293u, 1885017876581387765u }, { 3883613981455081034u, 1508014301265110212u }, + { 14174937629389795797u, 1206411441012088169u }, { 11611853762797942306u, 1930258305619341071u }, + { 5600134195496443521u, 1544206644495472857u }, { 15548153800622885787u, 1235365315596378285u }, + { 6430302007287065643u, 1976584504954205257u }, { 16212288050055383484u, 1581267603963364205u }, + { 12969830440044306787u, 1265014083170691364u }, { 9683682259845159889u, 2024022533073106183u }, + { 15125643437359948558u, 1619218026458484946u }, { 8411165935146048523u, 1295374421166787957u }, + { 17147214310975587960u, 2072599073866860731u }, { 10028422634038560045u, 1658079259093488585u }, + { 8022738107230848036u, 1326463407274790868u }, { 9147032156827446534u, 2122341451639665389u }, + { 11006974540203867551u, 1697873161311732311u }, { 5116230817421183718u, 1358298529049385849u }, + { 15564666937357714594u, 2173277646479017358u }, { 1383687105660440706u, 1738622117183213887u }, + { 12174996128754083534u, 1390897693746571109u }, { 8411947361780802685u, 2225436309994513775u }, + { 6729557889424642148u, 1780349047995611020u }, { 5383646311539713719u, 1424279238396488816u }, + { 1235136468979721303u, 2278846781434382106u }, { 15745504434151418335u, 1823077425147505684u }, + { 16285752362063044992u, 1458461940118004547u }, { 5649904260166615347u, 1166769552094403638u }, + { 5350498001524674232u, 1866831283351045821u }, { 591049586477829062u, 1493465026680836657u }, + { 11540886113407994219u, 1194772021344669325u }, { 18673707743239135u, 1911635234151470921u }, + { 14772334225162232601u, 1529308187321176736u }, { 8128518565387875758u, 1223446549856941389u }, + { 1937583260394870242u, 1957514479771106223u }, { 8928764237799716840u, 1566011583816884978u }, + { 14521709019723594119u, 1252809267053507982u }, { 8477339172590109297u, 2004494827285612772u }, + { 17849917782297818407u, 1603595861828490217u }, { 6901236596354434079u, 1282876689462792174u }, + { 18420676183650915173u, 2052602703140467478u }, { 3668494502695001169u, 1642082162512373983u }, + { 10313493231639821582u, 1313665730009899186u }, { 9122891541139893884u, 2101865168015838698u }, + { 14677010862395735754u, 1681492134412670958u }, { 673562245690857633u, 1345193707530136767u } +}; + + +static const uint64_t DOUBLE_POW5_SPLIT[DOUBLE_POW5_TABLE_SIZE][2] = { + { 0u, 1152921504606846976u }, { 0u, 1441151880758558720u }, + { 0u, 1801439850948198400u }, { 0u, 2251799813685248000u }, + { 0u, 1407374883553280000u }, { 0u, 1759218604441600000u }, + { 0u, 2199023255552000000u }, { 0u, 1374389534720000000u }, + { 0u, 1717986918400000000u }, { 0u, 2147483648000000000u }, + { 0u, 1342177280000000000u }, { 0u, 1677721600000000000u }, + { 0u, 2097152000000000000u }, { 0u, 1310720000000000000u }, + { 0u, 1638400000000000000u }, { 0u, 2048000000000000000u }, + { 0u, 1280000000000000000u }, { 0u, 1600000000000000000u }, + { 0u, 2000000000000000000u }, { 0u, 1250000000000000000u }, + { 0u, 1562500000000000000u }, { 0u, 1953125000000000000u }, + { 0u, 1220703125000000000u }, { 0u, 1525878906250000000u }, + { 0u, 1907348632812500000u }, { 0u, 1192092895507812500u }, + { 0u, 1490116119384765625u }, { 4611686018427387904u, 1862645149230957031u }, + { 9799832789158199296u, 1164153218269348144u }, { 12249790986447749120u, 1455191522836685180u }, + { 15312238733059686400u, 1818989403545856475u }, { 14528612397897220096u, 2273736754432320594u }, + { 13692068767113150464u, 1421085471520200371u }, { 12503399940464050176u, 1776356839400250464u }, + { 15629249925580062720u, 2220446049250313080u }, { 9768281203487539200u, 1387778780781445675u }, + { 7598665485932036096u, 1734723475976807094u }, { 274959820560269312u, 2168404344971008868u }, + { 9395221924704944128u, 1355252715606880542u }, { 2520655369026404352u, 1694065894508600678u }, + { 12374191248137781248u, 2117582368135750847u }, { 14651398557727195136u, 1323488980084844279u }, + { 13702562178731606016u, 1654361225106055349u }, { 3293144668132343808u, 2067951531382569187u }, + { 18199116482078572544u, 1292469707114105741u }, { 8913837547316051968u, 1615587133892632177u }, + { 15753982952572452864u, 2019483917365790221u }, { 12152082354571476992u, 1262177448353618888u }, + { 15190102943214346240u, 1577721810442023610u }, { 9764256642163156992u, 1972152263052529513u }, + { 17631875447420442880u, 1232595164407830945u }, { 8204786253993389888u, 1540743955509788682u }, + { 1032610780636961552u, 1925929944387235853u }, { 2951224747111794922u, 1203706215242022408u }, + { 3689030933889743652u, 1504632769052528010u }, { 13834660704216955373u, 1880790961315660012u }, + { 17870034976990372916u, 1175494350822287507u }, { 17725857702810578241u, 1469367938527859384u }, + { 3710578054803671186u, 1836709923159824231u }, { 26536550077201078u, 2295887403949780289u }, + { 11545800389866720434u, 1434929627468612680u }, { 14432250487333400542u, 1793662034335765850u }, + { 8816941072311974870u, 2242077542919707313u }, { 17039803216263454053u, 1401298464324817070u }, + { 12076381983474541759u, 1751623080406021338u }, { 5872105442488401391u, 2189528850507526673u }, + { 15199280947623720629u, 1368455531567204170u }, { 9775729147674874978u, 1710569414459005213u }, + { 16831347453020981627u, 2138211768073756516u }, { 1296220121283337709u, 1336382355046097823u }, + { 15455333206886335848u, 1670477943807622278u }, { 10095794471753144002u, 2088097429759527848u }, + { 6309871544845715001u, 1305060893599704905u }, { 12499025449484531656u, 1631326116999631131u }, + { 11012095793428276666u, 2039157646249538914u }, { 11494245889320060820u, 1274473528905961821u }, + { 532749306367912313u, 1593091911132452277u }, { 5277622651387278295u, 1991364888915565346u }, + { 7910200175544436838u, 1244603055572228341u }, { 14499436237857933952u, 1555753819465285426u }, + { 8900923260467641632u, 1944692274331606783u }, { 12480606065433357876u, 1215432671457254239u }, + { 10989071563364309441u, 1519290839321567799u }, { 9124653435777998898u, 1899113549151959749u }, + { 8008751406574943263u, 1186945968219974843u }, { 5399253239791291175u, 1483682460274968554u }, + { 15972438586593889776u, 1854603075343710692u }, { 759402079766405302u, 1159126922089819183u }, + { 14784310654990170340u, 1448908652612273978u }, { 9257016281882937117u, 1811135815765342473u }, + { 16182956370781059300u, 2263919769706678091u }, { 7808504722524468110u, 1414949856066673807u }, + { 5148944884728197234u, 1768687320083342259u }, { 1824495087482858639u, 2210859150104177824u }, + { 1140309429676786649u, 1381786968815111140u }, { 1425386787095983311u, 1727233711018888925u }, + { 6393419502297367043u, 2159042138773611156u }, { 13219259225790630210u, 1349401336733506972u }, + { 16524074032238287762u, 1686751670916883715u }, { 16043406521870471799u, 2108439588646104644u }, + { 803757039314269066u, 1317774742903815403u }, { 14839754354425000045u, 1647218428629769253u }, + { 4714634887749086344u, 2059023035787211567u }, { 9864175832484260821u, 1286889397367007229u }, + { 16941905809032713930u, 1608611746708759036u }, { 2730638187581340797u, 2010764683385948796u }, + { 10930020904093113806u, 1256727927116217997u }, { 18274212148543780162u, 1570909908895272496u }, + { 4396021111970173586u, 1963637386119090621u }, { 5053356204195052443u, 1227273366324431638u }, + { 15540067292098591362u, 1534091707905539547u }, { 14813398096695851299u, 1917614634881924434u }, + { 13870059828862294966u, 1198509146801202771u }, { 12725888767650480803u, 1498136433501503464u }, + { 15907360959563101004u, 1872670541876879330u }, { 14553786618154326031u, 1170419088673049581u }, + { 4357175217410743827u, 1463023860841311977u }, { 10058155040190817688u, 1828779826051639971u }, + { 7961007781811134206u, 2285974782564549964u }, { 14199001900486734687u, 1428734239102843727u }, + { 13137066357181030455u, 1785917798878554659u }, { 11809646928048900164u, 2232397248598193324u }, + { 16604401366885338411u, 1395248280373870827u }, { 16143815690179285109u, 1744060350467338534u }, + { 10956397575869330579u, 2180075438084173168u }, { 6847748484918331612u, 1362547148802608230u }, + { 17783057643002690323u, 1703183936003260287u }, { 17617136035325974999u, 2128979920004075359u }, + { 17928239049719816230u, 1330612450002547099u }, { 17798612793722382384u, 1663265562503183874u }, + { 13024893955298202172u, 2079081953128979843u }, { 5834715712847682405u, 1299426220705612402u }, + { 16516766677914378815u, 1624282775882015502u }, { 11422586310538197711u, 2030353469852519378u }, + { 11750802462513761473u, 1268970918657824611u }, { 10076817059714813937u, 1586213648322280764u }, + { 12596021324643517422u, 1982767060402850955u }, { 5566670318688504437u, 1239229412751781847u }, + { 2346651879933242642u, 1549036765939727309u }, { 7545000868343941206u, 1936295957424659136u }, + { 4715625542714963254u, 1210184973390411960u }, { 5894531928393704067u, 1512731216738014950u }, + { 16591536947346905892u, 1890914020922518687u }, { 17287239619732898039u, 1181821263076574179u }, + { 16997363506238734644u, 1477276578845717724u }, { 2799960309088866689u, 1846595723557147156u }, + { 10973347230035317489u, 1154122327223216972u }, { 13716684037544146861u, 1442652909029021215u }, + { 12534169028502795672u, 1803316136286276519u }, { 11056025267201106687u, 2254145170357845649u }, + { 18439230838069161439u, 1408840731473653530u }, { 13825666510731675991u, 1761050914342066913u }, + { 3447025083132431277u, 2201313642927583642u }, { 6766076695385157452u, 1375821026829739776u }, + { 8457595869231446815u, 1719776283537174720u }, { 10571994836539308519u, 2149720354421468400u }, + { 6607496772837067824u, 1343575221513417750u }, { 17482743002901110588u, 1679469026891772187u }, + { 17241742735199000331u, 2099336283614715234u }, { 15387775227926763111u, 1312085177259197021u }, + { 5399660979626290177u, 1640106471573996277u }, { 11361262242960250625u, 2050133089467495346u }, + { 11712474920277544544u, 1281333180917184591u }, { 10028907631919542777u, 1601666476146480739u }, + { 7924448521472040567u, 2002083095183100924u }, { 14176152362774801162u, 1251301934489438077u }, + { 3885132398186337741u, 1564127418111797597u }, { 9468101516160310080u, 1955159272639746996u }, + { 15140935484454969608u, 1221974545399841872u }, { 479425281859160394u, 1527468181749802341u }, + { 5210967620751338397u, 1909335227187252926u }, { 17091912818251750210u, 1193334516992033078u }, + { 12141518985959911954u, 1491668146240041348u }, { 15176898732449889943u, 1864585182800051685u }, + { 11791404716994875166u, 1165365739250032303u }, { 10127569877816206054u, 1456707174062540379u }, + { 8047776328842869663u, 1820883967578175474u }, { 836348374198811271u, 2276104959472719343u }, + { 7440246761515338900u, 1422565599670449589u }, { 13911994470321561530u, 1778206999588061986u }, + { 8166621051047176104u, 2222758749485077483u }, { 2798295147690791113u, 1389224218428173427u }, + { 17332926989895652603u, 1736530273035216783u }, { 17054472718942177850u, 2170662841294020979u }, + { 8353202440125167204u, 1356664275808763112u }, { 10441503050156459005u, 1695830344760953890u }, + { 3828506775840797949u, 2119787930951192363u }, { 86973725686804766u, 1324867456844495227u }, + { 13943775212390669669u, 1656084321055619033u }, { 3594660960206173375u, 2070105401319523792u }, + { 2246663100128858359u, 1293815875824702370u }, { 12031700912015848757u, 1617269844780877962u }, + { 5816254103165035138u, 2021587305976097453u }, { 5941001823691840913u, 1263492066235060908u }, + { 7426252279614801142u, 1579365082793826135u }, { 4671129331091113523u, 1974206353492282669u }, + { 5225298841145639904u, 1233878970932676668u }, { 6531623551432049880u, 1542348713665845835u }, + { 3552843420862674446u, 1927935892082307294u }, { 16055585193321335241u, 1204959932551442058u }, + { 10846109454796893243u, 1506199915689302573u }, { 18169322836923504458u, 1882749894611628216u }, + { 11355826773077190286u, 1176718684132267635u }, { 9583097447919099954u, 1470898355165334544u }, + { 11978871809898874942u, 1838622943956668180u }, { 14973589762373593678u, 2298278679945835225u }, + { 2440964573842414192u, 1436424174966147016u }, { 3051205717303017741u, 1795530218707683770u }, + { 13037379183483547984u, 2244412773384604712u }, { 8148361989677217490u, 1402757983365377945u }, + { 14797138505523909766u, 1753447479206722431u }, { 13884737113477499304u, 2191809349008403039u }, + { 15595489723564518921u, 1369880843130251899u }, { 14882676136028260747u, 1712351053912814874u }, + { 9379973133180550126u, 2140438817391018593u }, { 17391698254306313589u, 1337774260869386620u }, + { 3292878744173340370u, 1672217826086733276u }, { 4116098430216675462u, 2090272282608416595u }, + { 266718509671728212u, 1306420176630260372u }, { 333398137089660265u, 1633025220787825465u }, + { 5028433689789463235u, 2041281525984781831u }, { 10060300083759496378u, 1275800953740488644u }, + { 12575375104699370472u, 1594751192175610805u }, { 1884160825592049379u, 1993438990219513507u }, + { 17318501580490888525u, 1245899368887195941u }, { 7813068920331446945u, 1557374211108994927u }, + { 5154650131986920777u, 1946717763886243659u }, { 915813323278131534u, 1216698602428902287u }, + { 14979824709379828129u, 1520873253036127858u }, { 9501408849870009354u, 1901091566295159823u }, + { 12855909558809837702u, 1188182228934474889u }, { 2234828893230133415u, 1485227786168093612u }, + { 2793536116537666769u, 1856534732710117015u }, { 8663489100477123587u, 1160334207943823134u }, + { 1605989338741628675u, 1450417759929778918u }, { 11230858710281811652u, 1813022199912223647u }, + { 9426887369424876662u, 2266277749890279559u }, { 12809333633531629769u, 1416423593681424724u }, + { 16011667041914537212u, 1770529492101780905u }, { 6179525747111007803u, 2213161865127226132u }, + { 13085575628799155685u, 1383226165704516332u }, { 16356969535998944606u, 1729032707130645415u }, + { 15834525901571292854u, 2161290883913306769u }, { 2979049660840976177u, 1350806802445816731u }, + { 17558870131333383934u, 1688508503057270913u }, { 8113529608884566205u, 2110635628821588642u }, + { 9682642023980241782u, 1319147268013492901u }, { 16714988548402690132u, 1648934085016866126u }, + { 11670363648648586857u, 2061167606271082658u }, { 11905663298832754689u, 1288229753919426661u }, + { 1047021068258779650u, 1610287192399283327u }, { 15143834390605638274u, 2012858990499104158u }, + { 4853210475701136017u, 1258036869061940099u }, { 1454827076199032118u, 1572546086327425124u }, + { 1818533845248790147u, 1965682607909281405u }, { 3442426662494187794u, 1228551629943300878u }, + { 13526405364972510550u, 1535689537429126097u }, { 3072948650933474476u, 1919611921786407622u }, + { 15755650962115585259u, 1199757451116504763u }, { 15082877684217093670u, 1499696813895630954u }, + { 9630225068416591280u, 1874621017369538693u }, { 8324733676974063502u, 1171638135855961683u }, + { 5794231077790191473u, 1464547669819952104u }, { 7242788847237739342u, 1830684587274940130u }, + { 18276858095901949986u, 2288355734093675162u }, { 16034722328366106645u, 1430222333808546976u }, + { 1596658836748081690u, 1787777917260683721u }, { 6607509564362490017u, 2234722396575854651u }, + { 1823850468512862308u, 1396701497859909157u }, { 6891499104068465790u, 1745876872324886446u }, + { 17837745916940358045u, 2182346090406108057u }, { 4231062170446641922u, 1363966306503817536u }, + { 5288827713058302403u, 1704957883129771920u }, { 6611034641322878003u, 2131197353912214900u }, + { 13355268687681574560u, 1331998346195134312u }, { 16694085859601968200u, 1664997932743917890u }, + { 11644235287647684442u, 2081247415929897363u }, { 4971804045566108824u, 1300779634956185852u }, + { 6214755056957636030u, 1625974543695232315u }, { 3156757802769657134u, 2032468179619040394u }, + { 6584659645158423613u, 1270292612261900246u }, { 17454196593302805324u, 1587865765327375307u }, + { 17206059723201118751u, 1984832206659219134u }, { 6142101308573311315u, 1240520129162011959u }, + { 3065940617289251240u, 1550650161452514949u }, { 8444111790038951954u, 1938312701815643686u }, + { 665883850346957067u, 1211445438634777304u }, { 832354812933696334u, 1514306798293471630u }, + { 10263815553021896226u, 1892883497866839537u }, { 17944099766707154901u, 1183052186166774710u }, + { 13206752671529167818u, 1478815232708468388u }, { 16508440839411459773u, 1848519040885585485u }, + { 12623618533845856310u, 1155324400553490928u }, { 15779523167307320387u, 1444155500691863660u }, + { 1277659885424598868u, 1805194375864829576u }, { 1597074856780748586u, 2256492969831036970u }, + { 5609857803915355770u, 1410308106144398106u }, { 16235694291748970521u, 1762885132680497632u }, + { 1847873790976661535u, 2203606415850622041u }, { 12684136165428883219u, 1377254009906638775u }, + { 11243484188358716120u, 1721567512383298469u }, { 219297180166231438u, 2151959390479123087u }, + { 7054589765244976505u, 1344974619049451929u }, { 13429923224983608535u, 1681218273811814911u }, + { 12175718012802122765u, 2101522842264768639u }, { 14527352785642408584u, 1313451776415480399u }, + { 13547504963625622826u, 1641814720519350499u }, { 12322695186104640628u, 2052268400649188124u }, + { 16925056528170176201u, 1282667750405742577u }, { 7321262604930556539u, 1603334688007178222u }, + { 18374950293017971482u, 2004168360008972777u }, { 4566814905495150320u, 1252605225005607986u }, + { 14931890668723713708u, 1565756531257009982u }, { 9441491299049866327u, 1957195664071262478u }, + { 1289246043478778550u, 1223247290044539049u }, { 6223243572775861092u, 1529059112555673811u }, + { 3167368447542438461u, 1911323890694592264u }, { 1979605279714024038u, 1194577431684120165u }, + { 7086192618069917952u, 1493221789605150206u }, { 18081112809442173248u, 1866527237006437757u }, + { 13606538515115052232u, 1166579523129023598u }, { 7784801107039039482u, 1458224403911279498u }, + { 507629346944023544u, 1822780504889099373u }, { 5246222702107417334u, 2278475631111374216u }, + { 3278889188817135834u, 1424047269444608885u }, { 8710297504448807696u, 1780059086805761106u } +}; + +#endif // DTOA_RYU_TABLE_H diff --git a/dtoa.c b/dtoa.c index a89e824f9..5f823831b 100644 --- a/dtoa.c +++ b/dtoa.c @@ -1103,6 +1103,348 @@ static void dtoa_free(void *ptr) } #endif +/* ---------- Ryu: shortest double to decimal digits ---------- + * + * The exact bignum printer below finds the shortest representation by + * successive P-digit tries, which is the hot spot for the common free + * format conversion (`d + ""`, Number.prototype.toString). Ryu + * computes the same shortest digits in a handful of 128-bit + * operations instead. The mantissa/exponent it returns are fed into + * the same ECMAScript formatting rules as the bignum path, so the + * output is byte-identical; the bignum printer remains the fallback + * for every other radix and format. The 128-bit multiply is done via + * __int128 when available and a portable 32-bit-split umul128 otherwise + * (MSVC), so the fast path runs on all supported compilers. + * + * Ported from https://github.com/ulfjack/ryu (Copyright 2018 Ulf + * Adams, Apache-2.0 or BSL-1.0); lookup tables live in + * dtoa-ryu-table.h. */ +#if !defined(JS_DTOA_NO_FAST_PATH) + +#include "dtoa-ryu-table.h" + +#if defined(__SIZEOF_INT128__) +typedef __uint128_t ryu_uint128_t; +#endif + +static inline uint32_t ryu_decimal_length17(uint64_t v) +{ + if (v >= 10000000000000000L) return 17; + if (v >= 1000000000000000L) return 16; + if (v >= 100000000000000L) return 15; + if (v >= 10000000000000L) return 14; + if (v >= 1000000000000L) return 13; + if (v >= 100000000000L) return 12; + if (v >= 10000000000L) return 11; + if (v >= 1000000000L) return 10; + if (v >= 100000000L) return 9; + if (v >= 10000000L) return 8; + if (v >= 1000000L) return 7; + if (v >= 100000L) return 6; + if (v >= 10000L) return 5; + if (v >= 1000L) return 4; + if (v >= 100L) return 3; + if (v >= 10L) return 2; + return 1; +} + +static inline uint32_t ryu_log10_pow2(int32_t e) +{ + return ((uint32_t)e * 78913) >> 18; +} + +static inline uint32_t ryu_log10_pow5(int32_t e) +{ + return ((uint32_t)e * 732923) >> 20; +} + +static inline int32_t ryu_pow5bits(int32_t e) +{ + return (int32_t)((((uint32_t)e * 1217359) >> 19) + 1); +} + +static inline uint64_t ryu_div5(uint64_t x) +{ + return x / 5; +} + +static inline uint64_t ryu_div10(uint64_t x) +{ + return x / 10; +} + +static inline uint64_t ryu_div100(uint64_t x) +{ + return x / 100; +} + +static inline uint32_t ryu_pow5_factor(uint64_t value) +{ + const uint64_t m_inv_5 = 14757395258967641293u; /* 5 * m_inv_5 = 1 mod 2^64 */ + const uint64_t n_div_5 = 3689348814741910323u; /* 2^64 / 5 */ + uint32_t count = 0; + for (;;) { + value *= m_inv_5; + if (value > n_div_5) + break; + ++count; + } + return count; +} + +static inline bool ryu_multiple_of_power_of_5(uint64_t value, uint32_t p) +{ + return ryu_pow5_factor(value) >= p; +} + +static inline bool ryu_multiple_of_power_of_2(uint64_t value, uint32_t p) +{ + return (value & (((uint64_t)1 << p) - 1)) == 0; +} + +#if defined(__SIZEOF_INT128__) +static inline uint64_t ryu_mul_shift64(uint64_t m, const uint64_t *mul, + int32_t j) +{ + ryu_uint128_t b0 = (ryu_uint128_t)m * mul[0]; + ryu_uint128_t b2 = (ryu_uint128_t)m * mul[1]; + return (uint64_t)(((b0 >> 64) + b2) >> (j - 64)); +} +#else +/* portable 64x64 -> 128 multiply and 128-bit right shift for compilers + without __int128 (MSVC); m fits in 55 bits, j >= 115, j - 64 < 64 */ +static inline uint64_t ryu_umul128(uint64_t a, uint64_t b, uint64_t *hi) +{ + uint32_t a_lo = (uint32_t)a, a_hi = (uint32_t)(a >> 32); + uint32_t b_lo = (uint32_t)b, b_hi = (uint32_t)(b >> 32); + uint64_t b00 = (uint64_t)a_lo * b_lo; + uint64_t b01 = (uint64_t)a_lo * b_hi; + uint64_t b10 = (uint64_t)a_hi * b_lo; + uint64_t b11 = (uint64_t)a_hi * b_hi; + uint64_t mid1 = b10 + (b00 >> 32); + uint64_t mid2 = b01 + (uint32_t)mid1; + *hi = b11 + (mid1 >> 32) + (mid2 >> 32); + return ((mid2 & 0xffffffff) << 32) | (uint32_t)b00; +} + +static inline uint64_t ryu_shiftright128(uint64_t lo, uint64_t hi, + uint32_t dist) +{ + return (hi << (64 - dist)) | (lo >> dist); +} + +static inline uint64_t ryu_mul_shift64(uint64_t m, const uint64_t *mul, + int32_t j) +{ + uint64_t high1, high0, low1, sum; + low1 = ryu_umul128(m, mul[1], &high1); + ryu_umul128(m, mul[0], &high0); + sum = high0 + low1; + if (sum < high0) + ++high1; + return ryu_shiftright128(sum, high1, j - 64); +} +#endif + +static inline uint64_t ryu_mul_shift_all64(uint64_t m, const uint64_t *mul, + int32_t j, uint64_t *vp, + uint64_t *vm, uint32_t mm_shift) +{ + *vp = ryu_mul_shift64(4 * m + 2, mul, j); + *vm = ryu_mul_shift64(4 * m - 1 - mm_shift, mul, j); + return ryu_mul_shift64(4 * m, mul, j); +} + +/* shortest decimal digits for value = m2 * 2^(e2-52), m2 normalized to + 53 bits, e2 unbiased. *pexp is the exponent: value = *pmant * 10^exp. */ +static void ryu_d2d(uint64_t m2, int32_t e2, uint64_t *pmant, int32_t *pexp) +{ + int32_t e; + int32_t e10, k, j, i, removed = 0; + uint32_t q, mm_shift; + uint64_t mv, vr, vp, vm, output; + uint8_t last_removed_digit = 0; + bool accept_bounds, vm_is_trailing_zeros = false; + bool vr_is_trailing_zeros = false; + + if (e2 < -1022) { + /* Subnormals must stay in ryu's raw-mantissa form, otherwise the + pow5 table index i = -e - q can exceed DOUBLE_POW5_TABLE_SIZE */ + m2 >>= -e2 - 1022; + e = 1 - 1023 - 52 - 2; + } else { + e = e2 - 54; /* ryu uses two extra bits for the bounds */ + } + accept_bounds = (m2 & 1) == 0; + /* 1 unless the value is a normal power of two (subnormals keep an + asymmetric interval down to zero, hence e2 < -1022 also) */ + mm_shift = (m2 != ((uint64_t)1 << 52)) || (e2 < -1022) ? 1 : 0; + mv = 4 * m2; + if (e >= 0) { + q = ryu_log10_pow2(e) - (e > 3); + e10 = (int32_t)q; + k = DOUBLE_POW5_INV_BITCOUNT + ryu_pow5bits((int32_t)q) - 1; + i = -e + (int32_t)q + k; + vr = ryu_mul_shift_all64(m2, DOUBLE_POW5_INV_SPLIT[q], i, &vp, &vm, + mm_shift); + if (q <= 21) { + uint32_t mv_mod5 = (uint32_t)mv - + 5 * (uint32_t)ryu_div5(mv); + if (mv_mod5 == 0) { + vr_is_trailing_zeros = ryu_multiple_of_power_of_5(mv, q); + } else if (accept_bounds) { + vm_is_trailing_zeros = + ryu_multiple_of_power_of_5(mv - 1 - mm_shift, q); + } else { + vp -= ryu_multiple_of_power_of_5(mv + 2, q); + } + } + } else { + q = ryu_log10_pow5(-e) - (-e > 1); + e10 = (int32_t)q + e; + i = -e - (int32_t)q; + k = ryu_pow5bits(i) - DOUBLE_POW5_BITCOUNT; + j = (int32_t)q - k; + vr = ryu_mul_shift_all64(m2, DOUBLE_POW5_SPLIT[i], j, &vp, &vm, + mm_shift); + if (q <= 1) { + vr_is_trailing_zeros = true; + if (accept_bounds) { + vm_is_trailing_zeros = mm_shift == 1; + } else { + --vp; + } + } else if (q < 63) { + vr_is_trailing_zeros = ryu_multiple_of_power_of_2(mv, q); + } + } + if (vm_is_trailing_zeros || vr_is_trailing_zeros) { + /* rare case (~0.7%): the interval ends on a power of 10 boundary */ + for (;;) { + uint64_t vp_div10 = ryu_div10(vp); + uint64_t vm_div10 = ryu_div10(vm); + if (vp_div10 <= vm_div10) + break; + uint32_t vm_mod10 = (uint32_t)vm - 10 * (uint32_t)vm_div10; + uint64_t vr_div10 = ryu_div10(vr); + uint32_t vr_mod10 = (uint32_t)vr - 10 * (uint32_t)vr_div10; + vm_is_trailing_zeros &= vm_mod10 == 0; + vr_is_trailing_zeros &= last_removed_digit == 0; + last_removed_digit = (uint8_t)vr_mod10; + vr = vr_div10; + vp = vp_div10; + vm = vm_div10; + ++removed; + } + if (vm_is_trailing_zeros) { + for (;;) { + uint64_t vm_div10 = ryu_div10(vm); + uint32_t vm_mod10 = (uint32_t)vm - 10 * (uint32_t)vm_div10; + if (vm_mod10 != 0) + break; + uint64_t vp_div10 = ryu_div10(vp); + uint64_t vr_div10 = ryu_div10(vr); + uint32_t vr_mod10 = (uint32_t)vr - 10 * (uint32_t)vr_div10; + vr_is_trailing_zeros &= last_removed_digit == 0; + last_removed_digit = (uint8_t)vr_mod10; + vr = vr_div10; + vp = vp_div10; + vm = vm_div10; + ++removed; + } + } + /* round to even when the exact value ends in .....50..0 */ + if (vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0) + last_removed_digit = 4; + output = vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) + || last_removed_digit >= 5); + } else { + bool round_up = false; + uint64_t vp_div100 = ryu_div100(vp); + uint64_t vm_div100 = ryu_div100(vm); + if (vp_div100 > vm_div100) { + uint64_t vr_div100 = ryu_div100(vr); + uint32_t vr_mod100 = (uint32_t)vr - 100 * (uint32_t)vr_div100; + round_up = vr_mod100 >= 50; + vr = vr_div100; + vp = vp_div100; + vm = vm_div100; + removed += 2; + } + for (;;) { + uint64_t vp_div10 = ryu_div10(vp); + uint64_t vm_div10 = ryu_div10(vm); + if (vp_div10 <= vm_div10) + break; + uint64_t vr_div10 = ryu_div10(vr); + uint32_t vr_mod10 = (uint32_t)vr - 10 * (uint32_t)vr_div10; + round_up = vr_mod10 >= 5; + vr = vr_div10; + vp = vp_div10; + vm = vm_div10; + ++removed; + } + output = vr + (vr == vm || round_up); + } + *pmant = output; + *pexp = e10 + removed; +} + +/* format value = output * 10^exp with the ECMAScript free-format rules; + buf points at the output start (after an optional sign), q at the + current position. Returns the number of characters written. */ +static int ryu_format_free(char *buf, char *q, uint64_t output, int32_t exp) +{ + char digits[17]; + int olength = ryu_decimal_length17(output); + int e = exp + olength; /* number of integer digits */ + int i; + + for (i = olength - 1; i >= 0; i--) { + digits[i] = (char)('0' + output % 10); + output /= 10; + } + if (e <= -6 || e > 21) { + *q++ = digits[0]; + if (olength > 1) { + *q++ = '.'; + memcpy(q, digits + 1, olength - 1); + q += olength - 1; + } + *q++ = 'e'; + e--; + if (e < 0) { + *q++ = '-'; + e = -e; + } else { + *q++ = '+'; + } + q += u32toa(q, e); + } else if (e <= 0) { + *q++ = '0'; + *q++ = '.'; + for (i = 0; i < -e; i++) + *q++ = '0'; + memcpy(q, digits, olength); + q += olength; + } else if (e >= olength) { + memcpy(q, digits, olength); + q += olength; + for (i = 0; i < e - olength; i++) + *q++ = '0'; + } else { + memcpy(q, digits, e); + q += e; + *q++ = '.'; + memcpy(q, digits + e, olength - e); + q += olength - e; + } + *q = '\0'; + return q - buf; +} + +#endif /* !JS_DTOA_NO_FAST_PATH */ + /* return the length */ int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, JSDTOATempMem *tmp_mem) @@ -1176,6 +1518,22 @@ int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, } #endif +#if !defined(JS_DTOA_NO_FAST_PATH) + if (fmt == JS_DTOA_FORMAT_FREE && radix == 10 && + (flags & JS_DTOA_EXP_MASK) == JS_DTOA_EXP_AUTO) { + uint64_t output; + int32_t exp; + int len; + /* at this point value = m * 2^(e-53), m normalized to 53 bits */ + ryu_d2d(m, e - 1, &output, &exp); + len = ryu_format_free(buf, q, output, exp); + /* no-op on the arena allocator, real frees on the malloc one */ + dtoa_free(mant_max); + dtoa_free(tmp1); + return len; + } +#endif + /* this choice of E implies F=round(x*B^(P-E) is such as: B^(P-1) <= F < 2.B^P. */ E = 1 + mul_log2_radix(e - 1, radix); @@ -1350,6 +1708,294 @@ static void mpb_mul1_base(mpb_t *r, limb_t radix_base, limb_t a) } /* XXX: add fast path for small integers */ +/* ---------- fast path: plain base-10 string to double ---------- + * + * The exact bignum parser below handles every corner case but is the + * hot spot for runtime string->number coercions (Number(str), `+str`, + * `str | 0`, ...) which overwhelmingly receive short, plain decimal + * input. Parse such input with 128-bit integer arithmetic and round + * once at the end (round-half-to-even), which is bit-identical to the + * bignum path for the inputs accepted here: + * + * - at most 19 significant digits (fits in uint64_t) + * - decimal exponent in [-19, 19] (10^19 fits in uint64_t) + * - no separators, radix prefix or trailing garbage + * + * Anything else returns false and falls back to the exact parser, so + * behavior is unchanged. Disabled without 128-bit integer support + * (MSVC) and for clang targeting MSVC on Windows, which lowers 128-bit + * division and the u128->double conversion to compiler-rt libcalls + * (__udivti3, __floatuntidf) that lld does not link. MinGW and Cygwin + * link the runtime library and keep the fast path. Also disabled with + * JS_ATOD_NO_FAST_PATH for differential testing. */ +#ifdef JS_ATOD_USE_FAST_PATH + +static const uint64_t pow10_u64[20] = { + UINT64_C(1), + UINT64_C(10), + UINT64_C(100), + UINT64_C(1000), + UINT64_C(10000), + UINT64_C(100000), + UINT64_C(1000000), + UINT64_C(10000000), + UINT64_C(100000000), + UINT64_C(1000000000), + UINT64_C(10000000000), + UINT64_C(100000000000), + UINT64_C(1000000000000), + UINT64_C(10000000000000), + UINT64_C(100000000000000), + UINT64_C(1000000000000000), + UINT64_C(10000000000000000), + UINT64_C(100000000000000000), + UINT64_C(1000000000000000000), + UINT64_C(10000000000000000000), +}; + +static inline int clz128(uint64_t hi, uint64_t lo) +{ + /* caller guarantees n != 0 so at most one branch sees a zero value */ + return hi ? clz64(hi) : 64 + clz64(lo); +} + +/* round the exact integer n to nearest double, n >= 1. Used by the + multiply path where the scaled value fits in 128 bits. */ +static double round_u128_to_double(__uint128_t n) +{ + int b, shift; + uint64_t mant; + + b = 128 - clz128((uint64_t)(n >> 64), (uint64_t)n); + if (b <= 53) { + /* n < 2^53 fits in uint64_t; avoid a u128->double libcall */ + return (double)(uint64_t)n; /* exact */ + } + shift = b - 53; + mant = (uint64_t)(n >> shift); + { + __uint128_t low = n & (((__uint128_t)1 << shift) - 1); + __uint128_t half = (__uint128_t)1 << (shift - 1); + int round_up = low > half || (low == half && (mant & 1)); + if (round_up) { + mant++; + if (mant >> 53) { + /* carry to bit 53: mantissa becomes 2^52, exponent +1 */ + mant >>= 1; + b++; + } + } + } + /* value = mant * 2^(b-53) with mant in [2^52, 2^53) */ + return uint64_as_float64(((uint64_t)(b - 1 + 1023) << 52) | + (mant & (((uint64_t)1 << 52) - 1))); +} + +/* round m / d to nearest double, 0 < d < 2^62, m >= 1, b = bitlen(m). + The quotient stays in the normal double range because the caller + only feeds values in [10^-19, 10^19]; the rounding is exact because + m/d is kept as the rational q + r/d until the single final rounding. */ +static double round_m_div_d_to_double(uint64_t m, uint64_t d, int b) +{ + uint64_t n = m << (64 - b); /* top bit set */ + uint64_t q = n / d, r = n % d; + int bq, e, shift; + uint64_t mant; + + if (q == 0) { + /* m * 2^(64-b) < d, so v = m/d < 1: the 64-bit quotient is zero. + Re-do the division with a full 128-bit numerator so the + quotient has at least 64 bits and the rounding below applies + (d <= 10^19 < 2^64 makes this always well-defined). */ + __uint128_t n2 = (__uint128_t)m << (128 - b); + __uint128_t q2 = n2 / d; + uint64_t r2 = (uint64_t)(n2 % d); + int bq2 = 128 - clz128((uint64_t)(q2 >> 64), (uint64_t)q2); + int e2 = bq2 + b - 129; + int shift2 = bq2 - 53; + uint64_t mant2 = (uint64_t)(q2 >> shift2); + __uint128_t low2 = q2 & (((__uint128_t)1 << shift2) - 1); + __uint128_t half2 = (__uint128_t)1 << (shift2 - 1); + int round_up = low2 > half2 || + (low2 == half2 && (r2 != 0 || (mant2 & 1))); + if (round_up) { + mant2++; + if (mant2 >> 53) { + mant2 >>= 1; + e2++; + } + } + return uint64_as_float64(((uint64_t)(e2 + 1023) << 52) | + (mant2 & (((uint64_t)1 << 52) - 1))); + } + bq = 64 - clz64(q); + /* v = m/d = (q + r/d) * 2^(b-64) */ + e = bq + b - 65; + shift = bq - 53; + if (shift > 0) { + uint64_t low = q & (((uint64_t)1 << shift) - 1); + uint64_t half = (uint64_t)1 << (shift - 1); + int round_up; + mant = q >> shift; + round_up = low > half || + (low == half && (r != 0 || (mant & 1))); + if (round_up) { + mant++; + if (mant >> 53) { + mant >>= 1; + e++; + } + } + } else { + /* q has fewer than 53 bits: shift q left and fold the scaled + fraction r*2^-shift/d in, rounding the remainder once */ + int sh = -shift; + __uint128_t rn = (__uint128_t)r << sh; + uint64_t fi = (uint64_t)(rn / d); + uint64_t rem = (uint64_t)(rn % d); + uint64_t half_d = d / 2; /* d = 10^k is even; avoid 2*rem overflow */ + mant = (q << sh) + fi; + if (rem > half_d) + mant++; + else if (rem == half_d) + mant += (mant & 1); + /* q + fi can round up to exactly 2^53 (carry) */ + if (mant >> 53) { + mant >>= 1; + e++; + } + } + /* value = mant * 2^(e-52), mant in [2^52, 2^53) */ + return uint64_as_float64(((uint64_t)(e + 1023) << 52) | + (mant & (((uint64_t)1 << 52) - 1))); +} + +/* Shared core of the fast decimal parser: parse a plain base-10 number in + [p, end) with no sign and no surrounding spaces, at most 19 significant + digits and a decimal exponent in [-19, 19]. Return true and the mantissa + m and exponent e10 such that value = m * 10^e10 on success; false means + the caller must use the exact parser, which also sets *pnext for partial + matches. */ +static bool js_atod_fast10_parse_core(const char *p, const char *end, + bool int_only, uint64_t *pm, + int32_t *pe10) +{ + uint64_t m = 0; + int ndigits = 0, e10 = 0, exp; + bool saw_digit = false; + const char *q = p; + + /* leading zeros don't affect the value */ + while (q < end && *q == '0') { + saw_digit = true; + q++; + } + while (q < end && *q >= '0' && *q <= '9') { + saw_digit = true; + if (ndigits >= 19) { + if (*q != '0') + return false; /* 20th significant digit: not exact */ + /* trailing zeros still scale the value */ + e10++; + if (e10 > 19) + return false; + } else { + m = m * 10 + (*q - '0'); + ndigits++; + } + q++; + } + if (q < end && *q == '.') { + if (int_only) + return false; + q++; + while (q < end && *q >= '0' && *q <= '9') { + saw_digit = true; + if (ndigits >= 19) { + if (*q != '0') + return false; + } else { + m = m * 10 + (*q - '0'); + ndigits++; + e10--; + } + q++; + } + } + if (q < end && (*q == 'e' || *q == 'E')) { + const char *r = q + 1; + int eneg = 0; + if (int_only) + return false; + if (r < end && *r == '+') { + r++; + } else if (r < end && *r == '-') { + eneg = 1; + r++; + } + if (r >= end || *r < '0' || *r > '9') + return false; /* bare "1e" parses "1" in the exact parser */ + exp = 0; + while (r < end && *r >= '0' && *r <= '9') { + if (exp > 1000) + return false; /* huge exponent: exact parser clamps to 0/inf */ + exp = exp * 10 + (*r - '0'); + r++; + } + e10 += eneg ? -exp : exp; + q = r; + } + /* a number must contain at least one digit: ".", "+." and "-." are NaN */ + if (!saw_digit || q != end || e10 < -19 || e10 > 19) + return false; + *pm = m; + *pe10 = e10; + return true; +} + +/* Entry point for flat strings: optional sign and surrounding ASCII + spaces. Lets the runtime (JS_ToNumberHintFree) skip the general js_atof + scanner for the common short decimal inputs; m may be 0 (then e10 is + ignored by the round helper). */ +bool js_atod_fast10_parse(const char *p, size_t len, uint64_t *pm, + int32_t *pe10, int *pneg) +{ + const char *end = p + len, *q; + int neg = 0; + + while (p < end && *p == ' ') + p++; + if (p < end && (*p == '+' || *p == '-')) { + neg = (*p == '-'); + p++; + } + q = end; + while (q > p && q[-1] == ' ') + q--; + if (!js_atod_fast10_parse_core(p, q, 0, pm, pe10)) + return false; + *pneg = neg; + return true; +} + +/* Correctly round m * 10^e10 to double, with the sign applied; requires + 0 <= m < 10^19 and |e10| <= 19 (the parser's bounds). */ +double js_atod_fast10_round(uint64_t m, int32_t e10, int neg) +{ + double d; + + if (m == 0) { + d = 0.0; + } else if (e10 >= 0) { + d = round_u128_to_double((__uint128_t)m * pow10_u64[e10]); + } else { + int b = 64 - clz64(m); + d = round_m_div_d_to_double(m, pow10_u64[-e10], b); + } + return neg ? -d : d; +} +#endif + double js_atod(const char *str, const char **pnext, int radix, int flags, JSATODTempMem *tmp_mem) { @@ -1418,6 +2064,24 @@ double js_atod(const char *str, const char **pnext, int radix, int flags, if (radix == 0) radix = 10; +#ifdef JS_ATOD_USE_FAST_PATH + /* radix prefixes were consumed above, so p points at the digits */ + if (radix == 10) { + double d; + const char *end = p + strlen(p); + uint64_t m; + int32_t e10; + if (js_atod_fast10_parse_core(p, end, + (flags & JS_ATOD_INT_ONLY) != 0, + &m, &e10)) { + d = js_atod_fast10_round(m, e10, is_neg); + if (pnext) + *pnext = end; + return d; + } + } +#endif + cur_limb = 0; expn_offset = 0; digit_count = 0; diff --git a/dtoa.h b/dtoa.h index 85be04069..0b9fde149 100644 --- a/dtoa.h +++ b/dtoa.h @@ -24,6 +24,9 @@ #ifndef DTOA_H #define DTOA_H +#include +#include + //#define JS_DTOA_DUMP_STATS /* maximum number of digits for fixed and frac formats */ @@ -71,6 +74,20 @@ int js_dtoa(char *buf, double d, int radix, int n_digits, int flags, JSDTOATempMem *tmp_mem); double js_atod(const char *str, const char **pnext, int radix, int flags, JSATODTempMem *tmp_mem); +/* string->number fast path for plain base-10 input. Enabled when a 128-bit + integer type is available and not explicitly disabled. MSVC and clang + targeting MSVC on Windows are excluded because 128-bit division and the + u128->double conversion lower to compiler-rt libcalls that lld does not + link (MinGW/Cygwin link the runtime and keep the fast path). */ +#if !defined(JS_ATOD_NO_FAST_PATH) && defined(__SIZEOF_INT128__) && \ + !(defined(_WIN32) && defined(__clang__) && !defined(__MINGW32__)) +#define JS_ATOD_USE_FAST_PATH 1 +#endif +#ifdef JS_ATOD_USE_FAST_PATH +bool js_atod_fast10_parse(const char *str, size_t len, uint64_t *pm, + int32_t *pe10, int *pneg); +double js_atod_fast10_round(uint64_t m, int32_t e10, int neg); +#endif #ifdef JS_DTOA_DUMP_STATS void js_dtoa_dump_stats(void); diff --git a/quickjs.c b/quickjs.c index bcbbf912f..1ff1a4819 100644 --- a/quickjs.c +++ b/quickjs.c @@ -13975,6 +13975,35 @@ static JSValue JS_ToNumberHintFree(JSContext *ctx, JSValue val, const char *p; size_t len; + /* plain decimal strings: parse once with the fast dtoa path + and skip the general scanner (see js_atod_fast10_parse). + Not for clang targeting MSVC on Windows: it needs + compiler-rt libcalls for 128-bit division that lld does + not link (MinGW/Cygwin link the runtime and stay on). */ +#ifdef JS_ATOD_USE_FAST_PATH + if (JS_VALUE_GET_NORM_TAG(val) == JS_TAG_STRING) { + JSString *sp = JS_VALUE_GET_STRING(val); + uint64_t m; + int32_t e10; + int neg; + if (!sp->is_wide_char) { + if (js_atod_fast10_parse((const char *)str8(sp), sp->len, + &m, &e10, &neg)) { + JS_FreeValue(ctx, val); + if (m == 0) { + /* keep -0 per StringToNumber */ + return neg ? js_float64(-0.0) : js_int32(0); + } else if (e10 == 0 && + (neg ? m <= 2147483648 : m <= 2147483647)) { + /* skip the double round-trip for plain integers */ + return js_int32(neg ? (int32_t)(0 - (uint32_t)m) + : (int32_t)m); + } + return js_number(js_atod_fast10_round(m, e10, neg)); + } + } + } +#endif str = JS_ToCStringLen(ctx, &len, val); JS_FreeValue(ctx, val); if (!str) diff --git a/ryu_gen.c b/ryu_gen.c new file mode 100644 index 000000000..c821f61ac --- /dev/null +++ b/ryu_gen.c @@ -0,0 +1,356 @@ +/* + * Generation of Ryu double->shortest-decimal lookup tables + * + * Copyright 2018 Ulf Adams + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Regenerates dtoa-ryu-table.h. Ported from PrintDoubleLookupTable in the + * ryu project (https://github.com/ulfjack/ryu). + * + * usage: ryu_gen [output_file] + * + * If no output_file is given, the table is written to stdout. + */ +#include +#include +#include +#include +#include + +#define POS_TABLE_SIZE 326 +#define NEG_TABLE_SIZE 342 +#define POW5_BITCOUNT 125 +#define POW5_INV_BITCOUNT 125 + +/* Minimal big integer, little-endian, enough for 5^325 (755 bits) and + 2^917 (918 bits). */ +#define BIG_WORDS 16 +typedef struct { + uint64_t w[BIG_WORDS]; +} Big; + +static void big_zero(Big *a) +{ + memset(a, 0, sizeof(*a)); +} + +/* a = 2^e */ +static void big_set_pow2(Big *a, int e) +{ + big_zero(a); + a->w[e >> 6] = (uint64_t)1 << (e & 63); +} + +/* 64x64 -> 128 multiply, portable (no __int128, MSVC-safe) */ +static void mul64(uint64_t a, uint64_t b, uint64_t *lo, uint64_t *hi) +{ + uint32_t a0 = (uint32_t)a; + uint32_t a1 = (uint32_t)(a >> 32); + uint32_t b0 = (uint32_t)b; + uint32_t b1 = (uint32_t)(b >> 32); + uint64_t p00 = (uint64_t)a0 * b0; + uint64_t p01 = (uint64_t)a0 * b1; + uint64_t p10 = (uint64_t)a1 * b0; + uint64_t p11 = (uint64_t)a1 * b1; + uint64_t mid = p01 + p10; + uint64_t mid_carry = (mid < p01); + uint64_t sum = p00 + (mid << 32); + uint64_t carry = (sum < p00); + + *lo = sum; + *hi = p11 + (mid >> 32) + (mid_carry << 32) + carry; +} + +/* a <<= s, 0 <= s < 64 */ +static void big_shl_small(Big *a, int s) +{ + uint64_t carry = 0; + int i; + + if (s == 0) + return; + for (i = 0; i < BIG_WORDS; i++) { + uint64_t v = a->w[i]; + a->w[i] = (v << s) | carry; + carry = v >> (64 - s); + } +} + +/* a <<= 1 */ +static void big_shl1(Big *a) +{ + big_shl_small(a, 1); +} + +/* a *= m, m is a small positive integer (5 here) */ +static void big_mul_small(Big *a, uint32_t m) +{ + uint64_t carry = 0; + int i; + + for (i = 0; i < BIG_WORDS; i++) { + uint64_t lo, hi, sum; + mul64(a->w[i], m, &lo, &hi); + sum = lo + carry; + a->w[i] = sum; + carry = hi + (sum < lo); + } +} + +/* return -1, 0 or 1 */ +static int big_cmp(const Big *a, const Big *b) +{ + int i; + + for (i = BIG_WORDS - 1; i >= 0; i--) { + if (a->w[i] < b->w[i]) + return -1; + if (a->w[i] > b->w[i]) + return 1; + } + return 0; +} + +/* a -= b, assumes a >= b */ +static void big_sub(Big *a, const Big *b) +{ + uint64_t borrow = 0; + int i; + + for (i = 0; i < BIG_WORDS; i++) { + uint64_t diff = a->w[i] - b->w[i]; + uint64_t b1 = (a->w[i] < b->w[i]); + uint64_t diff2 = diff - borrow; + uint64_t b2 = (diff < borrow); + a->w[i] = diff2; + borrow = b1 | b2; + } +} + +/* a >>= s */ +static void big_shr(Big *a, int s) +{ + int word = s >> 6; + int bit = s & 63; + int i; + + for (i = 0; i < BIG_WORDS; i++) { + uint64_t v = 0; + if (i + word < BIG_WORDS) { + v = a->w[i + word] >> bit; + if (bit && i + word + 1 < BIG_WORDS) + v |= a->w[i + word + 1] << (64 - bit); + } + a->w[i] = v; + } +} + +/* a <<= s */ +static void big_shl(Big *a, int s) +{ + int word = s >> 6; + int bit = s & 63; + int i; + + for (i = BIG_WORDS - 1; i >= 0; i--) { + uint64_t v = 0; + if (i - word >= 0) { + v = a->w[i - word] << bit; + if (bit && i - word - 1 >= 0) + v |= a->w[i - word - 1] >> (64 - bit); + } + a->w[i] = v; + } +} + +/* number of significant bits, 0 for zero */ +static int big_bitlen(const Big *a) +{ + int i; + + for (i = BIG_WORDS - 1; i >= 0; i--) { + if (a->w[i]) { + uint64_t v = a->w[i]; + int bits = 0; + while (v) { + bits++; + v >>= 1; + } + return i * 64 + bits; + } + } + return 0; +} + +/* a += 1 */ +static void big_incr(Big *a) +{ + int i; + + for (i = 0; i < BIG_WORDS; i++) { + a->w[i]++; + if (a->w[i] != 0) + break; + } +} + +/* q = a / b, b != 0, remainder discarded */ +static void big_div(Big *q, const Big *a, const Big *b) +{ + Big r, qq; + int n, i; + + big_zero(&r); + big_zero(&qq); + n = big_bitlen(a); + for (i = n - 1; i >= 0; i--) { + big_shl1(&r); + if ((a->w[i >> 6] >> (i & 63)) & 1) + r.w[0] |= 1; + if (big_cmp(&r, b) >= 0) { + big_sub(&r, b); + qq.w[i >> 6] |= (uint64_t)1 << (i & 63); + } + } + *q = qq; +} + +/* ceil(log_2(5^e)), same as ryu's pow5bits */ +static int pow5bits(int e) +{ + return (int)(((uint32_t)e * 1217359) >> 19) + 1; +} + +/* invMultiplier(i) = [2^j / 5^i] + 1, j = pow5bits(i) - 1 + POW5_INV_BITCOUNT */ +static void inv_multiplier(int i, Big *out) +{ + Big pow, a; + int k, pow5len, j; + + big_zero(&pow); + pow.w[0] = 1; + for (k = 0; k < i; k++) + big_mul_small(&pow, 5); + pow5len = big_bitlen(&pow); + j = pow5len - 1 + POW5_INV_BITCOUNT; + big_set_pow2(&a, j); + big_div(out, &a, &pow); + big_incr(out); +} + +/* multiplier(i) = [5^i / 2^(ceil(log_2(5^i)) - POW5_BITCOUNT)], shifts left + when 5^i is shorter than POW5_BITCOUNT bits (small i) */ +static void multiplier(int i, Big *out) +{ + Big pow; + int k, pow5len, shift; + + big_zero(&pow); + pow.w[0] = 1; + for (k = 0; k < i; k++) + big_mul_small(&pow, 5); + pow5len = big_bitlen(&pow); + shift = pow5len - POW5_BITCOUNT; + if (shift >= 0) + big_shr(&pow, shift); + else + big_shl(&pow, -shift); + *out = pow; +} + +static void print_table(FILE *f, const char *name, const char *size_macro, + const Big *table, int len) +{ + int i; + + fprintf(f, "static const uint64_t %s[%s][2] = {\n", name, size_macro); + for (i = 0; i < len; i++) { + char low[32], high[32]; + snprintf(low, sizeof(low), "%" PRIu64, table[i].w[0]); + snprintf(high, sizeof(high), "%" PRIu64, table[i].w[1]); + fprintf(f, i % 2 == 0 ? " " : " "); + fprintf(f, "{ %20su, %18su }", low, high); + if (i != len - 1) + fprintf(f, ","); + if (i % 2 == 1) + fprintf(f, "\n"); + } + fprintf(f, "};\n"); +} + +int main(int argc, char **argv) +{ + const char *outfilename; + FILE *f; + Big large_inv_table[NEG_TABLE_SIZE]; + Big large_table[POS_TABLE_SIZE]; + int i; + + if (argc > 2) { + fprintf(stderr, "usage: %s [output_file]\n", argv[0]); + exit(1); + } + outfilename = (argc == 2) ? argv[1] : NULL; + if (outfilename) { + f = fopen(outfilename, "wb"); + if (!f) { + perror(outfilename); + exit(1); + } + } else { + f = stdout; + } + + for (i = 0; i < NEG_TABLE_SIZE; i++) + inv_multiplier(i, &large_inv_table[i]); + for (i = 0; i < POS_TABLE_SIZE; i++) + multiplier(i, &large_table[i]); + + fprintf(f, + "/* Ryu double->shortest-decimal lookup tables.\n" + " *\n" + " * Automatically generated by ryu_gen.c; see dtoa.c for the algorithm.\n" + " *\n" + " * Copyright 2018 Ulf Adams\n" + " *\n" + " * Licensed under the Apache License, Version 2.0 (Apache-2.0) or, at your\n" + " * option, the Boost Software License, Version 1.0 (BSL-1.0).\n" + " */\n" + "#ifndef DTOA_RYU_TABLE_H\n" + "#define DTOA_RYU_TABLE_H\n" + "\n" + "#include \n" + "\n" + "// These tables are generated by ryu_gen.c.\n" + "#define DOUBLE_POW5_INV_BITCOUNT %d\n" + "#define DOUBLE_POW5_BITCOUNT %d\n" + "\n" + "#define DOUBLE_POW5_INV_TABLE_SIZE %d\n" + "#define DOUBLE_POW5_TABLE_SIZE %d\n" + "\n", + POW5_INV_BITCOUNT, POW5_BITCOUNT, + NEG_TABLE_SIZE, POS_TABLE_SIZE); + + print_table(f, "DOUBLE_POW5_INV_SPLIT", "DOUBLE_POW5_INV_TABLE_SIZE", + large_inv_table, NEG_TABLE_SIZE); + fprintf(f, "\n\n"); + print_table(f, "DOUBLE_POW5_SPLIT", "DOUBLE_POW5_TABLE_SIZE", + large_table, POS_TABLE_SIZE); + + fprintf(f, "\n#endif // DTOA_RYU_TABLE_H\n"); + + if (outfilename) + fclose(f); + return 0; +} diff --git a/tests/atod-fast-path.js b/tests/atod-fast-path.js new file mode 100644 index 000000000..d0e5be2a8 --- /dev/null +++ b/tests/atod-fast-path.js @@ -0,0 +1,65 @@ +// Bit-exact regression test for the fast decimal string->double parser in +// dtoa.c. The fast path must produce the same correctly rounded result as +// the exact bignum parser; the expected bit patterns below were generated +// with the bignum-only build (JS_ATOD_NO_FAST_PATH) and cross-checked by a +// differential fuzz run over half a million inputs. +import { assert } from "./assert.js"; + +function bits(d) { + const buf = new ArrayBuffer(8); + const dv = new DataView(buf); + dv.setFloat64(0, d); + return dv.getBigUint64(0); +} + +const cases = [ + ["0.1", "3fb999999999999a"], + ["0.2", "3fc999999999999a"], + ["0.3", "3fd3333333333333"], + ["0.5", "3fe0000000000000"], + ["-0.5", "bfe0000000000000"], + ["-0", "8000000000000000"], + // 20 significant digits: fractional rounding through the 64-bit path + ["0.3333333455917782943", "3fd55555627ee8b6"], + ["782277.99999006", "4127df8bfffeb278"], + // small quotient with a big remainder (u64 overflow bug) + ["440e-19", "3c895d4100811d0c"], + ["7.07395e-16", "3cc97c9138154903"], + ["7772752141e-19", "3e0ab4fc2eaab957"], + // rounding carries the mantissa to 2^53 + ["5629499534213119999e-4", "4300000000000000"], + ["1e-19", "3bfd83c94fb6d2ac"], + ["0.0000000000000000001", "3bfd83c94fb6d2ac"], + ["12345.6", "40c81ccccccccccd"], + // 19 significant digits: 64-bit mantissa rounding + ["9999999999999999999", "43e158e460913d00"], + // trailing zeros scale the value even beyond 19 digits + ["10000000000000000000", "43e158e460913d00"], + ["100000000000000000000", "4415af1d78b58c40"], + // >19 significant digits: falls back to the bignum parser + ["9999999999999999999.5", "43e158e460913d00"], + ["18446744073709551615", "43f0000000000000"], + ["123456789012345678901", "441ac53a7e04bcda"], + ["9007199254740993", "4340000000000000"], + // boundaries: subnormal / max / overflow / underflow + ["2.2250738585072014e-308", "0010000000000000"], + ["1.7976931348623157e308", "7fefffffffffffff"], + ["1e309", "7ff0000000000000"], + ["5e-324", "0000000000000001"], + ["1e-323", "0000000000000002"], + ["1e-400", "0000000000000000"], + // radix prefixes and legacy octal keep their existing semantics + ["0x1A", "403a000000000000"], + ["0o10", "4020000000000000"], + ["0b10", "4000000000000000"], + ["077", "4053400000000000"], +]; + +for (const [str, hex] of cases) { + assert(bits(Number(str)), BigInt("0x" + hex), str); +} + +// ToInt32 (`| 0`) goes through the same conversion +assert(("12345" | 0) === 12345); +assert(("4294967296" | 0) === 0); +assert(("9007199254740993" | 0) === 0); diff --git a/tests/dtoa-shortest.js b/tests/dtoa-shortest.js new file mode 100644 index 000000000..252ab5f41 --- /dev/null +++ b/tests/dtoa-shortest.js @@ -0,0 +1,45 @@ +// Shortest round-trip representation tests for the Ryu free-format dtoa +// path in dtoa.c. Expected strings are verified against V8 and the Ryu +// reference implementation. The denormal cases below are inputs where the +// previous bignum printer emitted a non-shortest 17-digit form; the fast +// path must emit the shortest form. +import { assert } from "./assert.js"; + +function u2d(u) { + const buf = new ArrayBuffer(8); + const dv = new DataView(buf); + dv.setBigUint64(0, u); + return dv.getFloat64(0); +} + +const cases = [ + [0.1, "0.1"], + [0.2, "0.2"], + [0.3, "0.3"], + [1.5, "1.5"], + [-0.5, "-0.5"], + [1e21, "1e+21"], + [1e-7, "1e-7"], + [0.000001, "0.000001"], + [1e30, "1e+30"], + [1e-30, "1e-30"], + [Math.PI, "3.141592653589793"], + [1.7976931348623157e308, "1.7976931348623157e+308"], + [2.2250738585072014e-308, "2.2250738585072014e-308"], + [Number.MIN_VALUE, "5e-324"], + [1.0000000000000002, "1.0000000000000002"], + [9007199254740992, "9007199254740992"], + // bit-exact denormals where the shortest form has 16 digits + [u2d(0x0060000000000000n), "7.120236347223045e-307"], + [u2d(0x0031f57e09648c83n), "9.99e-308"], + [u2d(0x0031fa182c40c60en), "1.0000000000000001e-307"], + [u2d(0x0031feb24f1cff99n), "1.0010000000000002e-307"], + [u2d(0x00666d1ce02a67b5n), "9.979999999999999e-307"], + [u2d(0x006672dd8bbdafa3n), "9.99e-307"], + [u2d(0x0066789e3750f790n), "9.999999999999999e-307"], +]; + +for (const [d, expected] of cases) { + assert(String(d), expected); + assert(d.toString(), expected); +}