diff --git a/changelog.md b/changelog.md index 98a7a8c446c1..7a5777a72a18 100644 --- a/changelog.md +++ b/changelog.md @@ -70,6 +70,10 @@ - `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings. Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period. +- `system.addFloat` now uses dragonbox algorithm, which ensures roundtrip guarantee, minimum length, + and correct rounding. Use `-d:nimLegacyAddFloat` for a transition period. + +- `system/formatfloat` (an internal module) was deprecated; use the new module `std/strfloats` instead. ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/compiler/ast.nim b/compiler/ast.nim index 96adb8c1f0ee..5a3660c44e2b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -695,7 +695,7 @@ type mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2, mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, - mSymIsInstantiationOf, mNodeId, mPrivateAccess + mSymIsInstantiationOf, mNodeId, mPrivateAccess, mAddDependency # things that we can evaluate safely at compile time, even if not asked for it: diff --git a/compiler/builddeps.nim b/compiler/builddeps.nim new file mode 100644 index 000000000000..25bc1275a041 --- /dev/null +++ b/compiler/builddeps.nim @@ -0,0 +1,69 @@ +#[ +## TODO +* this will show `CC: dragonbox`: +`nim r -f --hint:cc --filenames:abs --processing:filenames --hint:conf nonexistant` +we could refine the logic to delay compilation until cgen phase instead. +(see also `tests/osproc/treadlines.nim`) +* allow some form of reporting so that caller can tell whether a dependency + doesn't exist, or was already built, or builds with error, or builds successfully, etc. +* allow some way to expose this to user code +]# + +import std/[osproc, os, strutils] +import msgs, options, ast, lineinfos, extccomp, pathutils + +const prefix = "__" # prevent clashing with user files + +type Job = object + input: string + copts: string + +proc addDependencyImpl(conf: ConfigRef, name: string, info: TLineInfo, jobs: seq[Job], linkerFlags = "") = + if name in conf.dependencies: return + conf.dependencies.add name + # xxx we could also build this under $nimb/build/ + let dir = conf.getNimcacheDir().string + createDir dir + for job in jobs: + let objFile = dir / ("$1_$2_$3.o" % [prefix, name, job.input.splitFile.name]) + if optForceFullMake in conf.globalOptions or not objFile.fileExists: + # xxx use `getCompilerExe` + when defined(osx): + let cppExe = "clang++" + else: + let cppExe = "g++" + when defined(linux): + # xxx: avoids linker errors: + # relocation R_X86_64_32S against `.rodata' can not be used when making a PIE object; recompile with -fPIE + let options = "-fPIE" + else: + let options = "" + let inputFile = conf.libpath.string / job.input + let cmd = "$# -c $# $# -O3 -o $# $#" % [cppExe.quoteShell, job.copts, options, objFile.quoteShell, inputFile.quoteShell] + # xxx use md5 hash to recompile if needed + writePrettyCmdsStderr displayProgressCC(conf, inputFile, cmd) + let (outp, status) = execCmdEx(cmd) + if status != 0: + localError(conf, info, "building '$#' failed: cmd: $#\noutput:\n$#" % [name, cmd, outp]) + conf.addExternalFileToLink(objFile.AbsoluteFile) + if linkerFlags.len > 0: + conf.addLinkOption(linkerFlags) + +proc addDependency*(conf: ConfigRef, name: string, info: TLineInfo) = + case name + of "drachennest_dragonbox": + let copts = "-std=c++11" + var jobs: seq[Job] + for file in "impl.cc dragonbox.cc schubfach_32.cc schubfach_64.cc".split: + jobs.add Job(input: "vendor/drachennest" / file, copts: copts) + addDependencyImpl(conf, name, info, jobs = jobs) + of "dragonbox_dragonbox": + let dir = "vendor/dragonbox" + let includeDir = conf.libpath.string / dir / "include" + let copts = "-std=c++17 -I $#" % includeDir.quoteShell + var jobs: seq[Job] + for file in "impl.cc dragonbox_to_chars.cpp".split: + jobs.add Job(input: dir / file, copts: copts) + addDependencyImpl(conf, name, info, jobs = jobs, linkerFlags = "-lc++") + else: + localError(conf, info, "expected: 'dragonbox', got: '$1'" % name) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 6a49584c86f3..a537b3813538 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -134,3 +134,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasIterable") defineSymbol("nimHasTypeofVoid") + defineSymbol("nimHasDragonbox") diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 1cea9edebe64..368000a38b5b 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -281,7 +281,7 @@ const hExt* = ".h" -template writePrettyCmdsStderr(cmd) = +template writePrettyCmdsStderr*(cmd) = if cmd.len > 0: flushDot(conf) stderr.writeLine(cmd) @@ -835,7 +835,7 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu else: platform.OS[conf.target.targetOS].dllFrmt % basename result = conf.getNimcacheDir / RelativeFile(targetName) -proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = +proc displayProgressCC*(conf: ConfigRef, path, compileCmd: string): string = if conf.hasHint(hintCC): if optListCmd in conf.globalOptions or conf.verbosity > 1: result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd) diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim index 61aa0e697f6b..0e7db9463f41 100644 --- a/compiler/ic/replayer.nim +++ b/compiler/ic/replayer.nim @@ -12,7 +12,7 @@ ## support. import ".." / [ast, modulegraphs, trees, extccomp, btrees, - msgs, lineinfos, pathutils, options, cgmeth] + msgs, lineinfos, pathutils, options, cgmeth, builddeps] import tables @@ -32,6 +32,7 @@ proc replayStateChanges*(module: PSym; g: ModuleGraph) = of "hint": message(g.config, n.info, hintUser, n[1].strVal) of "warning": message(g.config, n.info, warnUser, n[1].strVal) of "error": localError(g.config, n.info, errUser, n[1].strVal) + of "addDependency": addDependency(g.config, n[1].strVal, n.info) of "compile": internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit let cname = AbsoluteFile n[1].strVal diff --git a/compiler/options.nim b/compiler/options.nim index 9cbb747c9799..d8f8d8b17d61 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -359,6 +359,7 @@ type externalToLink*: seq[string] # files to link in addition to the file # we compiled (*) + dependencies*: seq[string] # dependencies linkOptionsCmd*: string compileOptionsCmd*: seq[string] linkOptions*: string # (*) @@ -398,7 +399,7 @@ proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) = proc hasHint*(conf: ConfigRef, note: TNoteKind): bool = # ternary states instead of binary states would simplify logic if optHints notin conf.options: false - elif note in {hintConf, hintProcessing}: + elif note in {hintConf, hintProcessing, hintCC}: # could add here other special notes like hintSource # these notes apply globally. note in conf.mainPackageNotes diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3a2d9cede743..e81e7e0d6f7f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -96,12 +96,6 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; isStatement: bool = false) -proc recordPragma(c: PContext; n: PNode; args: varargs[string]) = - var recorded = newNodeI(nkReplayAction, n.info) - for i in 0..args.high: - recorded.add newStrNode(args[i], n.info) - addPragmaComputation(c, recorded) - const errStringLiteralExpected = "string literal expected" errIntLiteralExpected = "integer literal expected" diff --git a/compiler/semdata.nim b/compiler/semdata.nim index f763a00e4d40..b4a419a735d7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -332,6 +332,13 @@ proc addPragmaComputation*(c: PContext; n: PNode) = if c.config.symbolFiles != disabledSf: addPragmaComputation(c.encoder, c.packedRepr, n) +proc recordPragma*(c: PContext; n: PNode; args: varargs[string]) = + # xxx rename to `recordAction`, since it can record things other than pragmas + var recorded = newNodeI(nkReplayAction, n.info) + for i in 0..args.high: + recorded.add newStrNode(args[i], n.info) + addPragmaComputation(c, recorded) + proc inclSym(sq: var seq[PSym], s: PSym): bool = for i in 0..", importc: "sprintf", varargs, noSideEffect.} + let n: int = c_sprintf(addr buf, "%.16g", value) + var hasDot = false + for i in 0.. 12.0 + buf[n] = '.' + buf[n+1] = '0' + result = n + 2 + else: + result = n + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: + addCstring(buf, "nan") + result = 3 + elif buf[n-1] == 'F': + if buf[0] == '-': + addCstring(buf, "-inf") + result = 4 + else: + addCstring(buf, "inf") + result = 3 + +when useDragonbox: + template fixup(buf, ret, first, result) = + result = cast[int](ret) - cast[int](first) + if buf[result-1] in {'f', 'n'}: # inf, -inf, nan + return result + for i in 0.. 12.0 + if buf[i] in {'.', 'e'}: # if needed, we could make 1e2 print as 1.0e2 + return result + buf[result] = '.' + buf[result+1] = '0' + result += 2 + + import private/deputils + + addDependency("dragonbox_dragonbox") + proc nimtoStringDragonbox0Impl(buf: ptr char, value: cdouble): ptr char {.importc: "nimtoStringDragonbox0ImplDouble".} + proc nimtoStringDragonbox0Impl(buf: ptr char, value: cfloat): ptr char {.importc: "nimtoStringDragonbox0ImplFloat".} + + addDependency("drachennest_dragonbox") + proc toStringDragonboxImpl(buf: ptr char, value: cdouble): ptr char {.importc: "nimtoStringDragonboxImplDouble".} + proc toStringDragonboxImpl(buf: ptr char, value: cfloat): ptr char {.importc: "nimSchubfachFtoa".} # note: using schubfach here because drachennest dragonbox doesn't yet support float32 + + proc nimSchubfachToString(buf: ptr char, value: cfloat): ptr char {.importc: "nimSchubfachFtoa".} + proc nimSchubfachToString(buf: ptr char, value: cdouble): ptr char {.importc: "nimSchubfachDtoa".} + + proc toStringDragonbox0*(buf: var array[strFloatBufLen, char]; value: float|float32): int {.inline.} = + let first = buf[0].addr + let ret = nimtoStringDragonbox0Impl(first, value) + fixup(buf, ret, first, result) + + proc toStringSchubfach*(buf: var array[strFloatBufLen, char]; value: float|float32): int {.inline.} = + let first = buf[0].addr + let ret = nimSchubfachToString(first, value) + fixup(buf, ret, first, result) + + proc toStringDragonbox*(buf: var array[strFloatBufLen, char]; value: float|float32): int {.inline.} = + let first = buf[0].addr + let ret = toStringDragonboxImpl(first, value) + fixup(buf, ret, first, result) + + template toString*(buf: var array[strFloatBufLen, char]; value: float|float32): int = + toStringDragonbox(buf, value) + # toStringDragonbox0(buf, value) +else: + template toString*(buf: var array[strFloatBufLen, char]; value: BiggestFloat): int = + toStringSprintf(buf, value) + +proc addFloat*(result: var string; x: float | float32) = + ## Converts `x` to its string representation and appends it to `result`. + ## + ## The algorithm is implementation defined, but currently uses dragonbox algorithm, + ## which ensures roundtrip guarantee, minimum length, and correct rounding. + runnableExamples: + var a = "prefix:" + a.addFloat(0.1) + assert a == "prefix:0.1" + + a.setLen 0 + var b = 0.1 + var c = b + 0.2 + a.addFloat(c) + assert a == "0.30000000000000004" + assert c != 0.3 # indeed, binary representation is not exact + when nimvm: # also a vmops, after bootstrap + result.add $x + else: + var buf {.noinit.}: array[strFloatBufLen, char] + let n = toString(buf, x) + result.addCharsN(buf[0].addr, n) diff --git a/lib/system.nim b/lib/system.nim index be30665ddff9..942d29fe872f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -112,7 +112,7 @@ proc compileOption*(option, arg: string): bool {. discard "compiled with optimization for size and uses Boehm's GC" {.push warning[GcMem]: off, warning[Uninit]: off.} -{.push hints: off.} +# {.push hints: off.} proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `or` meta class. @@ -2471,7 +2471,7 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = quit(errorcode) {.pop.} # checks: off -{.pop.} # hints: off +# {.pop.} # hints: off proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## Division of integers that results in a float. diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index cb46c8c36139..09d6824c43e1 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -1,59 +1,12 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2019 Nim contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# +{.deprecated: "use std/strfloats".} -proc c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs, noSideEffect.} +import std/strfloats -proc writeToBuffer(buf: var array[65, char]; value: cstring) = - var i = 0 - while value[i] != '\0': - buf[i] = value[i] - inc i +const N = 65 +static: + doAssert N > strFloatBufLen -proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats in the Nim - ## programming language. The specific format for floating point - ## numbers is not specified in the Nim programming language and - ## might change slightly in the future, but at least wherever you - ## format a float, it should be consistent. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - ## - ## * `buf` - A buffer to write into. The buffer does not need to be - ## initialized and it will be overridden. - ## - var n: int = c_sprintf(addr buf, "%.16g", value) - var hasDot = false - for i in 0..n-1: - if buf[i] == ',': - buf[i] = '.' - hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: - hasDot = true - if not hasDot: - buf[n] = '.' - buf[n+1] = '0' - buf[n+2] = '\0' - result = n + 2 - else: - result = n - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: - writeToBuffer(buf, "nan") - result = 3 - elif buf[n-1] == 'F': - if buf[0] == '-': - writeToBuffer(buf, "-inf") - result = 4 - else: - writeToBuffer(buf, "inf") - result = 3 +proc writeFloatToBuffer*(buf: var array[N, char]; value: BiggestFloat): int {.deprecated: "use strfloats.toString".} = + let buf2 = cast[ptr array[strFloatBufLen, char]](buf.addr) + result = toString(buf2[], value) + buf[result] = '\0' diff --git a/lib/system/io.nim b/lib/system/io.nim index 594c78209cff..2b9021c21c95 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -12,7 +12,7 @@ include inclrtl import std/private/since -import formatfloat +import std/strfloats # ----------------- IO Part ------------------------------------------------ type @@ -506,15 +506,16 @@ proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} = if b: write(f, "true") else: write(f, "false") +template writeFloatImpl(f: File, r: float32 | BiggestFloat) = + var buffer {.noinit.}: array[strFloatBufLen, char] + let n = toString(buffer, r) + if c_fprintf(f, "%.*s", n, buffer[0].addr) < 0: checkErr(f) + proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} = - var buffer {.noinit.}: array[65, char] - discard writeFloatToBuffer(buffer, r) - if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f) + writeFloatImpl(f, r) proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} = - var buffer {.noinit.}: array[65, char] - discard writeFloatToBuffer(buffer, r) - if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f) + writeFloatImpl(f, r) proc write*(f: File, c: char) {.tags: [WriteIOEffect], benign.} = discard c_putc(cint(c), f) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 42ea9d22609c..5f82a36393f7 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -136,29 +136,8 @@ proc nimIntToStr(x: int): string {.compilerRtl.} = result = newStringOfCap(sizeof(x)*4) result.addInt x -proc addCstringN(result: var string, buf: cstring; buflen: int) = - # no nimvm support needed, so it doesn't need to be fast here either - let oldLen = result.len - let newLen = oldLen + buflen - result.setLen newLen - copyMem(result[oldLen].addr, buf, buflen) - -import formatfloat - -proc addFloat*(result: var string; x: float) = - ## Converts float to its string representation and appends it to `result`. - ## - ## .. code-block:: Nim - ## var - ## a = "123" - ## b = 45.67 - ## a.addFloat(b) # a <- "12345.67" - when nimvm: - result.add $x - else: - var buffer {.noinit.}: array[65, char] - let n = writeFloatToBuffer(buffer, x) - result.addCstringN(cstring(buffer[0].addr), n) +import std/strfloats +export addFloat proc nimFloatToStr(f: float): string {.compilerproc.} = result = newStringOfCap(8) diff --git a/lib/vendor/drachennest/LICENSE.txt b/lib/vendor/drachennest/LICENSE.txt new file mode 100644 index 000000000000..36b7cd93cdfb --- /dev/null +++ b/lib/vendor/drachennest/LICENSE.txt @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/lib/vendor/drachennest/dragonbox.cc b/lib/vendor/drachennest/dragonbox.cc new file mode 100644 index 000000000000..14509fb301d0 --- /dev/null +++ b/lib/vendor/drachennest/dragonbox.cc @@ -0,0 +1,1526 @@ +// Copyright 2020 Junekey Jeon +// Copyright 2020 Alexander Bolz +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +#include "dragonbox.h" + +//-------------------------------------------------------------------------------------------------- +// This file contains an implementation of Junekey Jeon's Dragonbox algorithm. +// +// It is a simplified version of the reference implementation found here: +// https://github.com/jk-jeon/dragonbox +// +// The reference implementation also works with single-precision floating-point numbers and +// has options to configure the rounding mode. +//-------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#if defined(_MSC_VER) +#include +#endif + +#ifndef DRAGONBOX_ASSERT +#define DRAGONBOX_ASSERT(X) assert(X) +#endif + +//================================================================================================== +// +//================================================================================================== + +template +static inline Dest ReinterpretBits(Source source) +{ + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + + Dest dest; + std::memcpy(&dest, &source, sizeof(Source)); + return dest; +} + +namespace { +struct Double +{ + static_assert(std::numeric_limits::is_iec559 + && std::numeric_limits::digits == 53 + && std::numeric_limits::max_exponent == 1024, + "IEEE-754 double-precision implementation required"); + + using value_type = double; + using bits_type = uint64_t; + +// static constexpr int32_t MaxDigits10 = std::numeric_limits::max_digits10; + static constexpr int32_t SignificandSize = std::numeric_limits::digits; // = p (includes the hidden bit) + static constexpr int32_t ExponentBias = std::numeric_limits::max_exponent - 1 + (SignificandSize - 1); +// static constexpr int32_t MaxExponent = std::numeric_limits::max_exponent - 1 - (SignificandSize - 1); +// static constexpr int32_t MinExponent = std::numeric_limits::min_exponent - 1 - (SignificandSize - 1); + static constexpr bits_type MaxIeeeExponent = bits_type{2 * std::numeric_limits::max_exponent - 1}; + static constexpr bits_type HiddenBit = bits_type{1} << (SignificandSize - 1); // = 2^(p-1) + static constexpr bits_type SignificandMask = HiddenBit - 1; // = 2^(p-1) - 1 + static constexpr bits_type ExponentMask = MaxIeeeExponent << (SignificandSize - 1); + static constexpr bits_type SignMask = ~(~bits_type{0} >> 1); + + bits_type bits; + + explicit Double(bits_type bits_) : bits(bits_) {} + explicit Double(value_type value) : bits(ReinterpretBits(value)) {} + + bits_type PhysicalSignificand() const { + return bits & SignificandMask; + } + + bits_type PhysicalExponent() const { + return (bits & ExponentMask) >> (SignificandSize - 1); + } + + bool IsFinite() const { + return (bits & ExponentMask) != ExponentMask; + } + + bool IsInf() const { + return (bits & ExponentMask) == ExponentMask && (bits & SignificandMask) == 0; + } + + bool IsNaN() const { + return (bits & ExponentMask) == ExponentMask && (bits & SignificandMask) != 0; + } + + bool IsZero() const { + return (bits & ~SignMask) == 0; + } + + bool SignBit() const { + return (bits & SignMask) != 0; + } +}; +} // namespace + +//================================================================================================== +// +//================================================================================================== + +// Returns floor(x / 2^n). +// +// Technically, right-shift of negative integers is implementation defined... +// Should easily be optimized into SAR (or equivalent) instruction. +static inline int32_t FloorDivPow2(int32_t x, int32_t n) +{ +#if 0 + return x < 0 ? ~(~x >> n) : (x >> n); +#else + return x >> n; +#endif +} + +static inline int32_t FloorLog2Pow10(int32_t e) +{ + DRAGONBOX_ASSERT(e >= -1233); + DRAGONBOX_ASSERT(e <= 1233); + return FloorDivPow2(e * 1741647, 19); +} + +static inline int32_t FloorLog10Pow2(int32_t e) +{ + DRAGONBOX_ASSERT(e >= -1500); + DRAGONBOX_ASSERT(e <= 1500); + return FloorDivPow2(e * 1262611, 22); +} + +static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e) +{ + DRAGONBOX_ASSERT(e >= -1500); + DRAGONBOX_ASSERT(e <= 1500); + return FloorDivPow2(e * 1262611 - 524031, 22); +} + +//================================================================================================== +// +//================================================================================================== + +namespace { +struct uint64x2 { + uint64_t hi; + uint64_t lo; +}; +} + +static inline uint64x2 ComputePow10(int32_t k) +{ + static constexpr int32_t kMin = -292; + static constexpr int32_t kMax = 326; + static constexpr uint64x2 Pow10[kMax - kMin + 1] = { + {0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B}, + {0x9FAACF3DF73609B1, 0x77B191618C54E9AD}, + {0xC795830D75038C1D, 0xD59DF5B9EF6A2418}, + {0xF97AE3D0D2446F25, 0x4B0573286B44AD1E}, + {0x9BECCE62836AC577, 0x4EE367F9430AEC33}, + {0xC2E801FB244576D5, 0x229C41F793CDA740}, + {0xF3A20279ED56D48A, 0x6B43527578C11110}, + {0x9845418C345644D6, 0x830A13896B78AAAA}, + {0xBE5691EF416BD60C, 0x23CC986BC656D554}, + {0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9}, + {0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA}, + {0xB9E08A83A5E34F07, 0xDAF5CCD93FB0CC54}, + {0xE858AD248F5C22C9, 0xD1B3400F8F9CFF69}, + {0x91376C36D99995BE, 0x23100809B9C21FA2}, + {0xB58547448FFFFB2D, 0xABD40A0C2832A78B}, + {0xE2E69915B3FFF9F9, 0x16C90C8F323F516D}, + {0x8DD01FAD907FFC3B, 0xAE3DA7D97F6792E4}, + {0xB1442798F49FFB4A, 0x99CD11CFDF41779D}, + {0xDD95317F31C7FA1D, 0x40405643D711D584}, + {0x8A7D3EEF7F1CFC52, 0x482835EA666B2573}, + {0xAD1C8EAB5EE43B66, 0xDA3243650005EED0}, + {0xD863B256369D4A40, 0x90BED43E40076A83}, + {0x873E4F75E2224E68, 0x5A7744A6E804A292}, + {0xA90DE3535AAAE202, 0x711515D0A205CB37}, + {0xD3515C2831559A83, 0x0D5A5B44CA873E04}, + {0x8412D9991ED58091, 0xE858790AFE9486C3}, + {0xA5178FFF668AE0B6, 0x626E974DBE39A873}, + {0xCE5D73FF402D98E3, 0xFB0A3D212DC81290}, + {0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A}, + {0xA139029F6A239F72, 0x1C1FFFC1EBC44E81}, + {0xC987434744AC874E, 0xA327FFB266B56221}, + {0xFBE9141915D7A922, 0x4BF1FF9F0062BAA9}, + {0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4AA}, + {0xC4CE17B399107C22, 0xCB550FB4384D21D4}, + {0xF6019DA07F549B2B, 0x7E2A53A146606A49}, + {0x99C102844F94E0FB, 0x2EDA7444CBFC426E}, + {0xC0314325637A1939, 0xFA911155FEFB5309}, + {0xF03D93EEBC589F88, 0x793555AB7EBA27CB}, + {0x96267C7535B763B5, 0x4BC1558B2F3458DF}, + {0xBBB01B9283253CA2, 0x9EB1AAEDFB016F17}, + {0xEA9C227723EE8BCB, 0x465E15A979C1CADD}, + {0x92A1958A7675175F, 0x0BFACD89EC191ECA}, + {0xB749FAED14125D36, 0xCEF980EC671F667C}, + {0xE51C79A85916F484, 0x82B7E12780E7401B}, + {0x8F31CC0937AE58D2, 0xD1B2ECB8B0908811}, + {0xB2FE3F0B8599EF07, 0x861FA7E6DCB4AA16}, + {0xDFBDCECE67006AC9, 0x67A791E093E1D49B}, + {0x8BD6A141006042BD, 0xE0C8BB2C5C6D24E1}, + {0xAECC49914078536D, 0x58FAE9F773886E19}, + {0xDA7F5BF590966848, 0xAF39A475506A899F}, + {0x888F99797A5E012D, 0x6D8406C952429604}, + {0xAAB37FD7D8F58178, 0xC8E5087BA6D33B84}, + {0xD5605FCDCF32E1D6, 0xFB1E4A9A90880A65}, + {0x855C3BE0A17FCD26, 0x5CF2EEA09A550680}, + {0xA6B34AD8C9DFC06F, 0xF42FAA48C0EA481F}, + {0xD0601D8EFC57B08B, 0xF13B94DAF124DA27}, + {0x823C12795DB6CE57, 0x76C53D08D6B70859}, + {0xA2CB1717B52481ED, 0x54768C4B0C64CA6F}, + {0xCB7DDCDDA26DA268, 0xA9942F5DCF7DFD0A}, + {0xFE5D54150B090B02, 0xD3F93B35435D7C4D}, + {0x9EFA548D26E5A6E1, 0xC47BC5014A1A6DB0}, + {0xC6B8E9B0709F109A, 0x359AB6419CA1091C}, + {0xF867241C8CC6D4C0, 0xC30163D203C94B63}, + {0x9B407691D7FC44F8, 0x79E0DE63425DCF1E}, + {0xC21094364DFB5636, 0x985915FC12F542E5}, + {0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E}, + {0x979CF3CA6CEC5B5A, 0xA705992CEECF9C43}, + {0xBD8430BD08277231, 0x50C6FF782A838354}, + {0xECE53CEC4A314EBD, 0xA4F8BF5635246429}, + {0x940F4613AE5ED136, 0x871B7795E136BE9A}, + {0xB913179899F68584, 0x28E2557B59846E40}, + {0xE757DD7EC07426E5, 0x331AEADA2FE589D0}, + {0x9096EA6F3848984F, 0x3FF0D2C85DEF7622}, + {0xB4BCA50B065ABE63, 0x0FED077A756B53AA}, + {0xE1EBCE4DC7F16DFB, 0xD3E8495912C62895}, + {0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D}, + {0xB080392CC4349DEC, 0xBD8D794D96AACFB4}, + {0xDCA04777F541C567, 0xECF0D7A0FC5583A1}, + {0x89E42CAAF9491B60, 0xF41686C49DB57245}, + {0xAC5D37D5B79B6239, 0x311C2875C522CED6}, + {0xD77485CB25823AC7, 0x7D633293366B828C}, + {0x86A8D39EF77164BC, 0xAE5DFF9C02033198}, + {0xA8530886B54DBDEB, 0xD9F57F830283FDFD}, + {0xD267CAA862A12D66, 0xD072DF63C324FD7C}, + {0x8380DEA93DA4BC60, 0x4247CB9E59F71E6E}, + {0xA46116538D0DEB78, 0x52D9BE85F074E609}, + {0xCD795BE870516656, 0x67902E276C921F8C}, + {0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7}, + {0xA086CFCD97BF97F3, 0x80E8A40ECCD228A5}, + {0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE}, + {0xFAD2A4B13D1B5D6C, 0x796B805720085F82}, + {0x9CC3A6EEC6311A63, 0xCBE3303674053BB1}, + {0xC3F490AA77BD60FC, 0xBEDBFC4411068A9D}, + {0xF4F1B4D515ACB93B, 0xEE92FB5515482D45}, + {0x991711052D8BF3C5, 0x751BDD152D4D1C4B}, + {0xBF5CD54678EEF0B6, 0xD262D45A78A0635E}, + {0xEF340A98172AACE4, 0x86FB897116C87C35}, + {0x9580869F0E7AAC0E, 0xD45D35E6AE3D4DA1}, + {0xBAE0A846D2195712, 0x8974836059CCA10A}, + {0xE998D258869FACD7, 0x2BD1A438703FC94C}, + {0x91FF83775423CC06, 0x7B6306A34627DDD0}, + {0xB67F6455292CBF08, 0x1A3BC84C17B1D543}, + {0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94}, + {0x8E938662882AF53E, 0x547EB47B7282EE9D}, + {0xB23867FB2A35B28D, 0xE99E619A4F23AA44}, + {0xDEC681F9F4C31F31, 0x6405FA00E2EC94D5}, + {0x8B3C113C38F9F37E, 0xDE83BC408DD3DD05}, + {0xAE0B158B4738705E, 0x9624AB50B148D446}, + {0xD98DDAEE19068C76, 0x3BADD624DD9B0958}, + {0x87F8A8D4CFA417C9, 0xE54CA5D70A80E5D7}, + {0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4D}, + {0xD47487CC8470652B, 0x7647C32000696720}, + {0x84C8D4DFD2C63F3B, 0x29ECD9F40041E074}, + {0xA5FB0A17C777CF09, 0xF468107100525891}, + {0xCF79CC9DB955C2CC, 0x7182148D4066EEB5}, + {0x81AC1FE293D599BF, 0xC6F14CD848405531}, + {0xA21727DB38CB002F, 0xB8ADA00E5A506A7D}, + {0xCA9CF1D206FDC03B, 0xA6D90811F0E4851D}, + {0xFD442E4688BD304A, 0x908F4A166D1DA664}, + {0x9E4A9CEC15763E2E, 0x9A598E4E043287FF}, + {0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE}, + {0xF7549530E188C128, 0xD12BEE59E68EF47D}, + {0x9A94DD3E8CF578B9, 0x82BB74F8301958CF}, + {0xC13A148E3032D6E7, 0xE36A52363C1FAF02}, + {0xF18899B1BC3F8CA1, 0xDC44E6C3CB279AC2}, + {0x96F5600F15A7B7E5, 0x29AB103A5EF8C0BA}, + {0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8}, + {0xEBDF661791D60F56, 0x111B495B3464AD22}, + {0x936B9FCEBB25C995, 0xCAB10DD900BEEC35}, + {0xB84687C269EF3BFB, 0x3D5D514F40EEA743}, + {0xE65829B3046B0AFA, 0x0CB4A5A3112A5113}, + {0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC}, + {0xB3F4E093DB73A093, 0x59ED216765690F57}, + {0xE0F218B8D25088B8, 0x306869C13EC3532D}, + {0x8C974F7383725573, 0x1E414218C73A13FC}, + {0xAFBD2350644EEACF, 0xE5D1929EF90898FB}, + {0xDBAC6C247D62A583, 0xDF45F746B74ABF3A}, + {0x894BC396CE5DA772, 0x6B8BBA8C328EB784}, + {0xAB9EB47C81F5114F, 0x066EA92F3F326565}, + {0xD686619BA27255A2, 0xC80A537B0EFEFEBE}, + {0x8613FD0145877585, 0xBD06742CE95F5F37}, + {0xA798FC4196E952E7, 0x2C48113823B73705}, + {0xD17F3B51FCA3A7A0, 0xF75A15862CA504C6}, + {0x82EF85133DE648C4, 0x9A984D73DBE722FC}, + {0xA3AB66580D5FDAF5, 0xC13E60D0D2E0EBBB}, + {0xCC963FEE10B7D1B3, 0x318DF905079926A9}, + {0xFFBBCFE994E5C61F, 0xFDF17746497F7053}, + {0x9FD561F1FD0F9BD3, 0xFEB6EA8BEDEFA634}, + {0xC7CABA6E7C5382C8, 0xFE64A52EE96B8FC1}, + {0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1}, + {0x9C1661A651213E2D, 0x06BEA10CA65C084F}, + {0xC31BFA0FE5698DB8, 0x486E494FCFF30A63}, + {0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB}, + {0x986DDB5C6B3A76B7, 0xF89629465A75E01D}, + {0xBE89523386091465, 0xF6BBB397F1135824}, + {0xEE2BA6C0678B597F, 0x746AA07DED582E2D}, + {0x94DB483840B717EF, 0xA8C2A44EB4571CDD}, + {0xBA121A4650E4DDEB, 0x92F34D62616CE414}, + {0xE896A0D7E51E1566, 0x77B020BAF9C81D18}, + {0x915E2486EF32CD60, 0x0ACE1474DC1D122F}, + {0xB5B5ADA8AAFF80B8, 0x0D819992132456BB}, + {0xE3231912D5BF60E6, 0x10E1FFF697ED6C6A}, + {0x8DF5EFABC5979C8F, 0xCA8D3FFA1EF463C2}, + {0xB1736B96B6FD83B3, 0xBD308FF8A6B17CB3}, + {0xDDD0467C64BCE4A0, 0xAC7CB3F6D05DDBDF}, + {0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96C}, + {0xAD4AB7112EB3929D, 0x86C16C98D2C953C7}, + {0xD89D64D57A607744, 0xE871C7BF077BA8B8}, + {0x87625F056C7C4A8B, 0x11471CD764AD4973}, + {0xA93AF6C6C79B5D2D, 0xD598E40D3DD89BD0}, + {0xD389B47879823479, 0x4AFF1D108D4EC2C4}, + {0x843610CB4BF160CB, 0xCEDF722A585139BB}, + {0xA54394FE1EEDB8FE, 0xC2974EB4EE658829}, + {0xCE947A3DA6A9273E, 0x733D226229FEEA33}, + {0x811CCC668829B887, 0x0806357D5A3F5260}, + {0xA163FF802A3426A8, 0xCA07C2DCB0CF26F8}, + {0xC9BCFF6034C13052, 0xFC89B393DD02F0B6}, + {0xFC2C3F3841F17C67, 0xBBAC2078D443ACE3}, + {0x9D9BA7832936EDC0, 0xD54B944B84AA4C0E}, + {0xC5029163F384A931, 0x0A9E795E65D4DF12}, + {0xF64335BCF065D37D, 0x4D4617B5FF4A16D6}, + {0x99EA0196163FA42E, 0x504BCED1BF8E4E46}, + {0xC06481FB9BCF8D39, 0xE45EC2862F71E1D7}, + {0xF07DA27A82C37088, 0x5D767327BB4E5A4D}, + {0x964E858C91BA2655, 0x3A6A07F8D510F870}, + {0xBBE226EFB628AFEA, 0x890489F70A55368C}, + {0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F}, + {0x92C8AE6B464FC96F, 0x3B0B8BC90012929E}, + {0xB77ADA0617E3BBCB, 0x09CE6EBB40173745}, + {0xE55990879DDCAABD, 0xCC420A6A101D0516}, + {0x8F57FA54C2A9EAB6, 0x9FA946824A12232E}, + {0xB32DF8E9F3546564, 0x47939822DC96ABFA}, + {0xDFF9772470297EBD, 0x59787E2B93BC56F8}, + {0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B}, + {0xAEFAE51477A06B03, 0xEDE622920B6B23F2}, + {0xDAB99E59958885C4, 0xE95FAB368E45ECEE}, + {0x88B402F7FD75539B, 0x11DBCB0218EBB415}, + {0xAAE103B5FCD2A881, 0xD652BDC29F26A11A}, + {0xD59944A37C0752A2, 0x4BE76D3346F04960}, + {0x857FCAE62D8493A5, 0x6F70A4400C562DDC}, + {0xA6DFBD9FB8E5B88E, 0xCB4CCD500F6BB953}, + {0xD097AD07A71F26B2, 0x7E2000A41346A7A8}, + {0x825ECC24C873782F, 0x8ED400668C0C28C9}, + {0xA2F67F2DFA90563B, 0x728900802F0F32FB}, + {0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA}, + {0xFEA126B7D78186BC, 0xE2F610C84987BFA9}, + {0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7CA}, + {0xC6EDE63FA05D3143, 0x91503D1C79720DBC}, + {0xF8A95FCF88747D94, 0x75A44C6397CE912B}, + {0x9B69DBE1B548CE7C, 0xC986AFBE3EE11ABB}, + {0xC24452DA229B021B, 0xFBE85BADCE996169}, + {0xF2D56790AB41C2A2, 0xFAE27299423FB9C4}, + {0x97C560BA6B0919A5, 0xDCCD879FC967D41B}, + {0xBDB6B8E905CB600F, 0x5400E987BBC1C921}, + {0xED246723473E3813, 0x290123E9AAB23B69}, + {0x9436C0760C86E30B, 0xF9A0B6720AAF6522}, + {0xB94470938FA89BCE, 0xF808E40E8D5B3E6A}, + {0xE7958CB87392C2C2, 0xB60B1D1230B20E05}, + {0x90BD77F3483BB9B9, 0xB1C6F22B5E6F48C3}, + {0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF4}, + {0xE2280B6C20DD5232, 0x25C6DA63C38DE1B1}, + {0x8D590723948A535F, 0x579C487E5A38AD0F}, + {0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852}, + {0xDCDB1B2798182244, 0xF8E431456CF88E66}, + {0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900}, + {0xAC8B2D36EED2DAC5, 0xE272467E3D222F40}, + {0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10}, + {0x86CCBB52EA94BAEA, 0x98E947129FC2B4EA}, + {0xA87FEA27A539E9A5, 0x3F2398D747B36225}, + {0xD29FE4B18E88640E, 0x8EEC7F0D19A03AAE}, + {0x83A3EEEEF9153E89, 0x1953CF68300424AD}, + {0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD8}, + {0xCDB02555653131B6, 0x3792F412CB06794E}, + {0x808E17555F3EBF11, 0xE2BBD88BBEE40BD1}, + {0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC5}, + {0xC8DE047564D20A8B, 0xF245825A5A445276}, + {0xFB158592BE068D2E, 0xEED6E2F0F0D56713}, + {0x9CED737BB6C4183D, 0x55464DD69685606C}, + {0xC428D05AA4751E4C, 0xAA97E14C3C26B887}, + {0xF53304714D9265DF, 0xD53DD99F4B3066A9}, + {0x993FE2C6D07B7FAB, 0xE546A8038EFE402A}, + {0xBF8FDB78849A5F96, 0xDE98520472BDD034}, + {0xEF73D256A5C0F77C, 0x963E66858F6D4441}, + {0x95A8637627989AAD, 0xDDE7001379A44AA9}, + {0xBB127C53B17EC159, 0x5560C018580D5D53}, + {0xE9D71B689DDE71AF, 0xAAB8F01E6E10B4A7}, + {0x9226712162AB070D, 0xCAB3961304CA70E9}, + {0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D23}, + {0xE45C10C42A2B3B05, 0x8CB89A7DB77C506B}, + {0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243}, + {0xB267ED1940F1C61C, 0x55F038B237591ED4}, + {0xDF01E85F912E37A3, 0x6B6C46DEC52F6689}, + {0x8B61313BBABCE2C6, 0x2323AC4B3B3DA016}, + {0xAE397D8AA96C1B77, 0xABEC975E0A0D081B}, + {0xD9C7DCED53C72255, 0x96E7BD358C904A22}, + {0x881CEA14545C7575, 0x7E50D64177DA2E55}, + {0xAA242499697392D2, 0xDDE50BD1D5D0B9EA}, + {0xD4AD2DBFC3D07787, 0x955E4EC64B44E865}, + {0x84EC3C97DA624AB4, 0xBD5AF13BEF0B113F}, + {0xA6274BBDD0FADD61, 0xECB1AD8AEACDD58F}, + {0xCFB11EAD453994BA, 0x67DE18EDA5814AF3}, + {0x81CEB32C4B43FCF4, 0x80EACF948770CED8}, + {0xA2425FF75E14FC31, 0xA1258379A94D028E}, + {0xCAD2F7F5359A3B3E, 0x096EE45813A04331}, + {0xFD87B5F28300CA0D, 0x8BCA9D6E188853FD}, + {0x9E74D1B791E07E48, 0x775EA264CF55347E}, + {0xC612062576589DDA, 0x95364AFE032A819E}, + {0xF79687AED3EEC551, 0x3A83DDBD83F52205}, + {0x9ABE14CD44753B52, 0xC4926A9672793543}, + {0xC16D9A0095928A27, 0x75B7053C0F178294}, + {0xF1C90080BAF72CB1, 0x5324C68B12DD6339}, + {0x971DA05074DA7BEE, 0xD3F6FC16EBCA5E04}, + {0xBCE5086492111AEA, 0x88F4BB1CA6BCF585}, + {0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E6}, + {0x9392EE8E921D5D07, 0x3AFF322E62439FD0}, + {0xB877AA3236A4B449, 0x09BEFEB9FAD487C3}, + {0xE69594BEC44DE15B, 0x4C2EBE687989A9B4}, + {0x901D7CF73AB0ACD9, 0x0F9D37014BF60A11}, + {0xB424DC35095CD80F, 0x538484C19EF38C95}, + {0xE12E13424BB40E13, 0x2865A5F206B06FBA}, + {0x8CBCCC096F5088CB, 0xF93F87B7442E45D4}, + {0xAFEBFF0BCB24AAFE, 0xF78F69A51539D749}, + {0xDBE6FECEBDEDD5BE, 0xB573440E5A884D1C}, + {0x89705F4136B4A597, 0x31680A88F8953031}, + {0xABCC77118461CEFC, 0xFDC20D2B36BA7C3E}, + {0xD6BF94D5E57A42BC, 0x3D32907604691B4D}, + {0x8637BD05AF6C69B5, 0xA63F9A49C2C1B110}, + {0xA7C5AC471B478423, 0x0FCF80DC33721D54}, + {0xD1B71758E219652B, 0xD3C36113404EA4A9}, + {0x83126E978D4FDF3B, 0x645A1CAC083126EA}, + {0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4}, + {0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCD}, + {0x8000000000000000, 0x0000000000000000}, + {0xA000000000000000, 0x0000000000000000}, + {0xC800000000000000, 0x0000000000000000}, + {0xFA00000000000000, 0x0000000000000000}, + {0x9C40000000000000, 0x0000000000000000}, + {0xC350000000000000, 0x0000000000000000}, + {0xF424000000000000, 0x0000000000000000}, + {0x9896800000000000, 0x0000000000000000}, + {0xBEBC200000000000, 0x0000000000000000}, + {0xEE6B280000000000, 0x0000000000000000}, + {0x9502F90000000000, 0x0000000000000000}, + {0xBA43B74000000000, 0x0000000000000000}, + {0xE8D4A51000000000, 0x0000000000000000}, + {0x9184E72A00000000, 0x0000000000000000}, + {0xB5E620F480000000, 0x0000000000000000}, + {0xE35FA931A0000000, 0x0000000000000000}, + {0x8E1BC9BF04000000, 0x0000000000000000}, + {0xB1A2BC2EC5000000, 0x0000000000000000}, + {0xDE0B6B3A76400000, 0x0000000000000000}, + {0x8AC7230489E80000, 0x0000000000000000}, + {0xAD78EBC5AC620000, 0x0000000000000000}, + {0xD8D726B7177A8000, 0x0000000000000000}, + {0x878678326EAC9000, 0x0000000000000000}, + {0xA968163F0A57B400, 0x0000000000000000}, + {0xD3C21BCECCEDA100, 0x0000000000000000}, + {0x84595161401484A0, 0x0000000000000000}, + {0xA56FA5B99019A5C8, 0x0000000000000000}, + {0xCECB8F27F4200F3A, 0x0000000000000000}, + {0x813F3978F8940984, 0x4000000000000000}, + {0xA18F07D736B90BE5, 0x5000000000000000}, + {0xC9F2C9CD04674EDE, 0xA400000000000000}, + {0xFC6F7C4045812296, 0x4D00000000000000}, + {0x9DC5ADA82B70B59D, 0xF020000000000000}, + {0xC5371912364CE305, 0x6C28000000000000}, + {0xF684DF56C3E01BC6, 0xC732000000000000}, + {0x9A130B963A6C115C, 0x3C7F400000000000}, + {0xC097CE7BC90715B3, 0x4B9F100000000000}, + {0xF0BDC21ABB48DB20, 0x1E86D40000000000}, + {0x96769950B50D88F4, 0x1314448000000000}, + {0xBC143FA4E250EB31, 0x17D955A000000000}, + {0xEB194F8E1AE525FD, 0x5DCFAB0800000000}, + {0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000}, + {0xB7ABC627050305AD, 0xF14A3D9E40000000}, + {0xE596B7B0C643C719, 0x6D9CCD05D0000000}, + {0x8F7E32CE7BEA5C6F, 0xE4820023A2000000}, + {0xB35DBF821AE4F38B, 0xDDA2802C8A800000}, + {0xE0352F62A19E306E, 0xD50B2037AD200000}, + {0x8C213D9DA502DE45, 0x4526F422CC340000}, + {0xAF298D050E4395D6, 0x9670B12B7F410000}, + {0xDAF3F04651D47B4C, 0x3C0CDD765F114000}, + {0x88D8762BF324CD0F, 0xA5880A69FB6AC800}, + {0xAB0E93B6EFEE0053, 0x8EEA0D047A457A00}, + {0xD5D238A4ABE98068, 0x72A4904598D6D880}, + {0x85A36366EB71F041, 0x47A6DA2B7F864750}, + {0xA70C3C40A64E6C51, 0x999090B65F67D924}, + {0xD0CF4B50CFE20765, 0xFFF4B4E3F741CF6D}, + {0x82818F1281ED449F, 0xBFF8F10E7A8921A4}, + {0xA321F2D7226895C7, 0xAFF72D52192B6A0D}, + {0xCBEA6F8CEB02BB39, 0x9BF4F8A69F764490}, + {0xFEE50B7025C36A08, 0x02F236D04753D5B4}, + {0x9F4F2726179A2245, 0x01D762422C946590}, + {0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF5}, + {0xF8EBAD2B84E0D58B, 0xD2E0898765A7DEB2}, + {0x9B934C3B330C8577, 0x63CC55F49F88EB2F}, + {0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FB}, + {0xF316271C7FC3908A, 0x8BEF464E3945EF7A}, + {0x97EDD871CFDA3A56, 0x97758BF0E3CBB5AC}, + {0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA317}, + {0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDD}, + {0x945E455F24FB1CF8, 0x8FE8CAA93E74EF6A}, + {0xB975D6B6EE39E436, 0xB3E2FD538E122B44}, + {0xE7D34C64A9C85D44, 0x60DBBCA87196B616}, + {0x90E40FBEEA1D3A4A, 0xBC8955E946FE31CD}, + {0xB51D13AEA4A488DD, 0x6BABAB6398BDBE41}, + {0xE264589A4DCDAB14, 0xC696963C7EED2DD1}, + {0x8D7EB76070A08AEC, 0xFC1E1DE5CF543CA2}, + {0xB0DE65388CC8ADA8, 0x3B25A55F43294BCB}, + {0xDD15FE86AFFAD912, 0x49EF0EB713F39EBE}, + {0x8A2DBF142DFCC7AB, 0x6E3569326C784337}, + {0xACB92ED9397BF996, 0x49C2C37F07965404}, + {0xD7E77A8F87DAF7FB, 0xDC33745EC97BE906}, + {0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A3}, + {0xA8ACD7C0222311BC, 0xC40832EA0D68CE0C}, + {0xD2D80DB02AABD62B, 0xF50A3FA490C30190}, + {0x83C7088E1AAB65DB, 0x792667C6DA79E0FA}, + {0xA4B8CAB1A1563F52, 0x577001B891185938}, + {0xCDE6FD5E09ABCF26, 0xED4C0226B55E6F86}, + {0x80B05E5AC60B6178, 0x544F8158315B05B4}, + {0xA0DC75F1778E39D6, 0x696361AE3DB1C721}, + {0xC913936DD571C84C, 0x03BC3A19CD1E38E9}, + {0xFB5878494ACE3A5F, 0x04AB48A04065C723}, + {0x9D174B2DCEC0E47B, 0x62EB0D64283F9C76}, + {0xC45D1DF942711D9A, 0x3BA5D0BD324F8394}, + {0xF5746577930D6500, 0xCA8F44EC7EE36479}, + {0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECB}, + {0xBFC2EF456AE276E8, 0x9E3FEDD8C321A67E}, + {0xEFB3AB16C59B14A2, 0xC5CFE94EF3EA101E}, + {0x95D04AEE3B80ECE5, 0xBBA1F1D158724A12}, + {0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC97}, + {0xEA1575143CF97226, 0xF52D09D71A3293BD}, + {0x924D692CA61BE758, 0x593C2626705F9C56}, + {0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836C}, + {0xE498F455C38B997A, 0x0B6DFB9C0F956447}, + {0x8EDF98B59A373FEC, 0x4724BD4189BD5EAC}, + {0xB2977EE300C50FE7, 0x58EDEC91EC2CB657}, + {0xDF3D5E9BC0F653E1, 0x2F2967B66737E3ED}, + {0x8B865B215899F46C, 0xBD79E0D20082EE74}, + {0xAE67F1E9AEC07187, 0xECD8590680A3AA11}, + {0xDA01EE641A708DE9, 0xE80E6F4820CC9495}, + {0x884134FE908658B2, 0x3109058D147FDCDD}, + {0xAA51823E34A7EEDE, 0xBD4B46F0599FD415}, + {0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91A}, + {0x850FADC09923329E, 0x03E2CF6BC604DDB0}, + {0xA6539930BF6BFF45, 0x84DB8346B786151C}, + {0xCFE87F7CEF46FF16, 0xE612641865679A63}, + {0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07E}, + {0xA26DA3999AEF7749, 0xE3BE5E330F38F09D}, + {0xCB090C8001AB551C, 0x5CADF5BFD3072CC5}, + {0xFDCB4FA002162A63, 0x73D9732FC7C8F7F6}, + {0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFA}, + {0xC646D63501A1511D, 0xB281E1FD541501B8}, + {0xF7D88BC24209A565, 0x1F225A7CA91A4226}, + {0x9AE757596946075F, 0x3375788DE9B06958}, + {0xC1A12D2FC3978937, 0x0052D6B1641C83AE}, + {0xF209787BB47D6B84, 0xC0678C5DBD23A49A}, + {0x9745EB4D50CE6332, 0xF840B7BA963646E0}, + {0xBD176620A501FBFF, 0xB650E5A93BC3D898}, + {0xEC5D3FA8CE427AFF, 0xA3E51F138AB4CEBE}, + {0x93BA47C980E98CDF, 0xC66F336C36B10137}, + {0xB8A8D9BBE123F017, 0xB80B0047445D4184}, + {0xE6D3102AD96CEC1D, 0xA60DC059157491E5}, + {0x9043EA1AC7E41392, 0x87C89837AD68DB2F}, + {0xB454E4A179DD1877, 0x29BABE4598C311FB}, + {0xE16A1DC9D8545E94, 0xF4296DD6FEF3D67A}, + {0x8CE2529E2734BB1D, 0x1899E4A65F58660C}, + {0xB01AE745B101E9E4, 0x5EC05DCFF72E7F8F}, + {0xDC21A1171D42645D, 0x76707543F4FA1F73}, + {0x899504AE72497EBA, 0x6A06494A791C53A8}, + {0xABFA45DA0EDBDE69, 0x0487DB9D17636892}, + {0xD6F8D7509292D603, 0x45A9D2845D3C42B6}, + {0x865B86925B9BC5C2, 0x0B8A2392BA45A9B2}, + {0xA7F26836F282B732, 0x8E6CAC7768D7141E}, + {0xD1EF0244AF2364FF, 0x3207D795430CD926}, + {0x8335616AED761F1F, 0x7F44E6BD49E807B8}, + {0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A6}, + {0xCD036837130890A1, 0x36DBA887C37A8C0F}, + {0x802221226BE55A64, 0xC2494954DA2C9789}, + {0xA02AA96B06DEB0FD, 0xF2DB9BAA10B7BD6C}, + {0xC83553C5C8965D3D, 0x6F92829494E5ACC7}, + {0xFA42A8B73ABBF48C, 0xCB772339BA1F17F9}, + {0x9C69A97284B578D7, 0xFF2A760414536EFB}, + {0xC38413CF25E2D70D, 0xFEF5138519684ABA}, + {0xF46518C2EF5B8CD1, 0x7EB258665FC25D69}, + {0x98BF2F79D5993802, 0xEF2F773FFBD97A61}, + {0xBEEEFB584AFF8603, 0xAAFB550FFACFD8FA}, + {0xEEAABA2E5DBF6784, 0x95BA2A53F983CF38}, + {0x952AB45CFA97A0B2, 0xDD945A747BF26183}, + {0xBA756174393D88DF, 0x94F971119AEEF9E4}, + {0xE912B9D1478CEB17, 0x7A37CD5601AAB85D}, + {0x91ABB422CCB812EE, 0xAC62E055C10AB33A}, + {0xB616A12B7FE617AA, 0x577B986B314D6009}, + {0xE39C49765FDF9D94, 0xED5A7E85FDA0B80B}, + {0x8E41ADE9FBEBC27D, 0x14588F13BE847307}, + {0xB1D219647AE6B31C, 0x596EB2D8AE258FC8}, + {0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BB}, + {0x8AEC23D680043BEE, 0x25DE7BB9480D5854}, + {0xADA72CCC20054AE9, 0xAF561AA79A10AE6A}, + {0xD910F7FF28069DA4, 0x1B2BA1518094DA04}, + {0x87AA9AFF79042286, 0x90FB44D2F05D0842}, + {0xA99541BF57452B28, 0x353A1607AC744A53}, + {0xD3FA922F2D1675F2, 0x42889B8997915CE8}, + {0x847C9B5D7C2E09B7, 0x69956135FEBADA11}, + {0xA59BC234DB398C25, 0x43FAB9837E699095}, + {0xCF02B2C21207EF2E, 0x94F967E45E03F4BB}, + {0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F5}, + {0xA1BA1BA79E1632DC, 0x6462D92A69731732}, + {0xCA28A291859BBF93, 0x7D7B8F7503CFDCFE}, + {0xFCB2CB35E702AF78, 0x5CDA735244C3D43E}, + {0x9DEFBF01B061ADAB, 0x3A0888136AFA64A7}, + {0xC56BAEC21C7A1916, 0x088AAA1845B8FDD0}, + {0xF6C69A72A3989F5B, 0x8AAD549E57273D45}, + {0x9A3C2087A63F6399, 0x36AC54E2F678864B}, + {0xC0CB28A98FCF3C7F, 0x84576A1BB416A7DD}, + {0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D5}, + {0x969EB7C47859E743, 0x9F644AE5A4B1B325}, + {0xBC4665B596706114, 0x873D5D9F0DDE1FEE}, + {0xEB57FF22FC0C7959, 0xA90CB506D155A7EA}, + {0x9316FF75DD87CBD8, 0x09A7F12442D588F2}, + {0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB2F}, + {0xE5D3EF282A242E81, 0x8F1668C8A86DA5FA}, + {0x8FA475791A569D10, 0xF96E017D694487BC}, + {0xB38D92D760EC4455, 0x37C981DCC395A9AC}, + {0xE070F78D3927556A, 0x85BBE253F47B1417}, + {0x8C469AB843B89562, 0x93956D7478CCEC8E}, + {0xAF58416654A6BABB, 0x387AC8D1970027B2}, + {0xDB2E51BFE9D0696A, 0x06997B05FCC0319E}, + {0x88FCF317F22241E2, 0x441FECE3BDF81F03}, + {0xAB3C2FDDEEAAD25A, 0xD527E81CAD7626C3}, + {0xD60B3BD56A5586F1, 0x8A71E223D8D3B074}, + {0x85C7056562757456, 0xF6872D5667844E49}, + {0xA738C6BEBB12D16C, 0xB428F8AC016561DB}, + {0xD106F86E69D785C7, 0xE13336D701BEBA52}, + {0x82A45B450226B39C, 0xECC0024661173473}, + {0xA34D721642B06084, 0x27F002D7F95D0190}, + {0xCC20CE9BD35C78A5, 0x31EC038DF7B441F4}, + {0xFF290242C83396CE, 0x7E67047175A15271}, + {0x9F79A169BD203E41, 0x0F0062C6E984D386}, + {0xC75809C42C684DD1, 0x52C07B78A3E60868}, + {0xF92E0C3537826145, 0xA7709A56CCDF8A82}, + {0x9BBCC7A142B17CCB, 0x88A66076400BB691}, + {0xC2ABF989935DDBFE, 0x6ACFF893D00EA435}, + {0xF356F7EBF83552FE, 0x0583F6B8C4124D43}, + {0x98165AF37B2153DE, 0xC3727A337A8B704A}, + {0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5C}, + {0xEDA2EE1C7064130C, 0x1162DEF06F79DF73}, + {0x9485D4D1C63E8BE7, 0x8ADDCB5645AC2BA8}, + {0xB9A74A0637CE2EE1, 0x6D953E2BD7173692}, + {0xE8111C87C5C1BA99, 0xC8FA8DB6CCDD0437}, + {0x910AB1D4DB9914A0, 0x1D9C9892400A22A2}, + {0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4B}, + {0xE2A0B5DC971F303A, 0x2E44AE64840FD61D}, + {0x8DA471A9DE737E24, 0x5CEAECFED289E5D2}, + {0xB10D8E1456105DAD, 0x7425A83E872C5F47}, + {0xDD50F1996B947518, 0xD12F124E28F77719}, + {0x8A5296FFE33CC92F, 0x82BD6B70D99AAA6F}, + {0xACE73CBFDC0BFB7B, 0x636CC64D1001550B}, + {0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4E}, + {0x8714A775E3E95C78, 0x65ACFAEC34810A71}, + {0xA8D9D1535CE3B396, 0x7F1839A741A14D0D}, + {0xD31045A8341CA07C, 0x1EDE48111209A050}, + {0x83EA2B892091E44D, 0x934AED0AAB460432}, + {0xA4E4B66B68B65D60, 0xF81DA84D5617853F}, + {0xCE1DE40642E3F4B9, 0x36251260AB9D668E}, + {0x80D2AE83E9CE78F3, 0xC1D72B7C6B426019}, + {0xA1075A24E4421730, 0xB24CF65B8612F81F}, + {0xC94930AE1D529CFC, 0xDEE033F26797B627}, + {0xFB9B7CD9A4A7443C, 0x169840EF017DA3B1}, + {0x9D412E0806E88AA5, 0x8E1F289560EE864E}, + {0xC491798A08A2AD4E, 0xF1A6F2BAB92A27E2}, + {0xF5B5D7EC8ACB58A2, 0xAE10AF696774B1DB}, + {0x9991A6F3D6BF1765, 0xACCA6DA1E0A8EF29}, + {0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF3}, + {0xEFF394DCFF8A948E, 0xDDFC4B4CEF07F5B0}, + {0x95F83D0A1FB69CD9, 0x4ABDAF101564F98E}, + {0xBB764C4CA7A4440F, 0x9D6D1AD41ABE37F1}, + {0xEA53DF5FD18D5513, 0x84C86189216DC5ED}, + {0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB4}, + {0xB7118682DBB66A77, 0x3FBC8C33221DC2A1}, + {0xE4D5E82392A40515, 0x0FABAF3FEAA5334A}, + {0x8F05B1163BA6832D, 0x29CB4D87F2A7400E}, + {0xB2C71D5BCA9023F8, 0x743E20E9EF511012}, + {0xDF78E4B2BD342CF6, 0x914DA9246B255416}, + {0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548E}, + {0xAE9672ABA3D0C320, 0xA184AC2473B529B1}, + {0xDA3C0F568CC4F3E8, 0xC9E5D72D90A2741E}, + {0x8865899617FB1871, 0x7E2FA67C7A658892}, + {0xAA7EEBFB9DF9DE8D, 0xDDBB901B98FEEAB7}, + {0xD51EA6FA85785631, 0x552A74227F3EA565}, + {0x8533285C936B35DE, 0xD53A88958F87275F}, + {0xA67FF273B8460356, 0x8A892ABAF368F137}, + {0xD01FEF10A657842C, 0x2D2B7569B0432D85}, + {0x8213F56A67F6B29B, 0x9C3B29620E29FC73}, + {0xA298F2C501F45F42, 0x8349F3BA91B47B8F}, + {0xCB3F2F7642717713, 0x241C70A936219A73}, + {0xFE0EFB53D30DD4D7, 0xED238CD383AA0110}, + {0x9EC95D1463E8A506, 0xF4363804324A40AA}, + {0xC67BB4597CE2CE48, 0xB143C6053EDCD0D5}, + {0xF81AA16FDC1B81DA, 0xDD94B7868E94050A}, + {0x9B10A4E5E9913128, 0xCA7CF2B4191C8326}, + {0xC1D4CE1F63F57D72, 0xFD1C2F611F63A3F0}, + {0xF24A01A73CF2DCCF, 0xBC633B39673C8CEC}, + {0x976E41088617CA01, 0xD5BE0503E085D813}, + {0xBD49D14AA79DBC82, 0x4B2D8644D8A74E18}, + {0xEC9C459D51852BA2, 0xDDF8E7D60ED1219E}, + {0x93E1AB8252F33B45, 0xCABB90E5C942B503}, + {0xB8DA1662E7B00A17, 0x3D6A751F3B936243}, + {0xE7109BFBA19C0C9D, 0x0CC512670A783AD4}, + {0x906A617D450187E2, 0x27FB2B80668B24C5}, + {0xB484F9DC9641E9DA, 0xB1F9F660802DEDF6}, + {0xE1A63853BBD26451, 0x5E7873F8A0396973}, + {0x8D07E33455637EB2, 0xDB0B487B6423E1E8}, + {0xB049DC016ABC5E5F, 0x91CE1A9A3D2CDA62}, + {0xDC5C5301C56B75F7, 0x7641A140CC7810FB}, + {0x89B9B3E11B6329BA, 0xA9E904C87FCB0A9D}, + {0xAC2820D9623BF429, 0x546345FA9FBDCD44}, + {0xD732290FBACAF133, 0xA97C177947AD4095}, + {0x867F59A9D4BED6C0, 0x49ED8EABCCCC485D}, + {0xA81F301449EE8C70, 0x5C68F256BFFF5A74}, + {0xD226FC195C6A2F8C, 0x73832EEC6FFF3111}, + {0x83585D8FD9C25DB7, 0xC831FD53C5FF7EAB}, + {0xA42E74F3D032F525, 0xBA3E7CA8B77F5E55}, + {0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EB}, + {0x80444B5E7AA7CF85, 0x7980D163CF5B81B3}, + {0xA0555E361951C366, 0xD7E105BCC332621F}, + {0xC86AB5C39FA63440, 0x8DD9472BF3FEFAA7}, + {0xFA856334878FC150, 0xB14F98F6F0FEB951}, + {0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D3}, + {0xC3B8358109E84F07, 0x0A862F80EC4700C8}, + {0xF4A642E14C6262C8, 0xCD27BB612758C0FA}, + {0x98E7E9CCCFBD7DBD, 0x8038D51CB897789C}, + {0xBF21E44003ACDD2C, 0xE0470A63E6BD56C3}, + {0xEEEA5D5004981478, 0x1858CCFCE06CAC74}, + {0x95527A5202DF0CCB, 0x0F37801E0C43EBC8}, + {0xBAA718E68396CFFD, 0xD30560258F54E6BA}, + {0xE950DF20247C83FD, 0x47C6B82EF32A2069}, + {0x91D28B7416CDD27E, 0x4CDC331D57FA5441}, + {0xB6472E511C81471D, 0xE0133FE4ADF8E952}, + {0xE3D8F9E563A198E5, 0x58180FDDD97723A6}, + {0x8E679C2F5E44FF8F, 0x570F09EAA7EA7648}, + {0xB201833B35D63F73, 0x2CD2CC6551E513DA}, + {0xDE81E40A034BCF4F, 0xF8077F7EA65E58D1}, + {0x8B112E86420F6191, 0xFB04AFAF27FAF782}, + {0xADD57A27D29339F6, 0x79C5DB9AF1F9B563}, + {0xD94AD8B1C7380874, 0x18375281AE7822BC}, + {0x87CEC76F1C830548, 0x8F2293910D0B15B5}, + {0xA9C2794AE3A3C69A, 0xB2EB3875504DDB22}, + {0xD433179D9C8CB841, 0x5FA60692A46151EB}, + {0x849FEEC281D7F328, 0xDBC7C41BA6BCD333}, + {0xA5C7EA73224DEFF3, 0x12B9B522906C0800}, + {0xCF39E50FEAE16BEF, 0xD768226B34870A00}, + {0x81842F29F2CCE375, 0xE6A1158300D46640}, + {0xA1E53AF46F801C53, 0x60495AE3C1097FD0}, + {0xCA5E89B18B602368, 0x385BB19CB14BDFC4}, + {0xFCF62C1DEE382C42, 0x46729E03DD9ED7B5}, + {0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D1}, + {0xC5A05277621BE293, 0xC7098B7305241885}, + {0xF70867153AA2DB38, 0xB8CBEE4FC66D1EA7}, + }; + + DRAGONBOX_ASSERT(k >= kMin); + DRAGONBOX_ASSERT(k <= kMax); + return Pow10[static_cast(k - kMin)]; +} + +// Returns whether value is divisible by 2^e2 +static inline bool MultipleOfPow2(uint64_t value, int32_t e2) +{ + DRAGONBOX_ASSERT(e2 >= 0); + return e2 < 64 && (value & ((uint64_t{1} << e2) - 1)) == 0; +} + +// Returns whether value is divisible by 5^e5 +static inline bool MultipleOfPow5(uint64_t value, int32_t e5) +{ + struct MulCmp { + uint64_t mul; + uint64_t cmp; + }; + + static constexpr MulCmp Mod5[] = { + {0x0000000000000001u, 0xFFFFFFFFFFFFFFFFu}, // 5^0 + {0xCCCCCCCCCCCCCCCDu, 0x3333333333333333u}, // 5^1 + {0x8F5C28F5C28F5C29u, 0x0A3D70A3D70A3D70u}, // 5^2 + {0x1CAC083126E978D5u, 0x020C49BA5E353F7Cu}, // 5^3 + {0xD288CE703AFB7E91u, 0x0068DB8BAC710CB2u}, // 5^4 + {0x5D4E8FB00BCBE61Du, 0x0014F8B588E368F0u}, // 5^5 + {0x790FB65668C26139u, 0x000431BDE82D7B63u}, // 5^6 + {0xE5032477AE8D46A5u, 0x0000D6BF94D5E57Au}, // 5^7 + {0xC767074B22E90E21u, 0x00002AF31DC46118u}, // 5^8 + {0x8E47CE423A2E9C6Du, 0x0000089705F4136Bu}, // 5^9 + {0x4FA7F60D3ED61F49u, 0x000001B7CDFD9D7Bu}, // 5^10 + {0x0FEE64690C913975u, 0x00000057F5FF85E5u}, // 5^11 + {0x3662E0E1CF503EB1u, 0x000000119799812Du}, // 5^12 + {0xA47A2CF9F6433FBDu, 0x0000000384B84D09u}, // 5^13 + {0x54186F653140A659u, 0x00000000B424DC35u}, // 5^14 + {0x7738164770402145u, 0x0000000024075F3Du}, // 5^15 + {0xE4A4D1417CD9A041u, 0x000000000734ACA5u}, // 5^16 + {0xC75429D9E5C5200Du, 0x000000000170EF54u}, // 5^17 + {0xC1773B91FAC10669u, 0x000000000049C977u}, // 5^18 + {0x26B172506559CE15u, 0x00000000000EC1E4u}, // 5^19 + {0xD489E3A9ADDEC2D1u, 0x000000000002F394u}, // 5^20 + {0x90E860BB892C8D5Du, 0x000000000000971Du}, // 5^21 + {0x502E79BF1B6F4F79u, 0x0000000000001E39u}, // 5^22 + {0xDCD618596BE30FE5u, 0x000000000000060Bu}, // 5^23 + {0x2C2AD1AB7BFA3661u, 0x0000000000000135u}, // 5^24 + }; + + DRAGONBOX_ASSERT(e5 >= 0); + DRAGONBOX_ASSERT(e5 <= 24); + const MulCmp m5 = Mod5[static_cast(e5)]; + + return value * m5.mul <= m5.cmp; +} + +namespace { +struct FloatingDecimal64 { + uint64_t significand; + int32_t exponent; +}; +} + +static inline FloatingDecimal64 ToDecimal64_asymmetric_interval(int32_t e2) +{ + // NB: + // accept_lower_endpoint = true + // accept_upper_endpoint = true + + static constexpr int32_t P = Double::SignificandSize; + + // Compute k and beta + const int32_t minus_k = FloorLog10ThreeQuartersPow2(e2); + const int32_t beta_minus_1 = e2 + FloorLog2Pow10(-minus_k); + + // Compute xi and zi + const uint64x2 pow10 = ComputePow10(-minus_k); + + const uint64_t lower_endpoint = (pow10.hi - (pow10.hi >> (P + 1))) >> (64 - P - beta_minus_1); + const uint64_t upper_endpoint = (pow10.hi + (pow10.hi >> (P + 0))) >> (64 - P - beta_minus_1); + + // If we don't accept the left endpoint (but we do!) or + // if the left endpoint is not an integer, increase it + const bool lower_endpoint_is_integer = (2 <= e2 && e2 <= 3); + + const uint64_t xi = lower_endpoint + !lower_endpoint_is_integer; + const uint64_t zi = upper_endpoint; + + // Try bigger divisor + uint64_t q = zi / 10; + if (q * 10 >= xi) + { + return {q, minus_k + 1}; + } + + // Otherwise, compute the round-up of y + q = ((pow10.hi >> (64 - (P + 1) - beta_minus_1)) + 1) / 2; + + // When tie occurs, choose one of them according to the rule + if (e2 == -77) + { + q -= (q % 2 != 0); // Round to even. + } + else + { + q += (q < xi); + } + + return {q, minus_k}; +} + +static inline uint32_t ComputeDelta(uint64x2 pow10, int32_t beta_minus_1) +{ + DRAGONBOX_ASSERT(beta_minus_1 >= 0); + DRAGONBOX_ASSERT(beta_minus_1 <= 63); + return static_cast(pow10.hi >> (64 - 1 - beta_minus_1)); +} + +#if defined(__SIZEOF_INT128__) +static inline uint64x2 Mul128(uint64_t x, uint64_t y) // 1 mulx +{ + __extension__ using uint128_t = unsigned __int128; + + const uint128_t p = uint128_t{x} * y; + + const uint64_t hi = static_cast(p >> 64); + const uint64_t lo = static_cast(p); + return {hi, lo}; +} +#elif defined(_MSC_VER) && defined(_M_X64) +static inline uint64x2 Mul128(uint64_t x, uint64_t y) +{ + uint64_t hi = 0; + uint64_t lo = _umul128(x, y, &hi); + return {hi, lo}; +} +#else +static inline uint32_t Lo32(uint64_t x) +{ + return static_cast(x); +} + +static inline uint32_t Hi32(uint64_t x) +{ + return static_cast(x >> 32); +} + +static inline uint64x2 Mul128(uint64_t a, uint64_t b) +{ + const uint64_t b00 = uint64_t{Lo32(a)} * Lo32(b); + const uint64_t b01 = uint64_t{Lo32(a)} * Hi32(b); + const uint64_t b10 = uint64_t{Hi32(a)} * Lo32(b); + const uint64_t b11 = uint64_t{Hi32(a)} * Hi32(b); + + const uint64_t mid1 = b10 + Hi32(b00); + const uint64_t mid2 = b01 + Lo32(mid1); + + const uint64_t hi = b11 + Hi32(mid1) + Hi32(mid2); + const uint64_t lo = Lo32(b00) | uint64_t{Lo32(mid2)} << 32; + return {hi, lo}; +} +#endif + +// Returns (x * y) / 2^128 +static inline uint64_t MulShift(uint64_t x, uint64x2 y) // 2 mulx +{ + uint64x2 p1 = Mul128(x, y.hi); + uint64x2 p0 = Mul128(x, y.lo); + p1.lo += p0.hi; + p1.hi += p1.lo < p0.hi; + return p1.hi; +} + +static inline bool MulParity(uint64_t two_f, uint64x2 pow10, int32_t beta_minus_1) // 1 mulx, 1 mul +{ + DRAGONBOX_ASSERT(beta_minus_1 >= 1); + DRAGONBOX_ASSERT(beta_minus_1 <= 63); + + const uint64_t p01 = two_f * pow10.hi; + const uint64_t p10 = Mul128(two_f, pow10.lo).hi; + const uint64_t mid = p01 + p10; + + return (mid & (uint64_t{1} << (64 - beta_minus_1))) != 0; +} + +static inline bool IsIntegralEndpoint(uint64_t two_f, int32_t e2, int32_t minus_k) +{ + if (e2 < -2) + return false; + if (e2 <= 9) + return true; + if (e2 <= 86) + return MultipleOfPow5(two_f, minus_k); + + return false; +} + +static inline bool IsIntegralMidpoint(uint64_t two_f, int32_t e2, int32_t minus_k) +{ + if (e2 < -4) + return MultipleOfPow2(two_f, minus_k - e2 + 1); + if (e2 <= 9) + return true; + if (e2 <= 86) + return MultipleOfPow5(two_f, minus_k); + + return false; +} + +static inline FloatingDecimal64 ToDecimal64(const uint64_t ieee_significand, const uint64_t ieee_exponent) +{ + static constexpr int32_t Kappa = 2; + static constexpr uint32_t BigDivisor = 1000; // 10^(kappa + 1) + static constexpr uint32_t SmallDivisor = 100; // 10^(kappa) + + // + // Step 1: + // integer promotion & Schubfach multiplier calculation. + // + + uint64_t m2; + int32_t e2; + if (ieee_exponent != 0) + { + m2 = Double::HiddenBit | ieee_significand; + e2 = static_cast(ieee_exponent) - Double::ExponentBias; + + if /*unlikely*/ (0 <= -e2 && -e2 < Double::SignificandSize && MultipleOfPow2(m2, -e2)) + { + // Small integer. + return {m2 >> -e2, 0}; + } + + if /*unlikely*/ (ieee_significand == 0 && ieee_exponent > 1) + { + // Shorter interval case; proceed like Schubfach. + return ToDecimal64_asymmetric_interval(e2); + } + } + else + { + // Subnormal case; interval is always regular. + m2 = ieee_significand; + e2 = 1 - Double::ExponentBias; + } + + const bool is_even = (m2 % 2 == 0); + const bool accept_lower = is_even; + const bool accept_upper = is_even; + + // Compute k and beta. + const int32_t minus_k = FloorLog10Pow2(e2) - Kappa; + const int32_t beta_minus_1 = e2 + FloorLog2Pow10(-minus_k); + DRAGONBOX_ASSERT(beta_minus_1 >= 6); + DRAGONBOX_ASSERT(beta_minus_1 <= 9); + + const uint64x2 pow10 = ComputePow10(-minus_k); + + // Compute delta + // 10^kappa <= delta < 10^(kappa + 1) + // 100 <= delta < 1000 + const uint32_t delta = ComputeDelta(pow10, beta_minus_1); + DRAGONBOX_ASSERT(delta >= SmallDivisor); + DRAGONBOX_ASSERT(delta < BigDivisor ); + + const uint64_t two_fl = 2 * m2 - 1; + const uint64_t two_fc = 2 * m2; + const uint64_t two_fr = 2 * m2 + 1; // (54 bits) + + // Compute zi + // (54 + 9 = 63 bits) + const uint64_t zi = MulShift(two_fr << beta_minus_1, pow10); // 2 mulx + + // + // Step 2: + // Try larger divisor. + // + + uint64_t q = zi / BigDivisor; +// uint64_t q = Mul128(zi, 0x83126E978D4FDF3Cu).hi >> 9; // 1 mulx + uint32_t r = static_cast(zi) - BigDivisor * static_cast(q); // r = zi % BigDivisor + // 0 <= r < 1000 + + if /*likely ~50% ?!*/ (r < delta) + { + // Exclude the right endpoint if necessary + if /*likely*/ (r != 0 || accept_upper || !IsIntegralEndpoint(two_fr, e2, minus_k)) + { + return {q, minus_k + Kappa + 1}; + } + + DRAGONBOX_ASSERT(q != 0); + --q; + r = BigDivisor; + } + else if /*unlikely*/ (r == delta) + { + // Compare fractional parts. + // Check conditions in the order different from the paper + // to take advantage of short-circuiting + if ((accept_lower && IsIntegralEndpoint(two_fl, e2, minus_k)) || MulParity(two_fl, pow10, beta_minus_1)) // 1 mulx, 1 mul + { + return {q, minus_k + Kappa + 1}; + } + } + else /*likely ~50% ?!*/ // (r > deltai) + { + } + + // + // Step 3: + // Find the significand with the smaller divisor + // + + q *= 10; // 1 hmul + + // 0 <= r <= 1000 + + const uint32_t dist = r - (delta / 2) + (SmallDivisor / 2); + + const uint32_t dist_q = dist / 100; // 1 mul +// const uint32_t dist_r = dist % 100; + q += dist_q; + +// if /*likely*/ (dist_r == 0) + if /*likely*/ (dist == dist_q * 100) // 1 mul32 + { +// const bool approx_y_parity = ((dist ^ (SmallDivisor / 2)) & 1) != 0; + const bool approx_y_parity = (dist & 1) != 0; + + // Check z^(f) >= epsilon^(f) + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + // Since there are only 2 possibilities, we only need to care about the + // parity. Also, zi and r should have the same parity since the divisor + // is an even number + if /*likely*/ (MulParity(two_fc, pow10, beta_minus_1) != approx_y_parity) // 1 mulx, 1 mul + { + --q; + } + // If z^(f) >= epsilon^(f), we might have a tie + // when z^(f) == epsilon^(f), or equivalently, when y is an integer + else if (q % 2 != 0 && IsIntegralMidpoint(two_fc, e2, minus_k)) + { + --q; + } + } + + return {q, minus_k + Kappa}; +} + +//================================================================================================== +// ToChars +//================================================================================================== + +static inline void Utoa_2Digits(char* buf, uint32_t digits) +{ + static constexpr char Digits100[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9', + }; + + DRAGONBOX_ASSERT(digits <= 99); + std::memcpy(buf, &Digits100[2 * digits], 2 * sizeof(char)); +} + +static inline int32_t TrailingZeros_2Digits(uint32_t digits) +{ + static constexpr int8_t TrailingZeros100[100] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + DRAGONBOX_ASSERT(digits <= 99); + return TrailingZeros100[digits]; +} + +static inline int32_t Utoa_8Digits_skip_trailing_zeros(char* buf, uint32_t digits) +{ + DRAGONBOX_ASSERT(digits >= 1); + DRAGONBOX_ASSERT(digits <= 99999999); + + const uint32_t q = digits / 10000; + const uint32_t r = digits % 10000; + + const uint32_t qH = q / 100; + const uint32_t qL = q % 100; + Utoa_2Digits(buf + 0, qH); + Utoa_2Digits(buf + 2, qL); + + if (r == 0) + { + return TrailingZeros_2Digits(qL == 0 ? qH : qL) + (qL == 0 ? 6 : 4); + } + else + { + const uint32_t rH = r / 100; + const uint32_t rL = r % 100; + Utoa_2Digits(buf + 4, rH); + Utoa_2Digits(buf + 6, rL); + + return TrailingZeros_2Digits(rL == 0 ? rH : rL) + (rL == 0 ? 2 : 0); + } +} + +static inline int32_t PrintDecimalDigitsBackwards(char* buf, uint64_t output64) +{ + int32_t tz = 0; // number of trailing zeros removed. + int32_t nd = 0; // number of decimal digits processed. + + // At most 17 digits remaining + + if (output64 >= 100000000) + { + const uint64_t q = output64 / 100000000; + const uint32_t r = static_cast(output64 % 100000000); + output64 = q; + buf -= 8; + if (r != 0) + { + tz = Utoa_8Digits_skip_trailing_zeros(buf, r); + DRAGONBOX_ASSERT(tz >= 0); + DRAGONBOX_ASSERT(tz <= 7); + } + else + { + tz = 8; + } + nd = 8; + } + + // At most 9 digits remaining. + DRAGONBOX_ASSERT(output64 <= UINT32_MAX); + uint32_t output = static_cast(output64); + + if (output >= 10000) + { + const uint32_t q = output / 10000; + const uint32_t r = output % 10000; + output = q; + buf -= 4; + if (r != 0) + { + const uint32_t rH = r / 100; + const uint32_t rL = r % 100; + Utoa_2Digits(buf + 0, rH); + Utoa_2Digits(buf + 2, rL); + if (tz == nd) + { + tz += TrailingZeros_2Digits(rL == 0 ? rH : rL) + (rL == 0 ? 2 : 0); + } + } + else + { + if (tz == nd) + tz += 4; + else + std::memset(buf, '0', 4); // (actually not required...) + } + nd += 4; + } + + // At most 5 digits remaining. + + if (output >= 100) + { + const uint32_t q = output / 100; + const uint32_t r = output % 100; + output = q; + buf -= 2; + Utoa_2Digits(buf, r); + if (tz == nd) + { + tz += TrailingZeros_2Digits(r); + } + nd += 2; + + if (output >= 100) + { + const uint32_t q2 = output / 100; + const uint32_t r2 = output % 100; + output = q2; + buf -= 2; + Utoa_2Digits(buf, r2); + if (tz == nd) + { + tz += TrailingZeros_2Digits(r2); + } + nd += 2; + } + } + + // At most 2 digits remaining. + + DRAGONBOX_ASSERT(output >= 1); + DRAGONBOX_ASSERT(output <= 99); + + if (output >= 10) + { + const uint32_t q = output; + buf -= 2; + Utoa_2Digits(buf, q); + if (tz == nd) + { + tz += TrailingZeros_2Digits(q); + } +// nd += 2; + } + else + { + const uint32_t q = output; + DRAGONBOX_ASSERT(q >= 1); + DRAGONBOX_ASSERT(q <= 9); + *--buf = static_cast('0' + q); + } + + return tz; +} + +static inline int32_t DecimalLength(uint64_t v) +{ + DRAGONBOX_ASSERT(v >= 1); + DRAGONBOX_ASSERT(v <= 99999999999999999ull); + + if (static_cast(v >> 32) != 0) + { + if (v >= 10000000000000000ull) { return 17; } + if (v >= 1000000000000000ull) { return 16; } + if (v >= 100000000000000ull) { return 15; } + if (v >= 10000000000000ull) { return 14; } + if (v >= 1000000000000ull) { return 13; } + if (v >= 100000000000ull) { return 12; } + if (v >= 10000000000ull) { return 11; } + return 10; + } + + const uint32_t v32 = static_cast(v); + if (v32 >= 1000000000u) { return 10; } + if (v32 >= 100000000u) { return 9; } + if (v32 >= 10000000u) { return 8; } + if (v32 >= 1000000u) { return 7; } + if (v32 >= 100000u) { return 6; } + if (v32 >= 10000u) { return 5; } + if (v32 >= 1000u) { return 4; } + if (v32 >= 100u) { return 3; } + if (v32 >= 10u) { return 2; } + return 1; +} + +static inline char* FormatDigits(char* buffer, uint64_t digits, int32_t decimal_exponent, bool force_trailing_dot_zero = false) +{ + static constexpr int32_t MinFixedDecimalPoint = -6; + static constexpr int32_t MaxFixedDecimalPoint = 17; + static_assert(MinFixedDecimalPoint <= -1, "internal error"); + static_assert(MaxFixedDecimalPoint >= 17, "internal error"); + + DRAGONBOX_ASSERT(digits >= 1); + DRAGONBOX_ASSERT(digits <= 99999999999999999ull); + DRAGONBOX_ASSERT(decimal_exponent >= -999); + DRAGONBOX_ASSERT(decimal_exponent <= 999); + + int32_t num_digits = DecimalLength(digits); + const int32_t decimal_point = num_digits + decimal_exponent; + + const bool use_fixed = MinFixedDecimalPoint <= decimal_point && decimal_point <= MaxFixedDecimalPoint; + + // Prepare the buffer. + // Avoid calling memset/memcpy with variable arguments below... + + std::memset(buffer + 0, '0', 16); + std::memset(buffer + 16, '0', 16); + static_assert(MinFixedDecimalPoint >= -30, "internal error"); + static_assert(MaxFixedDecimalPoint <= 32, "internal error"); + + int32_t decimal_digits_position; + if (use_fixed) + { + if (decimal_point <= 0) + { + // 0.[000]digits + decimal_digits_position = 2 - decimal_point; + } + else + { + // dig.its + // digits[000] + decimal_digits_position = 0; + } + } + else + { + // dE+123 or d.igitsE+123 + decimal_digits_position = 1; + } + + char* digits_end = buffer + decimal_digits_position + num_digits; + + const int32_t tz = PrintDecimalDigitsBackwards(digits_end, digits); + digits_end -= tz; + num_digits -= tz; +// decimal_exponent += tz; // => decimal_point unchanged. + + if (use_fixed) + { + if (decimal_point <= 0) + { + // 0.[000]digits + buffer[1] = '.'; + buffer = digits_end; + } + else if (decimal_point < num_digits) + { + // dig.its +#if defined(_MSC_VER) && !defined(__clang__) + // VC does not inline the memmove call below. (Even if compiled with /arch:AVX2.) + // However, memcpy will be inlined. + uint8_t tmp[16]; + char* const src = buffer + decimal_point; + char* const dst = src + 1; + std::memcpy(tmp, src, 16); + std::memcpy(dst, tmp, 16); +#else + std::memmove(buffer + decimal_point + 1, buffer + decimal_point, 16); +#endif + buffer[decimal_point] = '.'; + buffer = digits_end + 1; + } + else + { + // digits[000] + buffer += decimal_point; + if (force_trailing_dot_zero) + { + std::memcpy(buffer, ".0", 2); + buffer += 2; + } + } + } + else + { + // Copy the first digit one place to the left. + buffer[0] = buffer[1]; + if (num_digits == 1) + { + // dE+123 + ++buffer; + } + else + { + // d.igitsE+123 + buffer[1] = '.'; + buffer = digits_end; + } + + const int32_t scientific_exponent = decimal_point - 1; +// SF_ASSERT(scientific_exponent != 0); + + std::memcpy(buffer, scientific_exponent < 0 ? "e-" : "e+", 2); + buffer += 2; + + const uint32_t k = static_cast(scientific_exponent < 0 ? -scientific_exponent : scientific_exponent); + if (k < 10) + { + *buffer++ = static_cast('0' + k); + } + else if (k < 100) + { + Utoa_2Digits(buffer, k); + buffer += 2; + } + else + { + const uint32_t q = k / 100; + const uint32_t r = k % 100; + *buffer++ = static_cast('0' + q); + Utoa_2Digits(buffer, r); + buffer += 2; + } + } + + return buffer; +} + +static inline char* ToChars(char* buffer, double value, bool force_trailing_dot_zero = false) +{ + const Double v(value); + + const uint64_t significand = v.PhysicalSignificand(); + const uint64_t exponent = v.PhysicalExponent(); + + if (exponent != Double::MaxIeeeExponent) // [[likely]] + { + // Finite + + buffer[0] = '-'; + buffer += v.SignBit(); + + if (exponent != 0 || significand != 0) // [[likely]] + { + // != 0 + + const auto dec = ToDecimal64(significand, exponent); + return FormatDigits(buffer, dec.significand, dec.exponent, force_trailing_dot_zero); + } + else + { + std::memcpy(buffer, "0.0 ", 4); + buffer += force_trailing_dot_zero ? 3 : 1; + return buffer; + } + } + + if (significand == 0) + { + buffer[0] = '-'; + buffer += v.SignBit(); + + std::memcpy(buffer, "inf ", 4); + return buffer + 3; + } + else + { + std::memcpy(buffer, "nan ", 4); + return buffer + 3; + } +} + +//================================================================================================== +// +//================================================================================================== + +char* dragonbox::Dtoa(char* buffer, double value) +{ + return ToChars(buffer, value); +} diff --git a/lib/vendor/drachennest/dragonbox.h b/lib/vendor/drachennest/dragonbox.h new file mode 100644 index 000000000000..3c8784600d83 --- /dev/null +++ b/lib/vendor/drachennest/dragonbox.h @@ -0,0 +1,31 @@ +// Copyright 2020 Alexander Bolz +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +namespace dragonbox { + +// char* output_end = Dtoa(buffer, value); +// +// Converts the given double-precision number into decimal form and stores the result in the given +// buffer. +// +// The buffer must be large enough, i.e. >= DtoaMinBufferLength. +// The output format is similar to printf("%g"). +// The output is _not_ null-terminted. +// +// The output is optimal, i.e. the output string +// 1. rounds back to the input number when read in (using round-to-nearest-even) +// 2. is as short as possible, +// 3. is as close to the input number as possible. +// +// Note: +// This function may temporarily write up to DtoaMinBufferLength characters into the buffer. + +constexpr int DtoaMinBufferLength = 64; + +char* Dtoa(char* buffer, double value); + +} // namespace dragonbox diff --git a/lib/vendor/drachennest/impl.cc b/lib/vendor/drachennest/impl.cc new file mode 100644 index 000000000000..11256ecf7c75 --- /dev/null +++ b/lib/vendor/drachennest/impl.cc @@ -0,0 +1,15 @@ +#include "schubfach_32.h" +#include "schubfach_64.h" +#include "dragonbox.h" + +extern "C" char* nimtoStringDragonboxImplDouble(char* buffer, double value){ + return dragonbox::Dtoa(buffer, value); +} + +extern "C" char* nimSchubfachFtoa(char* buffer, float value){ + return schubfach::Ftoa(buffer, value); +} + +extern "C" char* nimSchubfachDtoa(char* buffer, double value){ + return schubfach::Dtoa(buffer, value); +} diff --git a/lib/vendor/drachennest/readme.md b/lib/vendor/drachennest/readme.md new file mode 100644 index 000000000000..f89c8ab6ab44 --- /dev/null +++ b/lib/vendor/drachennest/readme.md @@ -0,0 +1,3 @@ +# files +LICENSE.txt: copied from https://github.com/abolz/Drachennest/blob/master/LICENSE +dragonbox.*,schubfach_32.*: adapted from https://github.com/abolz/Drachennest/blob/master/src/dragonbox.cc diff --git a/lib/vendor/drachennest/schubfach_32.cc b/lib/vendor/drachennest/schubfach_32.cc new file mode 100644 index 000000000000..1a27d9434839 --- /dev/null +++ b/lib/vendor/drachennest/schubfach_32.cc @@ -0,0 +1,699 @@ +// Copyright 2020 Alexander Bolz +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +#include "schubfach_32.h" + +//-------------------------------------------------------------------------------------------------- +// This file contains an implementation of the Schubfach algorithm as described in +// +// [1] Raffaello Giulietti, "The Schubfach way to render doubles", +// https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN +//-------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#if _MSC_VER +#include +#endif + +#ifndef SF_ASSERT +#define SF_ASSERT(X) assert(X) +#endif + +//================================================================================================== +// +//================================================================================================== + +template +static inline Dest ReinterpretBits(Source source) +{ + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + + Dest dest; + std::memcpy(&dest, &source, sizeof(Source)); + return dest; +} + +namespace { +struct Single +{ + static_assert(std::numeric_limits::is_iec559 + && std::numeric_limits::digits == 24 + && std::numeric_limits::max_exponent == 128, + "IEEE-754 single-precision implementation required"); + + using value_type = float; + using bits_type = uint32_t; + +// static constexpr int32_t MaxDigits10 = std::numeric_limits::max_digits10; + static constexpr int32_t SignificandSize = std::numeric_limits::digits; // = p (includes the hidden bit) + static constexpr int32_t ExponentBias = std::numeric_limits::max_exponent - 1 + (SignificandSize - 1); +// static constexpr int32_t MaxExponent = std::numeric_limits::max_exponent - 1 - (SignificandSize - 1); +// static constexpr int32_t MinExponent = std::numeric_limits::min_exponent - 1 - (SignificandSize - 1); + static constexpr bits_type MaxIeeeExponent = bits_type{2 * std::numeric_limits::max_exponent - 1}; + static constexpr bits_type HiddenBit = bits_type{1} << (SignificandSize - 1); // = 2^(p-1) + static constexpr bits_type SignificandMask = HiddenBit - 1; // = 2^(p-1) - 1 + static constexpr bits_type ExponentMask = MaxIeeeExponent << (SignificandSize - 1); + static constexpr bits_type SignMask = ~(~bits_type{0} >> 1); + + bits_type bits; + + explicit Single(bits_type bits_) : bits(bits_) {} + explicit Single(value_type value) : bits(ReinterpretBits(value)) {} + + bits_type PhysicalSignificand() const { + return bits & SignificandMask; + } + + bits_type PhysicalExponent() const { + return (bits & ExponentMask) >> (SignificandSize - 1); + } + + bool IsFinite() const { + return (bits & ExponentMask) != ExponentMask; + } + + bool IsInf() const { + return (bits & ExponentMask) == ExponentMask && (bits & SignificandMask) == 0; + } + + bool IsNaN() const { + return (bits & ExponentMask) == ExponentMask && (bits & SignificandMask) != 0; + } + + bool IsZero() const { + return (bits & ~SignMask) == 0; + } + + bool SignBit() const { + return (bits & SignMask) != 0; + } +}; +} // namespace + +//================================================================================================== +// +//================================================================================================== + +// Returns floor(x / 2^n). +// +// Technically, right-shift of negative integers is implementation defined... +// Should easily be optimized into SAR (or equivalent) instruction. +static inline int32_t FloorDivPow2(int32_t x, int32_t n) +{ +#if 0 + return x < 0 ? ~(~x >> n) : (x >> n); +#else + return x >> n; +#endif +} + +// Returns floor(log_10(2^e)) +// static inline int32_t FloorLog10Pow2(int32_t e) +// { +// SF_ASSERT(e >= -1500); +// SF_ASSERT(e <= 1500); +// return FloorDivPow2(e * 1262611, 22); +// } + +// Returns floor(log_10(3/4 2^e)) +// static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e) +// { +// SF_ASSERT(e >= -1500); +// SF_ASSERT(e <= 1500); +// return FloorDivPow2(e * 1262611 - 524031, 22); +// } + +// Returns floor(log_2(10^e)) +static inline int32_t FloorLog2Pow10(int32_t e) +{ + SF_ASSERT(e >= -1233); + SF_ASSERT(e <= 1233); + return FloorDivPow2(e * 1741647, 19); +} + +//================================================================================================== +// +//================================================================================================== + +static inline uint64_t ComputePow10(int32_t k) +{ + // There are unique beta and r such that 10^k = beta 2^r and + // 2^63 <= beta < 2^64, namely r = floor(log_2 10^k) - 63 and + // beta = 2^-r 10^k. + // Let g = ceil(beta), so (g-1) 2^r < 10^k <= g 2^r, with the latter + // value being a pretty good overestimate for 10^k. + + // NB: Since for all the required exponents k, we have g < 2^64, + // all constants can be stored in 128-bit integers. + + static constexpr int32_t kMin = -31; + static constexpr int32_t kMax = 45; + static constexpr uint64_t g[kMax - kMin + 1] = { + 0x81CEB32C4B43FCF5, // -31 + 0xA2425FF75E14FC32, // -30 + 0xCAD2F7F5359A3B3F, // -29 + 0xFD87B5F28300CA0E, // -28 + 0x9E74D1B791E07E49, // -27 + 0xC612062576589DDB, // -26 + 0xF79687AED3EEC552, // -25 + 0x9ABE14CD44753B53, // -24 + 0xC16D9A0095928A28, // -23 + 0xF1C90080BAF72CB2, // -22 + 0x971DA05074DA7BEF, // -21 + 0xBCE5086492111AEB, // -20 + 0xEC1E4A7DB69561A6, // -19 + 0x9392EE8E921D5D08, // -18 + 0xB877AA3236A4B44A, // -17 + 0xE69594BEC44DE15C, // -16 + 0x901D7CF73AB0ACDA, // -15 + 0xB424DC35095CD810, // -14 + 0xE12E13424BB40E14, // -13 + 0x8CBCCC096F5088CC, // -12 + 0xAFEBFF0BCB24AAFF, // -11 + 0xDBE6FECEBDEDD5BF, // -10 + 0x89705F4136B4A598, // -9 + 0xABCC77118461CEFD, // -8 + 0xD6BF94D5E57A42BD, // -7 + 0x8637BD05AF6C69B6, // -6 + 0xA7C5AC471B478424, // -5 + 0xD1B71758E219652C, // -4 + 0x83126E978D4FDF3C, // -3 + 0xA3D70A3D70A3D70B, // -2 + 0xCCCCCCCCCCCCCCCD, // -1 + 0x8000000000000000, // 0 + 0xA000000000000000, // 1 + 0xC800000000000000, // 2 + 0xFA00000000000000, // 3 + 0x9C40000000000000, // 4 + 0xC350000000000000, // 5 + 0xF424000000000000, // 6 + 0x9896800000000000, // 7 + 0xBEBC200000000000, // 8 + 0xEE6B280000000000, // 9 + 0x9502F90000000000, // 10 + 0xBA43B74000000000, // 11 + 0xE8D4A51000000000, // 12 + 0x9184E72A00000000, // 13 + 0xB5E620F480000000, // 14 + 0xE35FA931A0000000, // 15 + 0x8E1BC9BF04000000, // 16 + 0xB1A2BC2EC5000000, // 17 + 0xDE0B6B3A76400000, // 18 + 0x8AC7230489E80000, // 19 + 0xAD78EBC5AC620000, // 20 + 0xD8D726B7177A8000, // 21 + 0x878678326EAC9000, // 22 + 0xA968163F0A57B400, // 23 + 0xD3C21BCECCEDA100, // 24 + 0x84595161401484A0, // 25 + 0xA56FA5B99019A5C8, // 26 + 0xCECB8F27F4200F3A, // 27 + 0x813F3978F8940985, // 28 + 0xA18F07D736B90BE6, // 29 + 0xC9F2C9CD04674EDF, // 30 + 0xFC6F7C4045812297, // 31 + 0x9DC5ADA82B70B59E, // 32 + 0xC5371912364CE306, // 33 + 0xF684DF56C3E01BC7, // 34 + 0x9A130B963A6C115D, // 35 + 0xC097CE7BC90715B4, // 36 + 0xF0BDC21ABB48DB21, // 37 + 0x96769950B50D88F5, // 38 + 0xBC143FA4E250EB32, // 39 + 0xEB194F8E1AE525FE, // 40 + 0x92EFD1B8D0CF37BF, // 41 + 0xB7ABC627050305AE, // 42 + 0xE596B7B0C643C71A, // 43 + 0x8F7E32CE7BEA5C70, // 44 + 0xB35DBF821AE4F38C, // 45 + }; + + SF_ASSERT(k >= kMin); + SF_ASSERT(k <= kMax); + return g[static_cast(k - kMin)]; +} + +static inline uint32_t Lo32(uint64_t x) +{ + return static_cast(x); +} + +static inline uint32_t Hi32(uint64_t x) +{ + return static_cast(x >> 32); +} + +#if defined(__SIZEOF_INT128__) + +static inline uint32_t RoundToOdd(uint64_t g, uint32_t cp) +{ + __extension__ using uint128_t = unsigned __int128; + + const uint128_t p = uint128_t{g} * cp; + + const uint32_t y1 = Lo32(static_cast(p >> 64)); + const uint32_t y0 = Hi32(static_cast(p)); + + return y1 | (y0 > 1); +} + +#elif defined(_MSC_VER) && defined(_M_X64) + +static inline uint32_t RoundToOdd(uint64_t g, uint32_t cpHi) +{ + uint64_t p1 = 0; + uint64_t p0 = _umul128(g, cpHi, &p1); + + const uint32_t y1 = Lo32(p1); + const uint32_t y0 = Hi32(p0); + + return y1 | (y0 > 1); +} + +#else + +static inline uint32_t RoundToOdd(uint64_t g, uint32_t cp) +{ + const uint64_t b01 = uint64_t{Lo32(g)} * cp; + const uint64_t b11 = uint64_t{Hi32(g)} * cp; + const uint64_t hi = b11 + Hi32(b01); + + const uint32_t y1 = Hi32(hi); + const uint32_t y0 = Lo32(hi); + + return y1 | (y0 > 1); +} + +#endif + +// Returns whether value is divisible by 2^e2 +static inline bool MultipleOfPow2(uint32_t value, int32_t e2) +{ + SF_ASSERT(e2 >= 0); + SF_ASSERT(e2 <= 31); + return (value & ((uint32_t{1} << e2) - 1)) == 0; +} + +namespace { +struct FloatingDecimal32 { + uint32_t digits; // num_digits <= 9 + int32_t exponent; +}; +} + +static inline FloatingDecimal32 ToDecimal32(uint32_t ieee_significand, uint32_t ieee_exponent) +{ + uint32_t c; + int32_t q; + if (ieee_exponent != 0) + { + c = Single::HiddenBit | ieee_significand; + q = static_cast(ieee_exponent) - Single::ExponentBias; + + if (0 <= -q && -q < Single::SignificandSize && MultipleOfPow2(c, -q)) + { + return {c >> -q, 0}; + } + } + else + { + c = ieee_significand; + q = 1 - Single::ExponentBias; + } + + const bool is_even = (c % 2 == 0); + const bool accept_lower = is_even; + const bool accept_upper = is_even; + + const bool lower_boundary_is_closer = (ieee_significand == 0 && ieee_exponent > 1); + +// const int32_t qb = q - 2; + const uint32_t cbl = 4 * c - 2 + lower_boundary_is_closer; + const uint32_t cb = 4 * c; + const uint32_t cbr = 4 * c + 2; + + // (q * 1262611 ) >> 22 == floor(log_10( 2^q)) + // (q * 1262611 - 524031) >> 22 == floor(log_10(3/4 2^q)) + SF_ASSERT(q >= -1500); + SF_ASSERT(q <= 1500); + const int32_t k = FloorDivPow2(q * 1262611 - (lower_boundary_is_closer ? 524031 : 0), 22); + + const int32_t h = q + FloorLog2Pow10(-k) + 1; + SF_ASSERT(h >= 1); + SF_ASSERT(h <= 4); + + const uint64_t pow10 = ComputePow10(-k); + const uint32_t vbl = RoundToOdd(pow10, cbl << h); + const uint32_t vb = RoundToOdd(pow10, cb << h); + const uint32_t vbr = RoundToOdd(pow10, cbr << h); + + const uint32_t lower = vbl + !accept_lower; + const uint32_t upper = vbr - !accept_upper; + + // See Figure 4 in [1]. + // And the modifications in Figure 6. + + const uint32_t s = vb / 4; // NB: 4 * s == vb & ~3 == vb & -4 + + if (s >= 10) // vb >= 40 + { + const uint32_t sp = s / 10; // = vb / 40 + const bool up_inside = lower <= 40 * sp; + const bool wp_inside = 40 * sp + 40 <= upper; +// if (up_inside || wp_inside) // NB: At most one of u' and w' is in R_v. + if (up_inside != wp_inside) + { + return {sp + wp_inside, k + 1}; + } + } + + const bool u_inside = lower <= 4 * s; + const bool w_inside = 4 * s + 4 <= upper; + if (u_inside != w_inside) + { + return {s + w_inside, k}; + } + + // NB: s & 1 == vb & 0x4 + const uint32_t mid = 4 * s + 2; // = 2(s + t) + const bool round_up = vb > mid || (vb == mid && (s & 1) != 0); + + return {s + round_up, k}; +} + +//================================================================================================== +// ToChars +//================================================================================================== + +static inline void Utoa_2Digits(char* buf, uint32_t digits) +{ + static constexpr char Digits100[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9', + }; + + SF_ASSERT(digits <= 99); + std::memcpy(buf, &Digits100[2 * digits], 2 * sizeof(char)); +} + +static inline int TrailingZeros_2Digits(uint32_t digits) +{ + static constexpr int8_t TrailingZeros100[100] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + SF_ASSERT(digits <= 99); + return TrailingZeros100[digits]; +} + +static inline int PrintDecimalDigitsBackwards(char* buf, uint32_t output) +{ + int tz = 0; // number of trailing zeros removed. + int nd = 0; // number of decimal digits processed. + + // At most 9 digits remaining + + if (output >= 10000) + { + const uint32_t q = output / 10000; + const uint32_t r = output % 10000; + output = q; + buf -= 4; + if (r != 0) + { + const uint32_t rH = r / 100; + const uint32_t rL = r % 100; + Utoa_2Digits(buf + 0, rH); + Utoa_2Digits(buf + 2, rL); + + tz = TrailingZeros_2Digits(rL == 0 ? rH : rL) + (rL == 0 ? 2 : 0); + } + else + { + tz = 4; + } + nd = 4; + } + + // At most 5 digits remaining. + + if (output >= 100) + { + const uint32_t q = output / 100; + const uint32_t r = output % 100; + output = q; + buf -= 2; + Utoa_2Digits(buf, r); + if (tz == nd) + { + tz += TrailingZeros_2Digits(r); + } + nd += 2; + + if (output >= 100) + { + const uint32_t q2 = output / 100; + const uint32_t r2 = output % 100; + output = q2; + buf -= 2; + Utoa_2Digits(buf, r2); + if (tz == nd) + { + tz += TrailingZeros_2Digits(r2); + } + nd += 2; + } + } + + // At most 2 digits remaining. + + SF_ASSERT(output >= 1); + SF_ASSERT(output <= 99); + + if (output >= 10) + { + const uint32_t q = output; + buf -= 2; + Utoa_2Digits(buf, q); + if (tz == nd) + { + tz += TrailingZeros_2Digits(q); + } +// nd += 2; + } + else + { + const uint32_t q = output; + SF_ASSERT(q >= 1); + SF_ASSERT(q <= 9); + *--buf = static_cast('0' + q); + } + + return tz; +} + +static inline int32_t DecimalLength(uint32_t v) +{ + SF_ASSERT(v >= 1); + SF_ASSERT(v <= 999999999u); + + if (v >= 100000000u) { return 9; } + if (v >= 10000000u) { return 8; } + if (v >= 1000000u) { return 7; } + if (v >= 100000u) { return 6; } + if (v >= 10000u) { return 5; } + if (v >= 1000u) { return 4; } + if (v >= 100u) { return 3; } + if (v >= 10u) { return 2; } + return 1; +} + +static inline char* FormatDigits(char* buffer, uint32_t digits, int32_t decimal_exponent, bool force_trailing_dot_zero = false) +{ + static constexpr int32_t MinFixedDecimalPoint = -4; + static constexpr int32_t MaxFixedDecimalPoint = 9; + static_assert(MinFixedDecimalPoint <= -1, "internal error"); + static_assert(MaxFixedDecimalPoint >= 9, "internal error"); + + SF_ASSERT(digits >= 1); + SF_ASSERT(digits <= 999999999u); + SF_ASSERT(decimal_exponent >= -99); + SF_ASSERT(decimal_exponent <= 99); + + int32_t num_digits = DecimalLength(digits); + const int32_t decimal_point = num_digits + decimal_exponent; + + const bool use_fixed = MinFixedDecimalPoint <= decimal_point && decimal_point <= MaxFixedDecimalPoint; + + // Prepare the buffer. + // Avoid calling memset/memcpy with variable arguments below... + + std::memset(buffer, '0', 16); + static_assert(MinFixedDecimalPoint >= -14, "internal error"); + static_assert(MaxFixedDecimalPoint <= 16, "internal error"); + + int32_t decimal_digits_position; + if (use_fixed) + { + if (decimal_point <= 0) + { + // 0.[000]digits + decimal_digits_position = 2 - decimal_point; + } + else + { + // dig.its + // digits[000] + decimal_digits_position = 0; + } + } + else + { + // dE+123 or d.igitsE+123 + decimal_digits_position = 1; + } + + char* digits_end = buffer + decimal_digits_position + num_digits; + + const int tz = PrintDecimalDigitsBackwards(digits_end, digits); + digits_end -= tz; + num_digits -= tz; +// decimal_exponent += tz; // => decimal_point unchanged. + + if (use_fixed) + { + if (decimal_point <= 0) + { + // 0.[000]digits + buffer[1] = '.'; + buffer = digits_end; + } + else if (decimal_point < num_digits) + { + // dig.its + std::memmove(buffer + decimal_point + 1, buffer + decimal_point, 8); + buffer[decimal_point] = '.'; + buffer = digits_end + 1; + } + else + { + // digits[000] + buffer += decimal_point; + if (force_trailing_dot_zero) + { + std::memcpy(buffer, ".0", 2); + buffer += 2; + } + } + } + else + { + buffer[0] = buffer[1]; + if (num_digits == 1) + { + // dE+123 + ++buffer; + } + else + { + // d.igitsE+123 + buffer[1] = '.'; + buffer = digits_end; + } + + const int32_t scientific_exponent = decimal_point - 1; +// SF_ASSERT(scientific_exponent != 0); + + std::memcpy(buffer, scientific_exponent < 0 ? "e-" : "e+", 2); + buffer += 2; + + const uint32_t k = static_cast(scientific_exponent < 0 ? -scientific_exponent : scientific_exponent); + if (k < 10) + { + *buffer++ = static_cast('0' + k); + } + else + { + Utoa_2Digits(buffer, k); + buffer += 2; + } + } + + return buffer; +} + +static inline char* ToChars(char* buffer, float value, bool force_trailing_dot_zero = false) +{ + const Single v(value); + + const uint32_t significand = v.PhysicalSignificand(); + const uint32_t exponent = v.PhysicalExponent(); + + if (exponent != Single::MaxIeeeExponent) // [[likely]] + { + // Finite + + buffer[0] = '-'; + buffer += v.SignBit(); + + if (exponent != 0 || significand != 0) // [[likely]] + { + // != 0 + + const auto dec = ToDecimal32(significand, exponent); + return FormatDigits(buffer, dec.digits, dec.exponent, force_trailing_dot_zero); + } + else + { + std::memcpy(buffer, "0.0 ", 4); + buffer += force_trailing_dot_zero ? 3 : 1; + return buffer; + } + } + + if (significand == 0) + { + buffer[0] = '-'; + buffer += v.SignBit(); + + std::memcpy(buffer, "inf ", 4); + return buffer + 3; + } + else + { + std::memcpy(buffer, "nan ", 4); + return buffer + 3; + } +} + +//================================================================================================== +// +//================================================================================================== + +char* schubfach::Ftoa(char* buffer, float value) +{ + return ToChars(buffer, value); +} diff --git a/lib/vendor/drachennest/schubfach_32.h b/lib/vendor/drachennest/schubfach_32.h new file mode 100644 index 000000000000..ca48e495b533 --- /dev/null +++ b/lib/vendor/drachennest/schubfach_32.h @@ -0,0 +1,31 @@ +// Copyright 2020 Alexander Bolz +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +namespace schubfach { + +// char* output_end = Ftoa(buffer, value); +// +// Converts the given single-precision number into decimal form and stores the result in the given +// buffer. +// +// The buffer must be large enough, i.e. >= FtoaMinBufferLength. +// The output format is similar to printf("%g"). +// The output is _not_ null-terminted. +// +// The output is optimal, i.e. the output string +// 1. rounds back to the input number when read in (using round-to-nearest-even) +// 2. is as short as possible, +// 3. is as close to the input number as possible. +// +// Note: +// This function may temporarily write up to FtoaMinBufferLength characters into the buffer. + +constexpr int FtoaMinBufferLength = 32; + +char* Ftoa(char* buffer, float value); + +} // namespace schubfach diff --git a/lib/vendor/drachennest/schubfach_64.cc b/lib/vendor/drachennest/schubfach_64.cc new file mode 100644 index 000000000000..ad3348e33cab --- /dev/null +++ b/lib/vendor/drachennest/schubfach_64.cc @@ -0,0 +1,1354 @@ +// Copyright 2020 Alexander Bolz +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +#include "schubfach_64.h" + +//-------------------------------------------------------------------------------------------------- +// This file contains an implementation of the Schubfach algorithm as described in +// +// [1] Raffaello Giulietti, "The Schubfach way to render doubles", +// https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN +//-------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#if _MSC_VER +#include +#endif + +#ifndef SF_ASSERT +#define SF_ASSERT(X) assert(X) +#endif + +//================================================================================================== +// +//================================================================================================== + +template +static inline Dest ReinterpretBits(Source source) +{ + static_assert(sizeof(Dest) == sizeof(Source), "size mismatch"); + + Dest dest; + std::memcpy(&dest, &source, sizeof(Source)); + return dest; +} + +namespace { +struct Double +{ + static_assert(std::numeric_limits::is_iec559 + && std::numeric_limits::digits == 53 + && std::numeric_limits::max_exponent == 1024, + "IEEE-754 double-precision implementation required"); + + using value_type = double; + using bits_type = uint64_t; + +// static constexpr int32_t MaxDigits10 = std::numeric_limits::max_digits10; + static constexpr int32_t SignificandSize = std::numeric_limits::digits; // = p (includes the hidden bit) + static constexpr int32_t ExponentBias = std::numeric_limits::max_exponent - 1 + (SignificandSize - 1); +// static constexpr int32_t MaxExponent = std::numeric_limits::max_exponent - 1 - (SignificandSize - 1); +// static constexpr int32_t MinExponent = std::numeric_limits::min_exponent - 1 - (SignificandSize - 1); + static constexpr bits_type MaxIeeeExponent = bits_type{2 * std::numeric_limits::max_exponent - 1}; + static constexpr bits_type HiddenBit = bits_type{1} << (SignificandSize - 1); // = 2^(p-1) + static constexpr bits_type SignificandMask = HiddenBit - 1; // = 2^(p-1) - 1 + static constexpr bits_type ExponentMask = MaxIeeeExponent << (SignificandSize - 1); + static constexpr bits_type SignMask = ~(~bits_type{0} >> 1); + + bits_type bits; + + explicit Double(bits_type bits_) : bits(bits_) {} + explicit Double(value_type value) : bits(ReinterpretBits(value)) {} + + bits_type PhysicalSignificand() const { + return bits & SignificandMask; + } + + bits_type PhysicalExponent() const { + return (bits & ExponentMask) >> (SignificandSize - 1); + } + + bool IsFinite() const { + return (bits & ExponentMask) != ExponentMask; + } + + bool IsInf() const { + return (bits & ExponentMask) == ExponentMask && (bits & SignificandMask) == 0; + } + + bool IsNaN() const { + return (bits & ExponentMask) == ExponentMask && (bits & SignificandMask) != 0; + } + + bool IsZero() const { + return (bits & ~SignMask) == 0; + } + + bool SignBit() const { + return (bits & SignMask) != 0; + } +}; +} // namespace + +//================================================================================================== +// +//================================================================================================== + +// Returns floor(x / 2^n). +// +// Technically, right-shift of negative integers is implementation defined... +// Should easily be optimized into SAR (or equivalent) instruction. +static inline int32_t FloorDivPow2(int32_t x, int32_t n) +{ +#if 0 + return x < 0 ? ~(~x >> n) : (x >> n); +#else + return x >> n; +#endif +} + +// Returns floor(log_10(2^e)) +// static inline int32_t FloorLog10Pow2(int32_t e) +// { +// SF_ASSERT(e >= -1500); +// SF_ASSERT(e <= 1500); +// return FloorDivPow2(e * 1262611, 22); +// } + +// Returns floor(log_10(3/4 2^e)) +// static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e) +// { +// SF_ASSERT(e >= -1500); +// SF_ASSERT(e <= 1500); +// return FloorDivPow2(e * 1262611 - 524031, 22); +// } + +// Returns floor(log_2(10^e)) +static inline int32_t FloorLog2Pow10(int32_t e) +{ + SF_ASSERT(e >= -1233); + SF_ASSERT(e <= 1233); + return FloorDivPow2(e * 1741647, 19); +} + +//================================================================================================== +// +//================================================================================================== + +namespace { +struct uint64x2 { + uint64_t hi; + uint64_t lo; +}; +} + +static inline uint64x2 ComputePow10_Double(int32_t k) +{ + // There are unique beta and r such that 10^k = beta 2^r and + // 2^127 <= beta < 2^128, namely r = floor(log_2 10^k) - 127 and + // beta = 2^-r 10^k. + // Let g = ceil(beta), so (g-1) 2^r < 10^k <= g 2^r, with the latter + // value being a pretty good overestimate for 10^k. + + // NB: Since for all the required exponents k, we have g < 2^128, + // all constants can be stored in 128-bit integers. + + static constexpr int32_t kMin = -292; + static constexpr int32_t kMax = 324; + static constexpr uint64x2 g[kMax - kMin + 1] = { + {0xFF77B1FCBEBCDC4F, 0x25E8E89C13BB0F7B}, // -292 + {0x9FAACF3DF73609B1, 0x77B191618C54E9AD}, // -291 + {0xC795830D75038C1D, 0xD59DF5B9EF6A2418}, // -290 + {0xF97AE3D0D2446F25, 0x4B0573286B44AD1E}, // -289 + {0x9BECCE62836AC577, 0x4EE367F9430AEC33}, // -288 + {0xC2E801FB244576D5, 0x229C41F793CDA740}, // -287 + {0xF3A20279ED56D48A, 0x6B43527578C11110}, // -286 + {0x9845418C345644D6, 0x830A13896B78AAAA}, // -285 + {0xBE5691EF416BD60C, 0x23CC986BC656D554}, // -284 + {0xEDEC366B11C6CB8F, 0x2CBFBE86B7EC8AA9}, // -283 + {0x94B3A202EB1C3F39, 0x7BF7D71432F3D6AA}, // -282 + {0xB9E08A83A5E34F07, 0xDAF5CCD93FB0CC54}, // -281 + {0xE858AD248F5C22C9, 0xD1B3400F8F9CFF69}, // -280 + {0x91376C36D99995BE, 0x23100809B9C21FA2}, // -279 + {0xB58547448FFFFB2D, 0xABD40A0C2832A78B}, // -278 + {0xE2E69915B3FFF9F9, 0x16C90C8F323F516D}, // -277 + {0x8DD01FAD907FFC3B, 0xAE3DA7D97F6792E4}, // -276 + {0xB1442798F49FFB4A, 0x99CD11CFDF41779D}, // -275 + {0xDD95317F31C7FA1D, 0x40405643D711D584}, // -274 + {0x8A7D3EEF7F1CFC52, 0x482835EA666B2573}, // -273 + {0xAD1C8EAB5EE43B66, 0xDA3243650005EED0}, // -272 + {0xD863B256369D4A40, 0x90BED43E40076A83}, // -271 + {0x873E4F75E2224E68, 0x5A7744A6E804A292}, // -270 + {0xA90DE3535AAAE202, 0x711515D0A205CB37}, // -269 + {0xD3515C2831559A83, 0x0D5A5B44CA873E04}, // -268 + {0x8412D9991ED58091, 0xE858790AFE9486C3}, // -267 + {0xA5178FFF668AE0B6, 0x626E974DBE39A873}, // -266 + {0xCE5D73FF402D98E3, 0xFB0A3D212DC81290}, // -265 + {0x80FA687F881C7F8E, 0x7CE66634BC9D0B9A}, // -264 + {0xA139029F6A239F72, 0x1C1FFFC1EBC44E81}, // -263 + {0xC987434744AC874E, 0xA327FFB266B56221}, // -262 + {0xFBE9141915D7A922, 0x4BF1FF9F0062BAA9}, // -261 + {0x9D71AC8FADA6C9B5, 0x6F773FC3603DB4AA}, // -260 + {0xC4CE17B399107C22, 0xCB550FB4384D21D4}, // -259 + {0xF6019DA07F549B2B, 0x7E2A53A146606A49}, // -258 + {0x99C102844F94E0FB, 0x2EDA7444CBFC426E}, // -257 + {0xC0314325637A1939, 0xFA911155FEFB5309}, // -256 + {0xF03D93EEBC589F88, 0x793555AB7EBA27CB}, // -255 + {0x96267C7535B763B5, 0x4BC1558B2F3458DF}, // -254 + {0xBBB01B9283253CA2, 0x9EB1AAEDFB016F17}, // -253 + {0xEA9C227723EE8BCB, 0x465E15A979C1CADD}, // -252 + {0x92A1958A7675175F, 0x0BFACD89EC191ECA}, // -251 + {0xB749FAED14125D36, 0xCEF980EC671F667C}, // -250 + {0xE51C79A85916F484, 0x82B7E12780E7401B}, // -249 + {0x8F31CC0937AE58D2, 0xD1B2ECB8B0908811}, // -248 + {0xB2FE3F0B8599EF07, 0x861FA7E6DCB4AA16}, // -247 + {0xDFBDCECE67006AC9, 0x67A791E093E1D49B}, // -246 + {0x8BD6A141006042BD, 0xE0C8BB2C5C6D24E1}, // -245 + {0xAECC49914078536D, 0x58FAE9F773886E19}, // -244 + {0xDA7F5BF590966848, 0xAF39A475506A899F}, // -243 + {0x888F99797A5E012D, 0x6D8406C952429604}, // -242 + {0xAAB37FD7D8F58178, 0xC8E5087BA6D33B84}, // -241 + {0xD5605FCDCF32E1D6, 0xFB1E4A9A90880A65}, // -240 + {0x855C3BE0A17FCD26, 0x5CF2EEA09A550680}, // -239 + {0xA6B34AD8C9DFC06F, 0xF42FAA48C0EA481F}, // -238 + {0xD0601D8EFC57B08B, 0xF13B94DAF124DA27}, // -237 + {0x823C12795DB6CE57, 0x76C53D08D6B70859}, // -236 + {0xA2CB1717B52481ED, 0x54768C4B0C64CA6F}, // -235 + {0xCB7DDCDDA26DA268, 0xA9942F5DCF7DFD0A}, // -234 + {0xFE5D54150B090B02, 0xD3F93B35435D7C4D}, // -233 + {0x9EFA548D26E5A6E1, 0xC47BC5014A1A6DB0}, // -232 + {0xC6B8E9B0709F109A, 0x359AB6419CA1091C}, // -231 + {0xF867241C8CC6D4C0, 0xC30163D203C94B63}, // -230 + {0x9B407691D7FC44F8, 0x79E0DE63425DCF1E}, // -229 + {0xC21094364DFB5636, 0x985915FC12F542E5}, // -228 + {0xF294B943E17A2BC4, 0x3E6F5B7B17B2939E}, // -227 + {0x979CF3CA6CEC5B5A, 0xA705992CEECF9C43}, // -226 + {0xBD8430BD08277231, 0x50C6FF782A838354}, // -225 + {0xECE53CEC4A314EBD, 0xA4F8BF5635246429}, // -224 + {0x940F4613AE5ED136, 0x871B7795E136BE9A}, // -223 + {0xB913179899F68584, 0x28E2557B59846E40}, // -222 + {0xE757DD7EC07426E5, 0x331AEADA2FE589D0}, // -221 + {0x9096EA6F3848984F, 0x3FF0D2C85DEF7622}, // -220 + {0xB4BCA50B065ABE63, 0x0FED077A756B53AA}, // -219 + {0xE1EBCE4DC7F16DFB, 0xD3E8495912C62895}, // -218 + {0x8D3360F09CF6E4BD, 0x64712DD7ABBBD95D}, // -217 + {0xB080392CC4349DEC, 0xBD8D794D96AACFB4}, // -216 + {0xDCA04777F541C567, 0xECF0D7A0FC5583A1}, // -215 + {0x89E42CAAF9491B60, 0xF41686C49DB57245}, // -214 + {0xAC5D37D5B79B6239, 0x311C2875C522CED6}, // -213 + {0xD77485CB25823AC7, 0x7D633293366B828C}, // -212 + {0x86A8D39EF77164BC, 0xAE5DFF9C02033198}, // -211 + {0xA8530886B54DBDEB, 0xD9F57F830283FDFD}, // -210 + {0xD267CAA862A12D66, 0xD072DF63C324FD7C}, // -209 + {0x8380DEA93DA4BC60, 0x4247CB9E59F71E6E}, // -208 + {0xA46116538D0DEB78, 0x52D9BE85F074E609}, // -207 + {0xCD795BE870516656, 0x67902E276C921F8C}, // -206 + {0x806BD9714632DFF6, 0x00BA1CD8A3DB53B7}, // -205 + {0xA086CFCD97BF97F3, 0x80E8A40ECCD228A5}, // -204 + {0xC8A883C0FDAF7DF0, 0x6122CD128006B2CE}, // -203 + {0xFAD2A4B13D1B5D6C, 0x796B805720085F82}, // -202 + {0x9CC3A6EEC6311A63, 0xCBE3303674053BB1}, // -201 + {0xC3F490AA77BD60FC, 0xBEDBFC4411068A9D}, // -200 + {0xF4F1B4D515ACB93B, 0xEE92FB5515482D45}, // -199 + {0x991711052D8BF3C5, 0x751BDD152D4D1C4B}, // -198 + {0xBF5CD54678EEF0B6, 0xD262D45A78A0635E}, // -197 + {0xEF340A98172AACE4, 0x86FB897116C87C35}, // -196 + {0x9580869F0E7AAC0E, 0xD45D35E6AE3D4DA1}, // -195 + {0xBAE0A846D2195712, 0x8974836059CCA10A}, // -194 + {0xE998D258869FACD7, 0x2BD1A438703FC94C}, // -193 + {0x91FF83775423CC06, 0x7B6306A34627DDD0}, // -192 + {0xB67F6455292CBF08, 0x1A3BC84C17B1D543}, // -191 + {0xE41F3D6A7377EECA, 0x20CABA5F1D9E4A94}, // -190 + {0x8E938662882AF53E, 0x547EB47B7282EE9D}, // -189 + {0xB23867FB2A35B28D, 0xE99E619A4F23AA44}, // -188 + {0xDEC681F9F4C31F31, 0x6405FA00E2EC94D5}, // -187 + {0x8B3C113C38F9F37E, 0xDE83BC408DD3DD05}, // -186 + {0xAE0B158B4738705E, 0x9624AB50B148D446}, // -185 + {0xD98DDAEE19068C76, 0x3BADD624DD9B0958}, // -184 + {0x87F8A8D4CFA417C9, 0xE54CA5D70A80E5D7}, // -183 + {0xA9F6D30A038D1DBC, 0x5E9FCF4CCD211F4D}, // -182 + {0xD47487CC8470652B, 0x7647C32000696720}, // -181 + {0x84C8D4DFD2C63F3B, 0x29ECD9F40041E074}, // -180 + {0xA5FB0A17C777CF09, 0xF468107100525891}, // -179 + {0xCF79CC9DB955C2CC, 0x7182148D4066EEB5}, // -178 + {0x81AC1FE293D599BF, 0xC6F14CD848405531}, // -177 + {0xA21727DB38CB002F, 0xB8ADA00E5A506A7D}, // -176 + {0xCA9CF1D206FDC03B, 0xA6D90811F0E4851D}, // -175 + {0xFD442E4688BD304A, 0x908F4A166D1DA664}, // -174 + {0x9E4A9CEC15763E2E, 0x9A598E4E043287FF}, // -173 + {0xC5DD44271AD3CDBA, 0x40EFF1E1853F29FE}, // -172 + {0xF7549530E188C128, 0xD12BEE59E68EF47D}, // -171 + {0x9A94DD3E8CF578B9, 0x82BB74F8301958CF}, // -170 + {0xC13A148E3032D6E7, 0xE36A52363C1FAF02}, // -169 + {0xF18899B1BC3F8CA1, 0xDC44E6C3CB279AC2}, // -168 + {0x96F5600F15A7B7E5, 0x29AB103A5EF8C0BA}, // -167 + {0xBCB2B812DB11A5DE, 0x7415D448F6B6F0E8}, // -166 + {0xEBDF661791D60F56, 0x111B495B3464AD22}, // -165 + {0x936B9FCEBB25C995, 0xCAB10DD900BEEC35}, // -164 + {0xB84687C269EF3BFB, 0x3D5D514F40EEA743}, // -163 + {0xE65829B3046B0AFA, 0x0CB4A5A3112A5113}, // -162 + {0x8FF71A0FE2C2E6DC, 0x47F0E785EABA72AC}, // -161 + {0xB3F4E093DB73A093, 0x59ED216765690F57}, // -160 + {0xE0F218B8D25088B8, 0x306869C13EC3532D}, // -159 + {0x8C974F7383725573, 0x1E414218C73A13FC}, // -158 + {0xAFBD2350644EEACF, 0xE5D1929EF90898FB}, // -157 + {0xDBAC6C247D62A583, 0xDF45F746B74ABF3A}, // -156 + {0x894BC396CE5DA772, 0x6B8BBA8C328EB784}, // -155 + {0xAB9EB47C81F5114F, 0x066EA92F3F326565}, // -154 + {0xD686619BA27255A2, 0xC80A537B0EFEFEBE}, // -153 + {0x8613FD0145877585, 0xBD06742CE95F5F37}, // -152 + {0xA798FC4196E952E7, 0x2C48113823B73705}, // -151 + {0xD17F3B51FCA3A7A0, 0xF75A15862CA504C6}, // -150 + {0x82EF85133DE648C4, 0x9A984D73DBE722FC}, // -149 + {0xA3AB66580D5FDAF5, 0xC13E60D0D2E0EBBB}, // -148 + {0xCC963FEE10B7D1B3, 0x318DF905079926A9}, // -147 + {0xFFBBCFE994E5C61F, 0xFDF17746497F7053}, // -146 + {0x9FD561F1FD0F9BD3, 0xFEB6EA8BEDEFA634}, // -145 + {0xC7CABA6E7C5382C8, 0xFE64A52EE96B8FC1}, // -144 + {0xF9BD690A1B68637B, 0x3DFDCE7AA3C673B1}, // -143 + {0x9C1661A651213E2D, 0x06BEA10CA65C084F}, // -142 + {0xC31BFA0FE5698DB8, 0x486E494FCFF30A63}, // -141 + {0xF3E2F893DEC3F126, 0x5A89DBA3C3EFCCFB}, // -140 + {0x986DDB5C6B3A76B7, 0xF89629465A75E01D}, // -139 + {0xBE89523386091465, 0xF6BBB397F1135824}, // -138 + {0xEE2BA6C0678B597F, 0x746AA07DED582E2D}, // -137 + {0x94DB483840B717EF, 0xA8C2A44EB4571CDD}, // -136 + {0xBA121A4650E4DDEB, 0x92F34D62616CE414}, // -135 + {0xE896A0D7E51E1566, 0x77B020BAF9C81D18}, // -134 + {0x915E2486EF32CD60, 0x0ACE1474DC1D122F}, // -133 + {0xB5B5ADA8AAFF80B8, 0x0D819992132456BB}, // -132 + {0xE3231912D5BF60E6, 0x10E1FFF697ED6C6A}, // -131 + {0x8DF5EFABC5979C8F, 0xCA8D3FFA1EF463C2}, // -130 + {0xB1736B96B6FD83B3, 0xBD308FF8A6B17CB3}, // -129 + {0xDDD0467C64BCE4A0, 0xAC7CB3F6D05DDBDF}, // -128 + {0x8AA22C0DBEF60EE4, 0x6BCDF07A423AA96C}, // -127 + {0xAD4AB7112EB3929D, 0x86C16C98D2C953C7}, // -126 + {0xD89D64D57A607744, 0xE871C7BF077BA8B8}, // -125 + {0x87625F056C7C4A8B, 0x11471CD764AD4973}, // -124 + {0xA93AF6C6C79B5D2D, 0xD598E40D3DD89BD0}, // -123 + {0xD389B47879823479, 0x4AFF1D108D4EC2C4}, // -122 + {0x843610CB4BF160CB, 0xCEDF722A585139BB}, // -121 + {0xA54394FE1EEDB8FE, 0xC2974EB4EE658829}, // -120 + {0xCE947A3DA6A9273E, 0x733D226229FEEA33}, // -119 + {0x811CCC668829B887, 0x0806357D5A3F5260}, // -118 + {0xA163FF802A3426A8, 0xCA07C2DCB0CF26F8}, // -117 + {0xC9BCFF6034C13052, 0xFC89B393DD02F0B6}, // -116 + {0xFC2C3F3841F17C67, 0xBBAC2078D443ACE3}, // -115 + {0x9D9BA7832936EDC0, 0xD54B944B84AA4C0E}, // -114 + {0xC5029163F384A931, 0x0A9E795E65D4DF12}, // -113 + {0xF64335BCF065D37D, 0x4D4617B5FF4A16D6}, // -112 + {0x99EA0196163FA42E, 0x504BCED1BF8E4E46}, // -111 + {0xC06481FB9BCF8D39, 0xE45EC2862F71E1D7}, // -110 + {0xF07DA27A82C37088, 0x5D767327BB4E5A4D}, // -109 + {0x964E858C91BA2655, 0x3A6A07F8D510F870}, // -108 + {0xBBE226EFB628AFEA, 0x890489F70A55368C}, // -107 + {0xEADAB0ABA3B2DBE5, 0x2B45AC74CCEA842F}, // -106 + {0x92C8AE6B464FC96F, 0x3B0B8BC90012929E}, // -105 + {0xB77ADA0617E3BBCB, 0x09CE6EBB40173745}, // -104 + {0xE55990879DDCAABD, 0xCC420A6A101D0516}, // -103 + {0x8F57FA54C2A9EAB6, 0x9FA946824A12232E}, // -102 + {0xB32DF8E9F3546564, 0x47939822DC96ABFA}, // -101 + {0xDFF9772470297EBD, 0x59787E2B93BC56F8}, // -100 + {0x8BFBEA76C619EF36, 0x57EB4EDB3C55B65B}, // -99 + {0xAEFAE51477A06B03, 0xEDE622920B6B23F2}, // -98 + {0xDAB99E59958885C4, 0xE95FAB368E45ECEE}, // -97 + {0x88B402F7FD75539B, 0x11DBCB0218EBB415}, // -96 + {0xAAE103B5FCD2A881, 0xD652BDC29F26A11A}, // -95 + {0xD59944A37C0752A2, 0x4BE76D3346F04960}, // -94 + {0x857FCAE62D8493A5, 0x6F70A4400C562DDC}, // -93 + {0xA6DFBD9FB8E5B88E, 0xCB4CCD500F6BB953}, // -92 + {0xD097AD07A71F26B2, 0x7E2000A41346A7A8}, // -91 + {0x825ECC24C873782F, 0x8ED400668C0C28C9}, // -90 + {0xA2F67F2DFA90563B, 0x728900802F0F32FB}, // -89 + {0xCBB41EF979346BCA, 0x4F2B40A03AD2FFBA}, // -88 + {0xFEA126B7D78186BC, 0xE2F610C84987BFA9}, // -87 + {0x9F24B832E6B0F436, 0x0DD9CA7D2DF4D7CA}, // -86 + {0xC6EDE63FA05D3143, 0x91503D1C79720DBC}, // -85 + {0xF8A95FCF88747D94, 0x75A44C6397CE912B}, // -84 + {0x9B69DBE1B548CE7C, 0xC986AFBE3EE11ABB}, // -83 + {0xC24452DA229B021B, 0xFBE85BADCE996169}, // -82 + {0xF2D56790AB41C2A2, 0xFAE27299423FB9C4}, // -81 + {0x97C560BA6B0919A5, 0xDCCD879FC967D41B}, // -80 + {0xBDB6B8E905CB600F, 0x5400E987BBC1C921}, // -79 + {0xED246723473E3813, 0x290123E9AAB23B69}, // -78 + {0x9436C0760C86E30B, 0xF9A0B6720AAF6522}, // -77 + {0xB94470938FA89BCE, 0xF808E40E8D5B3E6A}, // -76 + {0xE7958CB87392C2C2, 0xB60B1D1230B20E05}, // -75 + {0x90BD77F3483BB9B9, 0xB1C6F22B5E6F48C3}, // -74 + {0xB4ECD5F01A4AA828, 0x1E38AEB6360B1AF4}, // -73 + {0xE2280B6C20DD5232, 0x25C6DA63C38DE1B1}, // -72 + {0x8D590723948A535F, 0x579C487E5A38AD0F}, // -71 + {0xB0AF48EC79ACE837, 0x2D835A9DF0C6D852}, // -70 + {0xDCDB1B2798182244, 0xF8E431456CF88E66}, // -69 + {0x8A08F0F8BF0F156B, 0x1B8E9ECB641B5900}, // -68 + {0xAC8B2D36EED2DAC5, 0xE272467E3D222F40}, // -67 + {0xD7ADF884AA879177, 0x5B0ED81DCC6ABB10}, // -66 + {0x86CCBB52EA94BAEA, 0x98E947129FC2B4EA}, // -65 + {0xA87FEA27A539E9A5, 0x3F2398D747B36225}, // -64 + {0xD29FE4B18E88640E, 0x8EEC7F0D19A03AAE}, // -63 + {0x83A3EEEEF9153E89, 0x1953CF68300424AD}, // -62 + {0xA48CEAAAB75A8E2B, 0x5FA8C3423C052DD8}, // -61 + {0xCDB02555653131B6, 0x3792F412CB06794E}, // -60 + {0x808E17555F3EBF11, 0xE2BBD88BBEE40BD1}, // -59 + {0xA0B19D2AB70E6ED6, 0x5B6ACEAEAE9D0EC5}, // -58 + {0xC8DE047564D20A8B, 0xF245825A5A445276}, // -57 + {0xFB158592BE068D2E, 0xEED6E2F0F0D56713}, // -56 + {0x9CED737BB6C4183D, 0x55464DD69685606C}, // -55 + {0xC428D05AA4751E4C, 0xAA97E14C3C26B887}, // -54 + {0xF53304714D9265DF, 0xD53DD99F4B3066A9}, // -53 + {0x993FE2C6D07B7FAB, 0xE546A8038EFE402A}, // -52 + {0xBF8FDB78849A5F96, 0xDE98520472BDD034}, // -51 + {0xEF73D256A5C0F77C, 0x963E66858F6D4441}, // -50 + {0x95A8637627989AAD, 0xDDE7001379A44AA9}, // -49 + {0xBB127C53B17EC159, 0x5560C018580D5D53}, // -48 + {0xE9D71B689DDE71AF, 0xAAB8F01E6E10B4A7}, // -47 + {0x9226712162AB070D, 0xCAB3961304CA70E9}, // -46 + {0xB6B00D69BB55C8D1, 0x3D607B97C5FD0D23}, // -45 + {0xE45C10C42A2B3B05, 0x8CB89A7DB77C506B}, // -44 + {0x8EB98A7A9A5B04E3, 0x77F3608E92ADB243}, // -43 + {0xB267ED1940F1C61C, 0x55F038B237591ED4}, // -42 + {0xDF01E85F912E37A3, 0x6B6C46DEC52F6689}, // -41 + {0x8B61313BBABCE2C6, 0x2323AC4B3B3DA016}, // -40 + {0xAE397D8AA96C1B77, 0xABEC975E0A0D081B}, // -39 + {0xD9C7DCED53C72255, 0x96E7BD358C904A22}, // -38 + {0x881CEA14545C7575, 0x7E50D64177DA2E55}, // -37 + {0xAA242499697392D2, 0xDDE50BD1D5D0B9EA}, // -36 + {0xD4AD2DBFC3D07787, 0x955E4EC64B44E865}, // -35 + {0x84EC3C97DA624AB4, 0xBD5AF13BEF0B113F}, // -34 + {0xA6274BBDD0FADD61, 0xECB1AD8AEACDD58F}, // -33 + {0xCFB11EAD453994BA, 0x67DE18EDA5814AF3}, // -32 + {0x81CEB32C4B43FCF4, 0x80EACF948770CED8}, // -31 + {0xA2425FF75E14FC31, 0xA1258379A94D028E}, // -30 + {0xCAD2F7F5359A3B3E, 0x096EE45813A04331}, // -29 + {0xFD87B5F28300CA0D, 0x8BCA9D6E188853FD}, // -28 + {0x9E74D1B791E07E48, 0x775EA264CF55347E}, // -27 + {0xC612062576589DDA, 0x95364AFE032A819E}, // -26 + {0xF79687AED3EEC551, 0x3A83DDBD83F52205}, // -25 + {0x9ABE14CD44753B52, 0xC4926A9672793543}, // -24 + {0xC16D9A0095928A27, 0x75B7053C0F178294}, // -23 + {0xF1C90080BAF72CB1, 0x5324C68B12DD6339}, // -22 + {0x971DA05074DA7BEE, 0xD3F6FC16EBCA5E04}, // -21 + {0xBCE5086492111AEA, 0x88F4BB1CA6BCF585}, // -20 + {0xEC1E4A7DB69561A5, 0x2B31E9E3D06C32E6}, // -19 + {0x9392EE8E921D5D07, 0x3AFF322E62439FD0}, // -18 + {0xB877AA3236A4B449, 0x09BEFEB9FAD487C3}, // -17 + {0xE69594BEC44DE15B, 0x4C2EBE687989A9B4}, // -16 + {0x901D7CF73AB0ACD9, 0x0F9D37014BF60A11}, // -15 + {0xB424DC35095CD80F, 0x538484C19EF38C95}, // -14 + {0xE12E13424BB40E13, 0x2865A5F206B06FBA}, // -13 + {0x8CBCCC096F5088CB, 0xF93F87B7442E45D4}, // -12 + {0xAFEBFF0BCB24AAFE, 0xF78F69A51539D749}, // -11 + {0xDBE6FECEBDEDD5BE, 0xB573440E5A884D1C}, // -10 + {0x89705F4136B4A597, 0x31680A88F8953031}, // -9 + {0xABCC77118461CEFC, 0xFDC20D2B36BA7C3E}, // -8 + {0xD6BF94D5E57A42BC, 0x3D32907604691B4D}, // -7 + {0x8637BD05AF6C69B5, 0xA63F9A49C2C1B110}, // -6 + {0xA7C5AC471B478423, 0x0FCF80DC33721D54}, // -5 + {0xD1B71758E219652B, 0xD3C36113404EA4A9}, // -4 + {0x83126E978D4FDF3B, 0x645A1CAC083126EA}, // -3 + {0xA3D70A3D70A3D70A, 0x3D70A3D70A3D70A4}, // -2 + {0xCCCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCD}, // -1 + {0x8000000000000000, 0x0000000000000000}, // 0 + {0xA000000000000000, 0x0000000000000000}, // 1 + {0xC800000000000000, 0x0000000000000000}, // 2 + {0xFA00000000000000, 0x0000000000000000}, // 3 + {0x9C40000000000000, 0x0000000000000000}, // 4 + {0xC350000000000000, 0x0000000000000000}, // 5 + {0xF424000000000000, 0x0000000000000000}, // 6 + {0x9896800000000000, 0x0000000000000000}, // 7 + {0xBEBC200000000000, 0x0000000000000000}, // 8 + {0xEE6B280000000000, 0x0000000000000000}, // 9 + {0x9502F90000000000, 0x0000000000000000}, // 10 + {0xBA43B74000000000, 0x0000000000000000}, // 11 + {0xE8D4A51000000000, 0x0000000000000000}, // 12 + {0x9184E72A00000000, 0x0000000000000000}, // 13 + {0xB5E620F480000000, 0x0000000000000000}, // 14 + {0xE35FA931A0000000, 0x0000000000000000}, // 15 + {0x8E1BC9BF04000000, 0x0000000000000000}, // 16 + {0xB1A2BC2EC5000000, 0x0000000000000000}, // 17 + {0xDE0B6B3A76400000, 0x0000000000000000}, // 18 + {0x8AC7230489E80000, 0x0000000000000000}, // 19 + {0xAD78EBC5AC620000, 0x0000000000000000}, // 20 + {0xD8D726B7177A8000, 0x0000000000000000}, // 21 + {0x878678326EAC9000, 0x0000000000000000}, // 22 + {0xA968163F0A57B400, 0x0000000000000000}, // 23 + {0xD3C21BCECCEDA100, 0x0000000000000000}, // 24 + {0x84595161401484A0, 0x0000000000000000}, // 25 + {0xA56FA5B99019A5C8, 0x0000000000000000}, // 26 + {0xCECB8F27F4200F3A, 0x0000000000000000}, // 27 + {0x813F3978F8940984, 0x4000000000000000}, // 28 + {0xA18F07D736B90BE5, 0x5000000000000000}, // 29 + {0xC9F2C9CD04674EDE, 0xA400000000000000}, // 30 + {0xFC6F7C4045812296, 0x4D00000000000000}, // 31 + {0x9DC5ADA82B70B59D, 0xF020000000000000}, // 32 + {0xC5371912364CE305, 0x6C28000000000000}, // 33 + {0xF684DF56C3E01BC6, 0xC732000000000000}, // 34 + {0x9A130B963A6C115C, 0x3C7F400000000000}, // 35 + {0xC097CE7BC90715B3, 0x4B9F100000000000}, // 36 + {0xF0BDC21ABB48DB20, 0x1E86D40000000000}, // 37 + {0x96769950B50D88F4, 0x1314448000000000}, // 38 + {0xBC143FA4E250EB31, 0x17D955A000000000}, // 39 + {0xEB194F8E1AE525FD, 0x5DCFAB0800000000}, // 40 + {0x92EFD1B8D0CF37BE, 0x5AA1CAE500000000}, // 41 + {0xB7ABC627050305AD, 0xF14A3D9E40000000}, // 42 + {0xE596B7B0C643C719, 0x6D9CCD05D0000000}, // 43 + {0x8F7E32CE7BEA5C6F, 0xE4820023A2000000}, // 44 + {0xB35DBF821AE4F38B, 0xDDA2802C8A800000}, // 45 + {0xE0352F62A19E306E, 0xD50B2037AD200000}, // 46 + {0x8C213D9DA502DE45, 0x4526F422CC340000}, // 47 + {0xAF298D050E4395D6, 0x9670B12B7F410000}, // 48 + {0xDAF3F04651D47B4C, 0x3C0CDD765F114000}, // 49 + {0x88D8762BF324CD0F, 0xA5880A69FB6AC800}, // 50 + {0xAB0E93B6EFEE0053, 0x8EEA0D047A457A00}, // 51 + {0xD5D238A4ABE98068, 0x72A4904598D6D880}, // 52 + {0x85A36366EB71F041, 0x47A6DA2B7F864750}, // 53 + {0xA70C3C40A64E6C51, 0x999090B65F67D924}, // 54 + {0xD0CF4B50CFE20765, 0xFFF4B4E3F741CF6D}, // 55 + {0x82818F1281ED449F, 0xBFF8F10E7A8921A5}, // 56 + {0xA321F2D7226895C7, 0xAFF72D52192B6A0E}, // 57 + {0xCBEA6F8CEB02BB39, 0x9BF4F8A69F764491}, // 58 + {0xFEE50B7025C36A08, 0x02F236D04753D5B5}, // 59 + {0x9F4F2726179A2245, 0x01D762422C946591}, // 60 + {0xC722F0EF9D80AAD6, 0x424D3AD2B7B97EF6}, // 61 + {0xF8EBAD2B84E0D58B, 0xD2E0898765A7DEB3}, // 62 + {0x9B934C3B330C8577, 0x63CC55F49F88EB30}, // 63 + {0xC2781F49FFCFA6D5, 0x3CBF6B71C76B25FC}, // 64 + {0xF316271C7FC3908A, 0x8BEF464E3945EF7B}, // 65 + {0x97EDD871CFDA3A56, 0x97758BF0E3CBB5AD}, // 66 + {0xBDE94E8E43D0C8EC, 0x3D52EEED1CBEA318}, // 67 + {0xED63A231D4C4FB27, 0x4CA7AAA863EE4BDE}, // 68 + {0x945E455F24FB1CF8, 0x8FE8CAA93E74EF6B}, // 69 + {0xB975D6B6EE39E436, 0xB3E2FD538E122B45}, // 70 + {0xE7D34C64A9C85D44, 0x60DBBCA87196B617}, // 71 + {0x90E40FBEEA1D3A4A, 0xBC8955E946FE31CE}, // 72 + {0xB51D13AEA4A488DD, 0x6BABAB6398BDBE42}, // 73 + {0xE264589A4DCDAB14, 0xC696963C7EED2DD2}, // 74 + {0x8D7EB76070A08AEC, 0xFC1E1DE5CF543CA3}, // 75 + {0xB0DE65388CC8ADA8, 0x3B25A55F43294BCC}, // 76 + {0xDD15FE86AFFAD912, 0x49EF0EB713F39EBF}, // 77 + {0x8A2DBF142DFCC7AB, 0x6E3569326C784338}, // 78 + {0xACB92ED9397BF996, 0x49C2C37F07965405}, // 79 + {0xD7E77A8F87DAF7FB, 0xDC33745EC97BE907}, // 80 + {0x86F0AC99B4E8DAFD, 0x69A028BB3DED71A4}, // 81 + {0xA8ACD7C0222311BC, 0xC40832EA0D68CE0D}, // 82 + {0xD2D80DB02AABD62B, 0xF50A3FA490C30191}, // 83 + {0x83C7088E1AAB65DB, 0x792667C6DA79E0FB}, // 84 + {0xA4B8CAB1A1563F52, 0x577001B891185939}, // 85 + {0xCDE6FD5E09ABCF26, 0xED4C0226B55E6F87}, // 86 + {0x80B05E5AC60B6178, 0x544F8158315B05B5}, // 87 + {0xA0DC75F1778E39D6, 0x696361AE3DB1C722}, // 88 + {0xC913936DD571C84C, 0x03BC3A19CD1E38EA}, // 89 + {0xFB5878494ACE3A5F, 0x04AB48A04065C724}, // 90 + {0x9D174B2DCEC0E47B, 0x62EB0D64283F9C77}, // 91 + {0xC45D1DF942711D9A, 0x3BA5D0BD324F8395}, // 92 + {0xF5746577930D6500, 0xCA8F44EC7EE3647A}, // 93 + {0x9968BF6ABBE85F20, 0x7E998B13CF4E1ECC}, // 94 + {0xBFC2EF456AE276E8, 0x9E3FEDD8C321A67F}, // 95 + {0xEFB3AB16C59B14A2, 0xC5CFE94EF3EA101F}, // 96 + {0x95D04AEE3B80ECE5, 0xBBA1F1D158724A13}, // 97 + {0xBB445DA9CA61281F, 0x2A8A6E45AE8EDC98}, // 98 + {0xEA1575143CF97226, 0xF52D09D71A3293BE}, // 99 + {0x924D692CA61BE758, 0x593C2626705F9C57}, // 100 + {0xB6E0C377CFA2E12E, 0x6F8B2FB00C77836D}, // 101 + {0xE498F455C38B997A, 0x0B6DFB9C0F956448}, // 102 + {0x8EDF98B59A373FEC, 0x4724BD4189BD5EAD}, // 103 + {0xB2977EE300C50FE7, 0x58EDEC91EC2CB658}, // 104 + {0xDF3D5E9BC0F653E1, 0x2F2967B66737E3EE}, // 105 + {0x8B865B215899F46C, 0xBD79E0D20082EE75}, // 106 + {0xAE67F1E9AEC07187, 0xECD8590680A3AA12}, // 107 + {0xDA01EE641A708DE9, 0xE80E6F4820CC9496}, // 108 + {0x884134FE908658B2, 0x3109058D147FDCDE}, // 109 + {0xAA51823E34A7EEDE, 0xBD4B46F0599FD416}, // 110 + {0xD4E5E2CDC1D1EA96, 0x6C9E18AC7007C91B}, // 111 + {0x850FADC09923329E, 0x03E2CF6BC604DDB1}, // 112 + {0xA6539930BF6BFF45, 0x84DB8346B786151D}, // 113 + {0xCFE87F7CEF46FF16, 0xE612641865679A64}, // 114 + {0x81F14FAE158C5F6E, 0x4FCB7E8F3F60C07F}, // 115 + {0xA26DA3999AEF7749, 0xE3BE5E330F38F09E}, // 116 + {0xCB090C8001AB551C, 0x5CADF5BFD3072CC6}, // 117 + {0xFDCB4FA002162A63, 0x73D9732FC7C8F7F7}, // 118 + {0x9E9F11C4014DDA7E, 0x2867E7FDDCDD9AFB}, // 119 + {0xC646D63501A1511D, 0xB281E1FD541501B9}, // 120 + {0xF7D88BC24209A565, 0x1F225A7CA91A4227}, // 121 + {0x9AE757596946075F, 0x3375788DE9B06959}, // 122 + {0xC1A12D2FC3978937, 0x0052D6B1641C83AF}, // 123 + {0xF209787BB47D6B84, 0xC0678C5DBD23A49B}, // 124 + {0x9745EB4D50CE6332, 0xF840B7BA963646E1}, // 125 + {0xBD176620A501FBFF, 0xB650E5A93BC3D899}, // 126 + {0xEC5D3FA8CE427AFF, 0xA3E51F138AB4CEBF}, // 127 + {0x93BA47C980E98CDF, 0xC66F336C36B10138}, // 128 + {0xB8A8D9BBE123F017, 0xB80B0047445D4185}, // 129 + {0xE6D3102AD96CEC1D, 0xA60DC059157491E6}, // 130 + {0x9043EA1AC7E41392, 0x87C89837AD68DB30}, // 131 + {0xB454E4A179DD1877, 0x29BABE4598C311FC}, // 132 + {0xE16A1DC9D8545E94, 0xF4296DD6FEF3D67B}, // 133 + {0x8CE2529E2734BB1D, 0x1899E4A65F58660D}, // 134 + {0xB01AE745B101E9E4, 0x5EC05DCFF72E7F90}, // 135 + {0xDC21A1171D42645D, 0x76707543F4FA1F74}, // 136 + {0x899504AE72497EBA, 0x6A06494A791C53A9}, // 137 + {0xABFA45DA0EDBDE69, 0x0487DB9D17636893}, // 138 + {0xD6F8D7509292D603, 0x45A9D2845D3C42B7}, // 139 + {0x865B86925B9BC5C2, 0x0B8A2392BA45A9B3}, // 140 + {0xA7F26836F282B732, 0x8E6CAC7768D7141F}, // 141 + {0xD1EF0244AF2364FF, 0x3207D795430CD927}, // 142 + {0x8335616AED761F1F, 0x7F44E6BD49E807B9}, // 143 + {0xA402B9C5A8D3A6E7, 0x5F16206C9C6209A7}, // 144 + {0xCD036837130890A1, 0x36DBA887C37A8C10}, // 145 + {0x802221226BE55A64, 0xC2494954DA2C978A}, // 146 + {0xA02AA96B06DEB0FD, 0xF2DB9BAA10B7BD6D}, // 147 + {0xC83553C5C8965D3D, 0x6F92829494E5ACC8}, // 148 + {0xFA42A8B73ABBF48C, 0xCB772339BA1F17FA}, // 149 + {0x9C69A97284B578D7, 0xFF2A760414536EFC}, // 150 + {0xC38413CF25E2D70D, 0xFEF5138519684ABB}, // 151 + {0xF46518C2EF5B8CD1, 0x7EB258665FC25D6A}, // 152 + {0x98BF2F79D5993802, 0xEF2F773FFBD97A62}, // 153 + {0xBEEEFB584AFF8603, 0xAAFB550FFACFD8FB}, // 154 + {0xEEAABA2E5DBF6784, 0x95BA2A53F983CF39}, // 155 + {0x952AB45CFA97A0B2, 0xDD945A747BF26184}, // 156 + {0xBA756174393D88DF, 0x94F971119AEEF9E5}, // 157 + {0xE912B9D1478CEB17, 0x7A37CD5601AAB85E}, // 158 + {0x91ABB422CCB812EE, 0xAC62E055C10AB33B}, // 159 + {0xB616A12B7FE617AA, 0x577B986B314D600A}, // 160 + {0xE39C49765FDF9D94, 0xED5A7E85FDA0B80C}, // 161 + {0x8E41ADE9FBEBC27D, 0x14588F13BE847308}, // 162 + {0xB1D219647AE6B31C, 0x596EB2D8AE258FC9}, // 163 + {0xDE469FBD99A05FE3, 0x6FCA5F8ED9AEF3BC}, // 164 + {0x8AEC23D680043BEE, 0x25DE7BB9480D5855}, // 165 + {0xADA72CCC20054AE9, 0xAF561AA79A10AE6B}, // 166 + {0xD910F7FF28069DA4, 0x1B2BA1518094DA05}, // 167 + {0x87AA9AFF79042286, 0x90FB44D2F05D0843}, // 168 + {0xA99541BF57452B28, 0x353A1607AC744A54}, // 169 + {0xD3FA922F2D1675F2, 0x42889B8997915CE9}, // 170 + {0x847C9B5D7C2E09B7, 0x69956135FEBADA12}, // 171 + {0xA59BC234DB398C25, 0x43FAB9837E699096}, // 172 + {0xCF02B2C21207EF2E, 0x94F967E45E03F4BC}, // 173 + {0x8161AFB94B44F57D, 0x1D1BE0EEBAC278F6}, // 174 + {0xA1BA1BA79E1632DC, 0x6462D92A69731733}, // 175 + {0xCA28A291859BBF93, 0x7D7B8F7503CFDCFF}, // 176 + {0xFCB2CB35E702AF78, 0x5CDA735244C3D43F}, // 177 + {0x9DEFBF01B061ADAB, 0x3A0888136AFA64A8}, // 178 + {0xC56BAEC21C7A1916, 0x088AAA1845B8FDD1}, // 179 + {0xF6C69A72A3989F5B, 0x8AAD549E57273D46}, // 180 + {0x9A3C2087A63F6399, 0x36AC54E2F678864C}, // 181 + {0xC0CB28A98FCF3C7F, 0x84576A1BB416A7DE}, // 182 + {0xF0FDF2D3F3C30B9F, 0x656D44A2A11C51D6}, // 183 + {0x969EB7C47859E743, 0x9F644AE5A4B1B326}, // 184 + {0xBC4665B596706114, 0x873D5D9F0DDE1FEF}, // 185 + {0xEB57FF22FC0C7959, 0xA90CB506D155A7EB}, // 186 + {0x9316FF75DD87CBD8, 0x09A7F12442D588F3}, // 187 + {0xB7DCBF5354E9BECE, 0x0C11ED6D538AEB30}, // 188 + {0xE5D3EF282A242E81, 0x8F1668C8A86DA5FB}, // 189 + {0x8FA475791A569D10, 0xF96E017D694487BD}, // 190 + {0xB38D92D760EC4455, 0x37C981DCC395A9AD}, // 191 + {0xE070F78D3927556A, 0x85BBE253F47B1418}, // 192 + {0x8C469AB843B89562, 0x93956D7478CCEC8F}, // 193 + {0xAF58416654A6BABB, 0x387AC8D1970027B3}, // 194 + {0xDB2E51BFE9D0696A, 0x06997B05FCC0319F}, // 195 + {0x88FCF317F22241E2, 0x441FECE3BDF81F04}, // 196 + {0xAB3C2FDDEEAAD25A, 0xD527E81CAD7626C4}, // 197 + {0xD60B3BD56A5586F1, 0x8A71E223D8D3B075}, // 198 + {0x85C7056562757456, 0xF6872D5667844E4A}, // 199 + {0xA738C6BEBB12D16C, 0xB428F8AC016561DC}, // 200 + {0xD106F86E69D785C7, 0xE13336D701BEBA53}, // 201 + {0x82A45B450226B39C, 0xECC0024661173474}, // 202 + {0xA34D721642B06084, 0x27F002D7F95D0191}, // 203 + {0xCC20CE9BD35C78A5, 0x31EC038DF7B441F5}, // 204 + {0xFF290242C83396CE, 0x7E67047175A15272}, // 205 + {0x9F79A169BD203E41, 0x0F0062C6E984D387}, // 206 + {0xC75809C42C684DD1, 0x52C07B78A3E60869}, // 207 + {0xF92E0C3537826145, 0xA7709A56CCDF8A83}, // 208 + {0x9BBCC7A142B17CCB, 0x88A66076400BB692}, // 209 + {0xC2ABF989935DDBFE, 0x6ACFF893D00EA436}, // 210 + {0xF356F7EBF83552FE, 0x0583F6B8C4124D44}, // 211 + {0x98165AF37B2153DE, 0xC3727A337A8B704B}, // 212 + {0xBE1BF1B059E9A8D6, 0x744F18C0592E4C5D}, // 213 + {0xEDA2EE1C7064130C, 0x1162DEF06F79DF74}, // 214 + {0x9485D4D1C63E8BE7, 0x8ADDCB5645AC2BA9}, // 215 + {0xB9A74A0637CE2EE1, 0x6D953E2BD7173693}, // 216 + {0xE8111C87C5C1BA99, 0xC8FA8DB6CCDD0438}, // 217 + {0x910AB1D4DB9914A0, 0x1D9C9892400A22A3}, // 218 + {0xB54D5E4A127F59C8, 0x2503BEB6D00CAB4C}, // 219 + {0xE2A0B5DC971F303A, 0x2E44AE64840FD61E}, // 220 + {0x8DA471A9DE737E24, 0x5CEAECFED289E5D3}, // 221 + {0xB10D8E1456105DAD, 0x7425A83E872C5F48}, // 222 + {0xDD50F1996B947518, 0xD12F124E28F7771A}, // 223 + {0x8A5296FFE33CC92F, 0x82BD6B70D99AAA70}, // 224 + {0xACE73CBFDC0BFB7B, 0x636CC64D1001550C}, // 225 + {0xD8210BEFD30EFA5A, 0x3C47F7E05401AA4F}, // 226 + {0x8714A775E3E95C78, 0x65ACFAEC34810A72}, // 227 + {0xA8D9D1535CE3B396, 0x7F1839A741A14D0E}, // 228 + {0xD31045A8341CA07C, 0x1EDE48111209A051}, // 229 + {0x83EA2B892091E44D, 0x934AED0AAB460433}, // 230 + {0xA4E4B66B68B65D60, 0xF81DA84D56178540}, // 231 + {0xCE1DE40642E3F4B9, 0x36251260AB9D668F}, // 232 + {0x80D2AE83E9CE78F3, 0xC1D72B7C6B42601A}, // 233 + {0xA1075A24E4421730, 0xB24CF65B8612F820}, // 234 + {0xC94930AE1D529CFC, 0xDEE033F26797B628}, // 235 + {0xFB9B7CD9A4A7443C, 0x169840EF017DA3B2}, // 236 + {0x9D412E0806E88AA5, 0x8E1F289560EE864F}, // 237 + {0xC491798A08A2AD4E, 0xF1A6F2BAB92A27E3}, // 238 + {0xF5B5D7EC8ACB58A2, 0xAE10AF696774B1DC}, // 239 + {0x9991A6F3D6BF1765, 0xACCA6DA1E0A8EF2A}, // 240 + {0xBFF610B0CC6EDD3F, 0x17FD090A58D32AF4}, // 241 + {0xEFF394DCFF8A948E, 0xDDFC4B4CEF07F5B1}, // 242 + {0x95F83D0A1FB69CD9, 0x4ABDAF101564F98F}, // 243 + {0xBB764C4CA7A4440F, 0x9D6D1AD41ABE37F2}, // 244 + {0xEA53DF5FD18D5513, 0x84C86189216DC5EE}, // 245 + {0x92746B9BE2F8552C, 0x32FD3CF5B4E49BB5}, // 246 + {0xB7118682DBB66A77, 0x3FBC8C33221DC2A2}, // 247 + {0xE4D5E82392A40515, 0x0FABAF3FEAA5334B}, // 248 + {0x8F05B1163BA6832D, 0x29CB4D87F2A7400F}, // 249 + {0xB2C71D5BCA9023F8, 0x743E20E9EF511013}, // 250 + {0xDF78E4B2BD342CF6, 0x914DA9246B255417}, // 251 + {0x8BAB8EEFB6409C1A, 0x1AD089B6C2F7548F}, // 252 + {0xAE9672ABA3D0C320, 0xA184AC2473B529B2}, // 253 + {0xDA3C0F568CC4F3E8, 0xC9E5D72D90A2741F}, // 254 + {0x8865899617FB1871, 0x7E2FA67C7A658893}, // 255 + {0xAA7EEBFB9DF9DE8D, 0xDDBB901B98FEEAB8}, // 256 + {0xD51EA6FA85785631, 0x552A74227F3EA566}, // 257 + {0x8533285C936B35DE, 0xD53A88958F872760}, // 258 + {0xA67FF273B8460356, 0x8A892ABAF368F138}, // 259 + {0xD01FEF10A657842C, 0x2D2B7569B0432D86}, // 260 + {0x8213F56A67F6B29B, 0x9C3B29620E29FC74}, // 261 + {0xA298F2C501F45F42, 0x8349F3BA91B47B90}, // 262 + {0xCB3F2F7642717713, 0x241C70A936219A74}, // 263 + {0xFE0EFB53D30DD4D7, 0xED238CD383AA0111}, // 264 + {0x9EC95D1463E8A506, 0xF4363804324A40AB}, // 265 + {0xC67BB4597CE2CE48, 0xB143C6053EDCD0D6}, // 266 + {0xF81AA16FDC1B81DA, 0xDD94B7868E94050B}, // 267 + {0x9B10A4E5E9913128, 0xCA7CF2B4191C8327}, // 268 + {0xC1D4CE1F63F57D72, 0xFD1C2F611F63A3F1}, // 269 + {0xF24A01A73CF2DCCF, 0xBC633B39673C8CED}, // 270 + {0x976E41088617CA01, 0xD5BE0503E085D814}, // 271 + {0xBD49D14AA79DBC82, 0x4B2D8644D8A74E19}, // 272 + {0xEC9C459D51852BA2, 0xDDF8E7D60ED1219F}, // 273 + {0x93E1AB8252F33B45, 0xCABB90E5C942B504}, // 274 + {0xB8DA1662E7B00A17, 0x3D6A751F3B936244}, // 275 + {0xE7109BFBA19C0C9D, 0x0CC512670A783AD5}, // 276 + {0x906A617D450187E2, 0x27FB2B80668B24C6}, // 277 + {0xB484F9DC9641E9DA, 0xB1F9F660802DEDF7}, // 278 + {0xE1A63853BBD26451, 0x5E7873F8A0396974}, // 279 + {0x8D07E33455637EB2, 0xDB0B487B6423E1E9}, // 280 + {0xB049DC016ABC5E5F, 0x91CE1A9A3D2CDA63}, // 281 + {0xDC5C5301C56B75F7, 0x7641A140CC7810FC}, // 282 + {0x89B9B3E11B6329BA, 0xA9E904C87FCB0A9E}, // 283 + {0xAC2820D9623BF429, 0x546345FA9FBDCD45}, // 284 + {0xD732290FBACAF133, 0xA97C177947AD4096}, // 285 + {0x867F59A9D4BED6C0, 0x49ED8EABCCCC485E}, // 286 + {0xA81F301449EE8C70, 0x5C68F256BFFF5A75}, // 287 + {0xD226FC195C6A2F8C, 0x73832EEC6FFF3112}, // 288 + {0x83585D8FD9C25DB7, 0xC831FD53C5FF7EAC}, // 289 + {0xA42E74F3D032F525, 0xBA3E7CA8B77F5E56}, // 290 + {0xCD3A1230C43FB26F, 0x28CE1BD2E55F35EC}, // 291 + {0x80444B5E7AA7CF85, 0x7980D163CF5B81B4}, // 292 + {0xA0555E361951C366, 0xD7E105BCC3326220}, // 293 + {0xC86AB5C39FA63440, 0x8DD9472BF3FEFAA8}, // 294 + {0xFA856334878FC150, 0xB14F98F6F0FEB952}, // 295 + {0x9C935E00D4B9D8D2, 0x6ED1BF9A569F33D4}, // 296 + {0xC3B8358109E84F07, 0x0A862F80EC4700C9}, // 297 + {0xF4A642E14C6262C8, 0xCD27BB612758C0FB}, // 298 + {0x98E7E9CCCFBD7DBD, 0x8038D51CB897789D}, // 299 + {0xBF21E44003ACDD2C, 0xE0470A63E6BD56C4}, // 300 + {0xEEEA5D5004981478, 0x1858CCFCE06CAC75}, // 301 + {0x95527A5202DF0CCB, 0x0F37801E0C43EBC9}, // 302 + {0xBAA718E68396CFFD, 0xD30560258F54E6BB}, // 303 + {0xE950DF20247C83FD, 0x47C6B82EF32A206A}, // 304 + {0x91D28B7416CDD27E, 0x4CDC331D57FA5442}, // 305 + {0xB6472E511C81471D, 0xE0133FE4ADF8E953}, // 306 + {0xE3D8F9E563A198E5, 0x58180FDDD97723A7}, // 307 + {0x8E679C2F5E44FF8F, 0x570F09EAA7EA7649}, // 308 + {0xB201833B35D63F73, 0x2CD2CC6551E513DB}, // 309 + {0xDE81E40A034BCF4F, 0xF8077F7EA65E58D2}, // 310 + {0x8B112E86420F6191, 0xFB04AFAF27FAF783}, // 311 + {0xADD57A27D29339F6, 0x79C5DB9AF1F9B564}, // 312 + {0xD94AD8B1C7380874, 0x18375281AE7822BD}, // 313 + {0x87CEC76F1C830548, 0x8F2293910D0B15B6}, // 314 + {0xA9C2794AE3A3C69A, 0xB2EB3875504DDB23}, // 315 + {0xD433179D9C8CB841, 0x5FA60692A46151EC}, // 316 + {0x849FEEC281D7F328, 0xDBC7C41BA6BCD334}, // 317 + {0xA5C7EA73224DEFF3, 0x12B9B522906C0801}, // 318 + {0xCF39E50FEAE16BEF, 0xD768226B34870A01}, // 319 + {0x81842F29F2CCE375, 0xE6A1158300D46641}, // 320 + {0xA1E53AF46F801C53, 0x60495AE3C1097FD1}, // 321 + {0xCA5E89B18B602368, 0x385BB19CB14BDFC5}, // 322 + {0xFCF62C1DEE382C42, 0x46729E03DD9ED7B6}, // 323 + {0x9E19DB92B4E31BA9, 0x6C07A2C26A8346D2}, // 324 + }; + + SF_ASSERT(k >= kMin); + SF_ASSERT(k <= kMax); + return g[static_cast(k - kMin)]; +} + +#if defined(__SIZEOF_INT128__) + +static inline uint64_t RoundToOdd(uint64x2 g, uint64_t cp) +{ + __extension__ using uint128_t = unsigned __int128; + + const uint128_t x = uint128_t{cp} * g.lo; + const uint128_t y = uint128_t{cp} * g.hi + static_cast(x >> 64); + + const uint64_t y0 = static_cast(y); + const uint64_t y1 = static_cast(y >> 64); + return y1 | (y0 > 1); +} + +#elif defined(_MSC_VER) && defined(_M_X64) + +static inline uint64_t RoundToOdd(uint64x2 g, uint64_t cp) +{ + uint64_t x1 = 0; + uint64_t x0 = _umul128(g.lo, cp, &x1); + uint64_t y1 = 0; + uint64_t y0 = _umul128(g.hi, cp, &y1); + static_cast(x0); + + // y0 += x1; + // y1 += y0 < x1; + _addcarry_u64(_addcarry_u64(0, y0, x1, &y0), y1, 0, &y1); + + return y1 | (y0 > 1); +} + +#else + +static inline uint32_t Lo32(uint64_t x) +{ + return static_cast(x); +} + +static inline uint32_t Hi32(uint64_t x) +{ + return static_cast(x >> 32); +} + +static inline uint64x2 Mul128(uint64_t a, uint64_t b) +{ + const uint64_t b00 = uint64_t{Lo32(a)} * Lo32(b); + const uint64_t b01 = uint64_t{Lo32(a)} * Hi32(b); + const uint64_t b10 = uint64_t{Hi32(a)} * Lo32(b); + const uint64_t b11 = uint64_t{Hi32(a)} * Hi32(b); + + const uint64_t mid1 = b10 + Hi32(b00); + const uint64_t mid2 = b01 + Lo32(mid1); + + const uint64_t hi = b11 + Hi32(mid1) + Hi32(mid2); + const uint64_t lo = Lo32(b00) | uint64_t{Lo32(mid2)} << 32; + return {hi, lo}; +} + +static inline uint64_t RoundToOdd(uint64x2 g, uint64_t cp) +{ + uint64_t x = Mul128(g.lo, cp).hi; + uint64x2 y = Mul128(g.hi, cp); + + y.lo += x; + y.hi += y.lo < x; + + return y.hi | (y.lo > 1); +} + +#endif + +// Returns whether value is divisible by 2^e2 +static inline bool MultipleOfPow2(uint64_t value, int32_t e2) +{ + SF_ASSERT(e2 >= 0); + SF_ASSERT(e2 <= 63); + return (value & ((uint64_t{1} << e2) - 1)) == 0; +} + +namespace { +struct FloatingDecimal64 { + uint64_t digits; // num_digits <= 17 + int32_t exponent; +}; +} + +static inline FloatingDecimal64 ToDecimal64(uint64_t ieee_significand, uint64_t ieee_exponent) +{ + uint64_t c; + int32_t q; + if (ieee_exponent != 0) + { + c = Double::HiddenBit | ieee_significand; + q = static_cast(ieee_exponent) - Double::ExponentBias; + + if (0 <= -q && -q < Double::SignificandSize && MultipleOfPow2(c, -q)) + { + return {c >> -q, 0}; + } + } + else + { + c = ieee_significand; + q = 1 - Double::ExponentBias; + } + + const bool is_even = (c % 2 == 0); + const bool accept_lower = is_even; + const bool accept_upper = is_even; + + const bool lower_boundary_is_closer = (ieee_significand == 0 && ieee_exponent > 1); + +// const int32_t qb = q - 2; + const uint64_t cbl = 4 * c - 2 + lower_boundary_is_closer; + const uint64_t cb = 4 * c; + const uint64_t cbr = 4 * c + 2; + + // (q * 1262611 ) >> 22 == floor(log_10( 2^q)) + // (q * 1262611 - 524031) >> 22 == floor(log_10(3/4 2^q)) + SF_ASSERT(q >= -1500); + SF_ASSERT(q <= 1500); + const int32_t k = FloorDivPow2(q * 1262611 - (lower_boundary_is_closer ? 524031 : 0), 22); + + const int32_t h = q + FloorLog2Pow10(-k) + 1; + SF_ASSERT(h >= 1); + SF_ASSERT(h <= 4); + + const uint64x2 pow10 = ComputePow10_Double(-k); + const uint64_t vbl = RoundToOdd(pow10, cbl << h); + const uint64_t vb = RoundToOdd(pow10, cb << h); + const uint64_t vbr = RoundToOdd(pow10, cbr << h); + + const uint64_t lower = vbl + !accept_lower; + const uint64_t upper = vbr - !accept_upper; + + // See Figure 4 in [1]. + // And the modifications in Figure 6. + + const uint64_t s = vb / 4; // NB: 4 * s == vb & ~3 == vb & -4 + + if (s >= 10) // vb >= 40 + { + const uint64_t sp = s / 10; // = vb / 40 + const bool up_inside = lower <= 40 * sp; + const bool wp_inside = 40 * sp + 40 <= upper; +// if (up_inside || wp_inside) // NB: At most one of u' and w' is in R_v. + if (up_inside != wp_inside) + { + return {sp + wp_inside, k + 1}; + } + } + + const bool u_inside = lower <= 4 * s; + const bool w_inside = 4 * s + 4 <= upper; + if (u_inside != w_inside) + { + return {s + w_inside, k}; + } + + // NB: s & 1 == vb & 0x4 + const uint64_t mid = 4 * s + 2; // = 2(s + t) + const bool round_up = vb > mid || (vb == mid && (s & 1) != 0); + + return {s + round_up, k}; +} + +//================================================================================================== +// ToChars +//================================================================================================== + +static inline void Utoa_2Digits(char* buf, uint32_t digits) +{ + static constexpr char Digits100[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9', + }; + + SF_ASSERT(digits <= 99); + std::memcpy(buf, &Digits100[2 * digits], 2 * sizeof(char)); +} + +static inline int TrailingZeros_2Digits(uint32_t digits) +{ + static constexpr int8_t TrailingZeros100[100] = { + 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }; + + SF_ASSERT(digits <= 99); + return TrailingZeros100[digits]; +} + +static inline int Utoa_8Digits_skip_trailing_zeros(char* buf, uint32_t digits) +{ + SF_ASSERT(digits >= 1); + SF_ASSERT(digits <= 99999999); + + const uint32_t q = digits / 10000; + const uint32_t r = digits % 10000; + + const uint32_t qH = q / 100; + const uint32_t qL = q % 100; + Utoa_2Digits(buf + 0, qH); + Utoa_2Digits(buf + 2, qL); + + if (r == 0) + { + return TrailingZeros_2Digits(qL == 0 ? qH : qL) + (qL == 0 ? 6 : 4); + } + else + { + const uint32_t rH = r / 100; + const uint32_t rL = r % 100; + Utoa_2Digits(buf + 4, rH); + Utoa_2Digits(buf + 6, rL); + + return TrailingZeros_2Digits(rL == 0 ? rH : rL) + (rL == 0 ? 2 : 0); + } +} + +static inline int PrintDecimalDigitsBackwards(char* buf, uint64_t output64) +{ + int tz = 0; // number of trailing zeros removed. + int nd = 0; // number of decimal digits processed. + + // At most 17 digits remaining + + if (output64 >= 100000000) + { + const uint64_t q = output64 / 100000000; + const uint32_t r = static_cast(output64 % 100000000); + output64 = q; + buf -= 8; + if (r != 0) + { + tz = Utoa_8Digits_skip_trailing_zeros(buf, r); + SF_ASSERT(tz >= 0); + SF_ASSERT(tz <= 7); + } + else + { + tz = 8; + } + nd = 8; + } + + // At most 9 digits remaining. + SF_ASSERT(output64 <= UINT32_MAX); + uint32_t output = static_cast(output64); + + if (output >= 10000) + { + const uint32_t q = output / 10000; + const uint32_t r = output % 10000; + output = q; + buf -= 4; + if (r != 0) + { + const uint32_t rH = r / 100; + const uint32_t rL = r % 100; + Utoa_2Digits(buf + 0, rH); + Utoa_2Digits(buf + 2, rL); + if (tz == nd) + { + tz += TrailingZeros_2Digits(rL == 0 ? rH : rL) + (rL == 0 ? 2 : 0); + } + } + else + { + if (tz == nd) + tz += 4; + else + std::memset(buf, '0', 4); // (actually not required...) + } + nd += 4; + } + + // At most 5 digits remaining. + + if (output >= 100) + { + const uint32_t q = output / 100; + const uint32_t r = output % 100; + output = q; + buf -= 2; + Utoa_2Digits(buf, r); + if (tz == nd) + { + tz += TrailingZeros_2Digits(r); + } + nd += 2; + + if (output >= 100) + { + const uint32_t q2 = output / 100; + const uint32_t r2 = output % 100; + output = q2; + buf -= 2; + Utoa_2Digits(buf, r2); + if (tz == nd) + { + tz += TrailingZeros_2Digits(r2); + } + nd += 2; + } + } + + // At most 2 digits remaining. + + SF_ASSERT(output >= 1); + SF_ASSERT(output <= 99); + + if (output >= 10) + { + const uint32_t q = output; + buf -= 2; + Utoa_2Digits(buf, q); + if (tz == nd) + { + tz += TrailingZeros_2Digits(q); + } +// nd += 2; + } + else + { + const uint32_t q = output; + SF_ASSERT(q >= 1); + SF_ASSERT(q <= 9); + *--buf = static_cast('0' + q); + } + + return tz; +} + +static inline int32_t DecimalLength(uint64_t v) +{ + SF_ASSERT(v >= 1); + SF_ASSERT(v <= 99999999999999999ull); + + if (static_cast(v >> 32) != 0) + { + if (v >= 10000000000000000ull) { return 17; } + if (v >= 1000000000000000ull) { return 16; } + if (v >= 100000000000000ull) { return 15; } + if (v >= 10000000000000ull) { return 14; } + if (v >= 1000000000000ull) { return 13; } + if (v >= 100000000000ull) { return 12; } + if (v >= 10000000000ull) { return 11; } + return 10; + } + + const uint32_t v32 = static_cast(v); + if (v32 >= 1000000000u) { return 10; } + if (v32 >= 100000000u) { return 9; } + if (v32 >= 10000000u) { return 8; } + if (v32 >= 1000000u) { return 7; } + if (v32 >= 100000u) { return 6; } + if (v32 >= 10000u) { return 5; } + if (v32 >= 1000u) { return 4; } + if (v32 >= 100u) { return 3; } + if (v32 >= 10u) { return 2; } + return 1; +} + +static inline char* FormatDigits(char* buffer, uint64_t digits, int32_t decimal_exponent, bool force_trailing_dot_zero = false) +{ + static constexpr int32_t MinFixedDecimalPoint = -6; + static constexpr int32_t MaxFixedDecimalPoint = 17; + static_assert(MinFixedDecimalPoint <= -1, "internal error"); + static_assert(MaxFixedDecimalPoint >= 17, "internal error"); + + SF_ASSERT(digits >= 1); + SF_ASSERT(digits <= 99999999999999999ull); + SF_ASSERT(decimal_exponent >= -999); + SF_ASSERT(decimal_exponent <= 999); + + int32_t num_digits = DecimalLength(digits); + const int32_t decimal_point = num_digits + decimal_exponent; + + const bool use_fixed = MinFixedDecimalPoint <= decimal_point && decimal_point <= MaxFixedDecimalPoint; + + // Prepare the buffer. + // Avoid calling memset/memcpy with variable arguments below... + + std::memset(buffer + 0, '0', 16); + std::memset(buffer + 16, '0', 16); + static_assert(MinFixedDecimalPoint >= -30, "internal error"); + static_assert(MaxFixedDecimalPoint <= 32, "internal error"); + + int32_t decimal_digits_position; + if (use_fixed) + { + if (decimal_point <= 0) + { + // 0.[000]digits + decimal_digits_position = 2 - decimal_point; + } + else + { + // dig.its + // digits[000] + decimal_digits_position = 0; + } + } + else + { + // dE+123 or d.igitsE+123 + decimal_digits_position = 1; + } + + char* digits_end = buffer + decimal_digits_position + num_digits; + + const int tz = PrintDecimalDigitsBackwards(digits_end, digits); + digits_end -= tz; + num_digits -= tz; +// decimal_exponent += tz; // => decimal_point unchanged. + + if (use_fixed) + { + if (decimal_point <= 0) + { + // 0.[000]digits + buffer[1] = '.'; + buffer = digits_end; + } + else if (decimal_point < num_digits) + { + // dig.its +#if defined(_MSC_VER) && !defined(__clang__) + // VC does not inline the memmove call below. (Even if compiled with /arch:AVX2.) + // However, memcpy will be inlined. + uint8_t tmp[16]; + char* const src = buffer + decimal_point; + char* const dst = src + 1; + std::memcpy(tmp, src, 16); + std::memcpy(dst, tmp, 16); +#else + std::memmove(buffer + decimal_point + 1, buffer + decimal_point, 16); +#endif + buffer[decimal_point] = '.'; + buffer = digits_end + 1; + } + else + { + // digits[000] + buffer += decimal_point; + if (force_trailing_dot_zero) + { + std::memcpy(buffer, ".0", 2); + buffer += 2; + } + } + } + else + { + // Copy the first digit one place to the left. + buffer[0] = buffer[1]; + if (num_digits == 1) + { + // dE+123 + ++buffer; + } + else + { + // d.igitsE+123 + buffer[1] = '.'; + buffer = digits_end; + } + + const int32_t scientific_exponent = decimal_point - 1; +// SF_ASSERT(scientific_exponent != 0); + + std::memcpy(buffer, scientific_exponent < 0 ? "e-" : "e+", 2); + buffer += 2; + + const uint32_t k = static_cast(scientific_exponent < 0 ? -scientific_exponent : scientific_exponent); + if (k < 10) + { + *buffer++ = static_cast('0' + k); + } + else if (k < 100) + { + Utoa_2Digits(buffer, k); + buffer += 2; + } + else + { + const uint32_t q = k / 100; + const uint32_t r = k % 100; + *buffer++ = static_cast('0' + q); + Utoa_2Digits(buffer, r); + buffer += 2; + } + } + + return buffer; +} + +static inline char* ToChars(char* buffer, double value, bool force_trailing_dot_zero = false) +{ + const Double v(value); + + const uint64_t significand = v.PhysicalSignificand(); + const uint64_t exponent = v.PhysicalExponent(); + + if (exponent != Double::MaxIeeeExponent) // [[likely]] + { + // Finite + + buffer[0] = '-'; + buffer += v.SignBit(); + + if (exponent != 0 || significand != 0) // [[likely]] + { + // != 0 + + const auto dec = ToDecimal64(significand, exponent); + return FormatDigits(buffer, dec.digits, dec.exponent, force_trailing_dot_zero); + } + else + { + std::memcpy(buffer, "0.0 ", 4); + buffer += force_trailing_dot_zero ? 3 : 1; + return buffer; + } + } + + if (significand == 0) + { + buffer[0] = '-'; + buffer += v.SignBit(); + + std::memcpy(buffer, "inf ", 4); + return buffer + 3; + } + else + { + std::memcpy(buffer, "nan ", 4); + return buffer + 3; + } +} + +//================================================================================================== +// +//================================================================================================== + +char* schubfach::Dtoa(char* buffer, double value) +{ + return ToChars(buffer, value); +} diff --git a/lib/vendor/drachennest/schubfach_64.h b/lib/vendor/drachennest/schubfach_64.h new file mode 100644 index 000000000000..10d9a12f98a9 --- /dev/null +++ b/lib/vendor/drachennest/schubfach_64.h @@ -0,0 +1,31 @@ +// Copyright 2020 Alexander Bolz +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +#pragma once + +namespace schubfach { + +// char* output_end = Dtoa(buffer, value); +// +// Converts the given double-precision number into decimal form and stores the result in the given +// buffer. +// +// The buffer must be large enough, i.e. >= DtoaMinBufferLength. +// The output format is similar to printf("%g"). +// The output is _not_ null-terminted. +// +// The output is optimal, i.e. the output string +// 1. rounds back to the input number when read in (using round-to-nearest-even) +// 2. is as short as possible, +// 3. is as close to the input number as possible. +// +// Note: +// This function may temporarily write up to DtoaMinBufferLength characters into the buffer. + +constexpr int DtoaMinBufferLength = 64; + +char* Dtoa(char* buffer, double value); + +} // namespace schubfach diff --git a/lib/vendor/dragonbox/dragonbox_to_chars.cpp b/lib/vendor/dragonbox/dragonbox_to_chars.cpp new file mode 100644 index 000000000000..eb99d28158f6 --- /dev/null +++ b/lib/vendor/dragonbox/dragonbox_to_chars.cpp @@ -0,0 +1,238 @@ +// The contents of this file is based on contents of: +// +// https://github.com/ulfjack/ryu/blob/master/ryu/common.h, +// https://github.com/ulfjack/ryu/blob/master/ryu/d2s.c, and +// https://github.com/ulfjack/ryu/blob/master/ryu/f2s.c, +// +// which are distributed under the following terms: +//-------------------------------------------------------------------------------- +// Copyright 2018 Ulf Adams +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// (See accompanying file LICENSE-Apache or copy at +// http://www.apache.org/licenses/LICENSE-2.0) +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE-Boost or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. +//-------------------------------------------------------------------------------- +// Modifications Copyright 2020 Junekey Jeon +// +// Following modifications were made to the original contents: +// - Put everything inside the namespace jkj::dragonbox::to_chars_detail +// - Combined decimalLength9 (from common.h) and decimalLength17 (from d2s.c) +// into a single template function decimal_length +// - Combined to_chars (from f2s.c) and to_chars (from d2s.c) into a +// single template function fp_to_chars_impl +// - Removed index counting statements; replaced them with pointer increments +// - Removed usages of DIGIT_TABLE; replaced them with radix_100_table +// +// These modifications, together with other contents of this file may be used +// under the same terms as the original contents. + + +#include "dragonbox/dragonbox_to_chars.h" + +namespace jkj::dragonbox { + namespace to_chars_detail { + static constexpr char radix_100_table[] = { + '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', + '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', + '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', + '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', + '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', + '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', + '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', + '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', + '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', + '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', + '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', + '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', + '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', + '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', + '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', + '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', + '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', + '9', '5', '9', '6', '9', '7', '9', '8', '9', '9' + }; + + template + static constexpr std::uint32_t decimal_length(UInt const v) { + if constexpr (std::is_same_v) { + // Function precondition: v is not a 10-digit number. + // (f2s: 9 digits are sufficient for round-tripping.) + // (d2fixed: We print 9-digit blocks.) + assert(v < 1000000000); + if (v >= 100000000) { return 9; } + if (v >= 10000000) { return 8; } + if (v >= 1000000) { return 7; } + if (v >= 100000) { return 6; } + if (v >= 10000) { return 5; } + if (v >= 1000) { return 4; } + if (v >= 100) { return 3; } + if (v >= 10) { return 2; } + return 1; + } + else { + static_assert(std::is_same_v); + // This is slightly faster than a loop. + // The average output length is 16.38 digits, so we check high-to-low. + // Function precondition: v is not an 18, 19, or 20-digit number. + // (17 digits are sufficient for round-tripping.) + assert(v < 100000000000000000L); + 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; + } + } + + template + static char* to_chars_impl(unsigned_fp_t v, char* buffer) + { + auto output = v.significand; + auto const olength = decimal_length(output); + + // Print the decimal digits. + // The following code is equivalent to: + // for (uint32_t i = 0; i < olength - 1; ++i) { + // const uint32_t c = output % 10; output /= 10; + // result[index + olength - i] = (char) ('0' + c); + // } + // result[index] = '0' + output % 10; + + uint32_t i = 0; + if constexpr (sizeof(Float) == 8) { + // We prefer 32-bit operations, even on 64-bit platforms. + // We have at most 17 digits, and uint32_t can store 9 digits. + // If output doesn't fit into uint32_t, we cut off 8 digits, + // so the rest will fit into uint32_t. + if ((output >> 32) != 0) { + // Expensive 64-bit division. + const uint64_t q = output / 100000000; + uint32_t output2 = ((uint32_t)output) - 100000000 * ((uint32_t)q); + output = q; + + const uint32_t c = output2 % 10000; + output2 /= 10000; + const uint32_t d = output2 % 10000; + const uint32_t c0 = (c % 100) << 1; + const uint32_t c1 = (c / 100) << 1; + const uint32_t d0 = (d % 100) << 1; + const uint32_t d1 = (d / 100) << 1; + memcpy(buffer + olength - i - 1, radix_100_table + c0, 2); + memcpy(buffer + olength - i - 3, radix_100_table + c1, 2); + memcpy(buffer + olength - i - 5, radix_100_table + d0, 2); + memcpy(buffer + olength - i - 7, radix_100_table + d1, 2); + i += 8; + } + } + + auto output2 = (uint32_t)output; + while (output2 >= 10000) { +#ifdef __clang__ // https://bugs.llvm.org/show_bug.cgi?id=38217 + const uint32_t c = output2 - 10000 * (output2 / 10000); +#else + const uint32_t c = output2 % 10000; +#endif + output2 /= 10000; + const uint32_t c0 = (c % 100) << 1; + const uint32_t c1 = (c / 100) << 1; + memcpy(buffer + olength - i - 1, radix_100_table + c0, 2); + memcpy(buffer + olength - i - 3, radix_100_table + c1, 2); + i += 4; + } + if (output2 >= 100) { + const uint32_t c = (output2 % 100) << 1; + output2 /= 100; + memcpy(buffer + olength - i - 1, radix_100_table + c, 2); + i += 2; + } + if (output2 >= 10) { + const uint32_t c = output2 << 1; + // We can't use memcpy here: the decimal dot goes between these two digits. + buffer[olength - i] = radix_100_table[c + 1]; + buffer[0] = radix_100_table[c]; + } + else { + buffer[0] = (char)('0' + output2); + } + + // Print decimal point if needed. + if (olength > 1) { + buffer[1] = '.'; + buffer += olength + 1; + } + else { + ++buffer; + } + + // Print the exponent. + *buffer = 'E'; + ++buffer; + int32_t exp = v.exponent + (int32_t)olength - 1; + if (exp < 0) { + *buffer = '-'; + ++buffer; + exp = -exp; + } + if constexpr (sizeof(Float) == 8) { + if (exp >= 100) { + const int32_t c = exp % 10; + memcpy(buffer, radix_100_table + 2 * (exp / 10), 2); + buffer[2] = (char)('0' + c); + buffer += 3; + } + else if (exp >= 10) { + memcpy(buffer, radix_100_table + 2 * exp, 2); + buffer += 2; + } + else { + *buffer = (char)('0' + exp); + ++buffer; + } + } + else { + if (exp >= 10) { + memcpy(buffer, radix_100_table + 2 * exp, 2); + buffer += 2; + } + else { + *buffer = (char)('0' + exp); + ++buffer; + } + } + + return buffer; + } + + char* to_chars(unsigned_fp_t v, char* buffer) { + return to_chars_impl(v, buffer); + } + char* to_chars(unsigned_fp_t v, char* buffer) { + return to_chars_impl(v, buffer); + } + } +} diff --git a/lib/vendor/dragonbox/impl.cc b/lib/vendor/dragonbox/impl.cc new file mode 100644 index 000000000000..fe14185c3b08 --- /dev/null +++ b/lib/vendor/dragonbox/impl.cc @@ -0,0 +1,10 @@ +#include "dragonbox/dragonbox_to_chars.h" + +extern "C" char* nimtoStringDragonbox0ImplDouble(char* buffer, double value){ + return jkj::dragonbox::to_chars_n(value, buffer); + // could also pass options, eg: `jkj::dragonbox::policy::cache::compressed` +} + +extern "C" char* nimtoStringDragonbox0ImplFloat(char* buffer, float value){ + return jkj::dragonbox::to_chars_n(value, buffer); +} diff --git a/lib/vendor/dragonbox/include/dragonbox/dragonbox.h b/lib/vendor/dragonbox/include/dragonbox/dragonbox.h new file mode 100644 index 000000000000..9280226bd990 --- /dev/null +++ b/lib/vendor/dragonbox/include/dragonbox/dragonbox.h @@ -0,0 +1,3308 @@ +// Copyright 2020 Junekey Jeon +// +// The contents of this file may be used under the terms of +// the Apache License v2.0 with LLVM Exceptions. +// +// (See accompanying file LICENSE-Apache or copy at +// https://llvm.org/foundation/relicensing/LICENSE.txt) +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE-Boost or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. + + +#ifndef JKJ_DRAGONBOX +#define JKJ_DRAGONBOX + +#include +#include +#include +#include +#include + +// Suppress additional buffer overrun check +// I have no idea why MSVC thinks some functions here are vulnerable to the buffer overrun attacks +// No, they aren't. +#if defined(__GNUC__) || defined(__clang__) +#define JKJ_SAFEBUFFERS +#define JKJ_FORCEINLINE inline __attribute__((always_inline)) +#elif defined(_MSC_VER) +#define JKJ_SAFEBUFFERS __declspec(safebuffers) +#define JKJ_FORCEINLINE __forceinline +#else +#define JKJ_SAFEBUFFERS +#define JKJ_FORCEINLINE inline +#endif + +#if defined(_MSC_VER) +#include +#endif + +namespace jkj::dragonbox { + namespace detail { + template + constexpr std::size_t physical_bits = sizeof(T) * std::numeric_limits::digits; + + template + constexpr std::size_t value_bits = + std::numeric_limits, T>>::digits; + } + + enum class ieee754_format { + binary32, + binary64 + }; + + template + struct ieee754_format_info; + + template <> + struct ieee754_format_info { + static constexpr auto format = ieee754_format::binary32; + static constexpr int significand_bits = 23; + static constexpr int exponent_bits = 8; + static constexpr int min_exponent = -126; + static constexpr int max_exponent = 127; + static constexpr int exponent_bias = -127; + static constexpr int decimal_digits = 9; + }; + + template <> + struct ieee754_format_info { + static constexpr auto format = ieee754_format::binary64; + static constexpr int significand_bits = 52; + static constexpr int exponent_bits = 11; + static constexpr int min_exponent = -1022; + static constexpr int max_exponent = 1023; + static constexpr int exponent_bias = -1023; + static constexpr int decimal_digits = 17; + }; + + // To reduce boilerplates + template + struct default_ieee754_traits { + static_assert(detail::physical_bits == 32 || detail::physical_bits == 64); + + using type = T; + static constexpr ieee754_format format = + detail::physical_bits == 32 ? ieee754_format::binary32 : ieee754_format::binary64; + + using carrier_uint = std::conditional_t< + detail::physical_bits == 32, + std::uint32_t, + std::uint64_t>; + static_assert(sizeof(carrier_uint) == sizeof(T)); + + static constexpr int carrier_bits = int(detail::physical_bits); + + static T carrier_to_float(carrier_uint u) noexcept { + T x; + std::memcpy(&x, &u, sizeof(carrier_uint)); + return x; + } + static carrier_uint float_to_carrier(T x) noexcept { + carrier_uint u; + std::memcpy(&u, &x, sizeof(carrier_uint)); + return u; + } + + static constexpr unsigned int extract_exponent_bits(carrier_uint u) noexcept { + constexpr int significand_bits = ieee754_format_info::significand_bits; + constexpr int exponent_bits = ieee754_format_info::exponent_bits; + static_assert(detail::value_bits > exponent_bits); + constexpr auto exponent_bits_mask = (unsigned int)(((unsigned int)(1) << exponent_bits) - 1); + return (unsigned int)((u >> significand_bits) & exponent_bits_mask); + } + static constexpr carrier_uint extract_significand_bits(carrier_uint u) noexcept { + constexpr int significand_bits = ieee754_format_info::significand_bits; + constexpr auto significand_bits_mask = carrier_uint((carrier_uint(1) << significand_bits) - 1); + return carrier_uint(u & significand_bits_mask); + } + + // Allows positive zero and positive NaN's, but not allow negative zero + static constexpr bool is_positive(carrier_uint u) noexcept { + return (u >> (carrier_bits - 1)) == 0; + } + // Allows negative zero and negative NaN's, but not allow positive zero + static constexpr bool is_negative(carrier_uint u) noexcept { + return (u >> (carrier_bits - 1)) != 0; + } + + static constexpr int exponent_bias = 1 - (1 << (carrier_bits - ieee754_format_info::significand_bits - 2)); + + static constexpr bool is_finite(carrier_uint u) noexcept { + constexpr int significand_bits = ieee754_format_info::significand_bits; + constexpr int exponent_bits = ieee754_format_info::exponent_bits; + constexpr auto exponent_bits_mask = + carrier_uint(((carrier_uint(1) << exponent_bits) - 1) << significand_bits); + + return (u & exponent_bits_mask) != exponent_bits_mask; + } + static constexpr bool is_nonzero(carrier_uint u) noexcept { + return (u << 1) != 0; + } + // Allows positive and negative zeros + static constexpr bool is_subnormal(carrier_uint u) noexcept { + constexpr int significand_bits = ieee754_format_info::significand_bits; + constexpr int exponent_bits = ieee754_format_info::exponent_bits; + constexpr auto exponent_bits_mask = + carrier_uint(((carrier_uint(1) << exponent_bits) - 1) << significand_bits); + + return (u & exponent_bits_mask) == 0; + } + static constexpr bool is_positive_infinity(carrier_uint u) noexcept { + constexpr int significand_bits = ieee754_format_info::significand_bits; + constexpr int exponent_bits = ieee754_format_info::exponent_bits; + constexpr auto positive_infinity = + carrier_uint((carrier_uint(1) << exponent_bits) - 1) << significand_bits; + return u == positive_infinity; + } + static constexpr bool is_negative_infinity(carrier_uint u) noexcept { + constexpr int significand_bits = ieee754_format_info::significand_bits; + constexpr int exponent_bits = ieee754_format_info::exponent_bits; + constexpr auto negative_infinity = + (carrier_uint((carrier_uint(1) << exponent_bits) - 1) << significand_bits) + | (carrier_uint(1) << (carrier_bits - 1)); + return u == negative_infinity; + } + static constexpr bool is_infinity(carrier_uint u) noexcept { + return is_positive_infinity(u) || is_negative_infinity(u); + } + static constexpr bool is_nan(carrier_uint u) noexcept { + return !is_finite(u) && (extract_significand_bits(u) != 0); + } + }; + + // Speciailze this class template for possible extensions + template + struct ieee754_traits : default_ieee754_traits { + // I don't know if there is a truly reliable way of detecting + // IEEE-754 binary32/binary64 formats; I just did my best here + static_assert(std::numeric_limits::is_iec559 && + std::numeric_limits::radix == 2 && + (detail::physical_bits == 32 || detail::physical_bits == 64), + "default_ieee754_traits only worsk for 32-bits or 64-bits types " + "supporting binary32 or binary64 formats!"); + }; + + // Convenient wrapper for ieee754_traits + // In order to reduce the argument passing overhead, + // this class should be as simple as possible + // (e.g., no inheritance, no private non-static data member, etc.; + // this is an unfortunate fact about x64 calling convention) + template + struct ieee754_bits { + using carrier_uint = typename ieee754_traits::carrier_uint; + carrier_uint u; + + ieee754_bits() = default; + constexpr explicit ieee754_bits(carrier_uint bit_pattern) noexcept : + u{ bit_pattern } {} + constexpr explicit ieee754_bits(T float_value) noexcept : + u{ ieee754_traits::float_to_carrier(float_value) } {} + + constexpr T to_float() const noexcept { + return ieee754_traits::carrier_to_float(u); + } + + constexpr carrier_uint extract_significand_bits() const noexcept { + return ieee754_traits::extract_significand_bits(u); + } + constexpr unsigned int extract_exponent_bits() const noexcept { + return ieee754_traits::extract_exponent_bits(u); + } + + constexpr carrier_uint binary_significand() const noexcept { + using format_info = ieee754_format_info::format>; + auto s = extract_significand_bits(); + if (extract_exponent_bits() == 0) { + return s; + } + else { + return s | (carrier_uint(1) << format_info::significand_bits); + } + } + constexpr int binary_exponent() const noexcept { + using format_info = ieee754_format_info::format>; + auto e = extract_exponent_bits(); + if (e == 0) { + return format_info::min_exponent; + } + else { + return e + format_info::exponent_bias; + } + } + + constexpr bool is_finite() const noexcept { + return ieee754_traits::is_finite(u); + } + constexpr bool is_nonzero() const noexcept { + return ieee754_traits::is_nonzero(u); + } + // Allows positive and negative zeros + constexpr bool is_subnormal() const noexcept { + return ieee754_traits::is_subnormal(u); + } + // Allows positive zero and positive NaN's, but not allow negative zero + constexpr bool is_positive() const noexcept { + return ieee754_traits::is_positive(u); + } + // Allows negative zero and negative NaN's, but not allow positive zero + constexpr bool is_negative() const noexcept { + return ieee754_traits::is_negative(u); + } + constexpr bool is_positive_infinity() const noexcept { + return ieee754_traits::is_positive_infinity(u); + } + + constexpr bool is_negative_infinity() const noexcept { + return ieee754_traits::is_negative_infinity(u); + } + // Allows both plus and minus infinities + constexpr bool is_infinity() const noexcept { + return ieee754_traits::is_infinity(u); + } + constexpr bool is_nan() const noexcept { + return ieee754_traits::is_nan(u); + } + }; + + namespace detail { + //////////////////////////////////////////////////////////////////////////////////////// + // Bit operation intrinsics + //////////////////////////////////////////////////////////////////////////////////////// + + namespace bits { + template + inline int countr_zero(UInt n) noexcept { + static_assert(std::is_unsigned_v && value_bits <= 64); +#if defined(__GNUC__) || defined(__clang__) +#define JKJ_HAS_COUNTR_ZERO_INTRINSIC 1 + if constexpr (std::is_same_v) { + return __builtin_ctzl(n); + } + else if constexpr (std::is_same_v) { + return __builtin_ctzll(n); + } + else { + static_assert(sizeof(UInt) <= sizeof(unsigned int)); + return __builtin_ctz((unsigned int)n); + } +#elif defined(_MSC_VER) +#define JKJ_HAS_COUNTR_ZERO_INTRINSIC 1 + if constexpr (std::is_same_v) { +#if defined(_M_X64) + return int(_tzcnt_u64(n)); +#else + return ((unsigned int)(n) == 0) ? + (32 + (_tzcnt_u32((unsigned int)(n >> 32)))) : + (_tzcnt_u32((unsigned int)n)); +#endif + } + else { + static_assert(sizeof(UInt) <= sizeof(unsigned int)); + return int(_tzcnt_u32((unsigned int)n)); + } +#else +#define JKJ_HAS_COUNTR_ZERO_INTRINSIC 0 + int count; + auto n32 = std::uint32_t(n); + + if constexpr (value_bits > 32) { + if (n32 != 0) { + count = 31; + } + else { + n32 = std::uint32_t(n >> 32); + if constexpr (value_bits == 64) { + if (n32 != 0) { + count = 63; + } + else { + return 64; + } + } + else { + count = value_bits; + } + } + } + else { + if constexpr (value_bits == 32) { + if (n32 != 0) { + count = 31; + } + else { + return 32; + } + } + else { + count = value_bits; + } + } + + n32 &= (0 - n32); + if constexpr (value_bits > 16) { + if ((n32 & 0x0000ffff) != 0) count -= 16; + } + if constexpr (value_bits > 8) { + if ((n32 & 0x00ff00ff) != 0) count -= 8; + } + if ((n32 & 0x0f0f0f0f) != 0) count -= 4; + if ((n32 & 0x33333333) != 0) count -= 2; + if ((n32 & 0x55555555) != 0) count -= 1; + + return count; +#endif + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // Utilities for wide unsigned integer arithmetic + //////////////////////////////////////////////////////////////////////////////////////// + + namespace wuint { + struct uint128 { + uint128() = default; + +#if defined(__SIZEOF_INT128__) + unsigned __int128 internal_; + + constexpr uint128(std::uint64_t high, std::uint64_t low) noexcept : + internal_{ ((unsigned __int128)low) | (((unsigned __int128)high) << 64) } {} + + constexpr uint128(unsigned __int128 u) noexcept : internal_{ u } {} + + constexpr std::uint64_t high() const noexcept { + return std::uint64_t(internal_ >> 64); + } + constexpr std::uint64_t low() const noexcept { + return std::uint64_t(internal_); + } + + uint128& operator+=(std::uint64_t n) & noexcept { + internal_ += n; + return *this; + } +#else + std::uint64_t high_; + std::uint64_t low_; + + constexpr uint128(std::uint64_t high, std::uint64_t low) noexcept : + high_{ high }, low_{ low } {} + + constexpr std::uint64_t high() const noexcept { + return high_; + } + constexpr std::uint64_t low() const noexcept { + return low_; + } + + uint128& operator+=(std::uint64_t n) & noexcept { +#if defined(_MSC_VER) && defined(_M_X64) + auto carry = _addcarry_u64(0, low_, n, &low_); + _addcarry_u64(carry, high_, 0, &high_); + return *this; +#else + auto sum = low_ + n; + high_ += (sum < low_ ? 1 : 0); + low_ = sum; + return *this; +#endif + } +#endif + }; + + static inline std::uint64_t umul64(std::uint32_t x, std::uint32_t y) noexcept { +#if defined(_MSC_VER) && defined(_M_IX86) + return __emulu(x, y); +#else + return x * std::uint64_t(y); +#endif + } + + // Get 128-bit result of multiplication of two 64-bit unsigned integers + JKJ_SAFEBUFFERS inline uint128 umul128(std::uint64_t x, std::uint64_t y) noexcept { +#if defined(__SIZEOF_INT128__) + return (unsigned __int128)(x) * (unsigned __int128)(y); +#elif defined(_MSC_VER) && defined(_M_X64) + uint128 result; + result.low_ = _umul128(x, y, &result.high_); + return result; +#else + auto a = std::uint32_t(x >> 32); + auto b = std::uint32_t(x); + auto c = std::uint32_t(y >> 32); + auto d = std::uint32_t(y); + + auto ac = umul64(a, c); + auto bc = umul64(b, c); + auto ad = umul64(a, d); + auto bd = umul64(b, d); + + auto intermediate = (bd >> 32) + std::uint32_t(ad) + std::uint32_t(bc); + + return{ ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32), + (intermediate << 32) + std::uint32_t(bd) }; +#endif + } + + JKJ_SAFEBUFFERS inline std::uint64_t umul128_upper64(std::uint64_t x, std::uint64_t y) noexcept { +#if defined(__SIZEOF_INT128__) + auto p = (unsigned __int128)(x) * (unsigned __int128)(y); + return std::uint64_t(p >> 64); +#elif defined(_MSC_VER) && defined(_M_X64) + return __umulh(x, y); +#else + auto a = std::uint32_t(x >> 32); + auto b = std::uint32_t(x); + auto c = std::uint32_t(y >> 32); + auto d = std::uint32_t(y); + + auto ac = umul64(a, c); + auto bc = umul64(b, c); + auto ad = umul64(a, d); + auto bd = umul64(b, d); + + auto intermediate = (bd >> 32) + std::uint32_t(ad) + std::uint32_t(bc); + + return ac + (intermediate >> 32) + (ad >> 32) + (bc >> 32); +#endif + } + + // Get upper 64-bits of multiplication of a 64-bit unsigned integer and a 128-bit unsigned integer + JKJ_SAFEBUFFERS inline std::uint64_t umul192_upper64(std::uint64_t x, uint128 y) noexcept { + auto g0 = umul128(x, y.high()); + g0 += umul128_upper64(x, y.low()); + return g0.high(); + } + + // Get upper 32-bits of multiplication of a 32-bit unsigned integer and a 64-bit unsigned integer + inline std::uint32_t umul96_upper32(std::uint32_t x, std::uint64_t y) noexcept { +#if defined(__SIZEOF_INT128__) || (defined(_MSC_VER) && defined(_M_X64)) + return std::uint32_t(umul128_upper64(x, y)); +#else + //std::uint32_t a = 0; + auto b = x; + auto c = std::uint32_t(y >> 32); + auto d = std::uint32_t(y); + + //std::uint64_t ac = 0; + auto bc = umul64(b, c); + //std::uint64_t ad = 0; + auto bd = umul64(b, d); + + auto intermediate = (bd >> 32) + bc; + return std::uint32_t(intermediate >> 32); +#endif + } + + // Get middle 64-bits of multiplication of a 64-bit unsigned integer and a 128-bit unsigned integer + JKJ_SAFEBUFFERS inline std::uint64_t umul192_middle64(std::uint64_t x, uint128 y) noexcept { + auto g01 = x * y.high(); + auto g10 = umul128_upper64(x, y.low()); + return g01 + g10; + } + + // Get middle 32-bits of multiplication of a 32-bit unsigned integer and a 64-bit unsigned integer + inline std::uint64_t umul96_lower64(std::uint32_t x, std::uint64_t y) noexcept { + return x * y; + } + } + + template + constexpr Int compute_power(Int a) noexcept { + static_assert(k >= 0); + Int p = 1; + for (int i = 0; i < k; ++i) { + p *= a; + } + return p; + } + + template + constexpr int count_factors(UInt n) noexcept { + static_assert(a > 1); + int c = 0; + while (n % a == 0) { + n /= a; + ++c; + } + return c; + } + + //////////////////////////////////////////////////////////////////////////////////////// + // Utilities for fast/constexpr log computation + //////////////////////////////////////////////////////////////////////////////////////// + + namespace log { + constexpr std::int32_t floor_shift( + std::uint32_t integer_part, + std::uint64_t fractional_digits, + std::size_t shift_amount) noexcept + { + assert(shift_amount < 32); + // Ensure no overflow + assert(shift_amount == 0 || integer_part < (std::uint32_t(1) << (32 - shift_amount))); + + return shift_amount == 0 ? std::int32_t(integer_part) : + std::int32_t( + (integer_part << shift_amount) | + (fractional_digits >> (64 - shift_amount))); + } + + // Compute floor(e * c - s) + template < + std::uint32_t c_integer_part, + std::uint64_t c_fractional_digits, + std::size_t shift_amount, + std::int32_t max_exponent, + std::uint32_t s_integer_part = 0, + std::uint64_t s_fractional_digits = 0 + > + constexpr int compute(int e) noexcept { + assert(e <= max_exponent && e >= -max_exponent); + constexpr auto c = floor_shift(c_integer_part, c_fractional_digits, shift_amount); + constexpr auto s = floor_shift(s_integer_part, s_fractional_digits, shift_amount); + return int((std::int32_t(e) * c - s) >> shift_amount); + } + + inline constexpr std::uint64_t log10_2_fractional_digits{ 0x4d10'4d42'7de7'fbcc }; + inline constexpr std::uint64_t log10_4_over_3_fractional_digits{ 0x1ffb'fc2b'bc78'0375 }; + inline constexpr std::size_t floor_log10_pow2_shift_amount = 22; + inline constexpr int floor_log10_pow2_input_limit = 1700; + inline constexpr int floor_log10_pow2_minus_log10_4_over_3_input_limit = 1700; + + inline constexpr std::uint64_t log2_10_fractional_digits{ 0x5269'e12f'346e'2bf9 }; + inline constexpr std::size_t floor_log2_pow10_shift_amount = 19; + inline constexpr int floor_log2_pow10_input_limit = 1233; + + inline constexpr std::uint64_t log5_2_fractional_digits{ 0x6e40'd1a4'143d'cb94 }; + inline constexpr std::uint64_t log5_3_fractional_digits{ 0xaebf'4791'5d44'3b24 }; + inline constexpr std::size_t floor_log5_pow2_shift_amount = 20; + inline constexpr int floor_log5_pow2_input_limit = 1492; + inline constexpr int floor_log5_pow2_minus_log5_3_input_limit = 2427; + + // For constexpr computation + // Returns -1 when n = 0 + template + constexpr int floor_log2(UInt n) noexcept { + int count = -1; + while (n != 0) { + ++count; + n >>= 1; + } + return count; + } + + constexpr int floor_log10_pow2(int e) noexcept { + using namespace log; + return compute< + 0, log10_2_fractional_digits, + floor_log10_pow2_shift_amount, + floor_log10_pow2_input_limit>(e); + } + + constexpr int floor_log2_pow10(int e) noexcept { + using namespace log; + return compute< + 3, log2_10_fractional_digits, + floor_log2_pow10_shift_amount, + floor_log2_pow10_input_limit>(e); + } + + constexpr int floor_log5_pow2(int e) noexcept { + using namespace log; + return compute< + 0, log5_2_fractional_digits, + floor_log5_pow2_shift_amount, + floor_log5_pow2_input_limit>(e); + } + + constexpr int floor_log5_pow2_minus_log5_3(int e) noexcept { + using namespace log; + return compute< + 0, log5_2_fractional_digits, + floor_log5_pow2_shift_amount, + floor_log5_pow2_minus_log5_3_input_limit, + 0, log5_3_fractional_digits>(e); + } + + constexpr int floor_log10_pow2_minus_log10_4_over_3(int e) noexcept { + using namespace log; + return compute< + 0, log10_2_fractional_digits, + floor_log10_pow2_shift_amount, + floor_log10_pow2_minus_log10_4_over_3_input_limit, + 0, log10_4_over_3_fractional_digits>(e); + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // Utilities for fast divisibility test + //////////////////////////////////////////////////////////////////////////////////////// + + namespace div { + template + constexpr UInt modular_inverse(unsigned int bit_width = unsigned(value_bits)) noexcept { + // By Euler's theorem, a^phi(2^n) == 1 (mod 2^n), + // where phi(2^n) = 2^(n-1), so the modular inverse of a is + // a^(2^(n-1) - 1) = a^(1 + 2 + 2^2 + ... + 2^(n-2)) + std::common_type_t mod_inverse = 1; + for (unsigned int i = 1; i < bit_width; ++i) { + mod_inverse = mod_inverse * mod_inverse * a; + } + if (bit_width < value_bits) { + auto mask = UInt((UInt(1) << bit_width) - 1); + return UInt(mod_inverse & mask); + } + else { + return UInt(mod_inverse); + } + } + + template + struct table_t { + static_assert(std::is_unsigned_v); + static_assert(a % 2 != 0); + static_assert(N > 0); + + static constexpr std::size_t size = N; + UInt mod_inv[N]; + UInt max_quotients[N]; + }; + + template + struct table_holder { + static constexpr table_t table = [] { + constexpr auto mod_inverse = modular_inverse(); + table_t table{}; + std::common_type_t pow_of_mod_inverse = 1; + UInt pow_of_a = 1; + for (std::size_t i = 0; i < N; ++i) { + table.mod_inv[i] = UInt(pow_of_mod_inverse); + table.max_quotients[i] = UInt(std::numeric_limits::max() / pow_of_a); + + pow_of_mod_inverse *= mod_inverse; + pow_of_a *= a; + } + + return table; + }(); + }; + + template + constexpr bool divisible_by_power_of_5(UInt x, unsigned int exp) noexcept { + auto const& table = table_holder::table; + assert(exp < table.size); + return (x * table.mod_inv[exp]) <= table.max_quotients[exp]; + } + + template + constexpr bool divisible_by_power_of_2(UInt x, unsigned int exp) noexcept { + assert(exp >= 1); + assert(x != 0); +#if JKJ_HAS_COUNTR_ZERO_INTRINSIC + return bits::countr_zero(x) >= int(exp); +#else + if (exp >= value_bits) { + return false; + } + auto mask = UInt((UInt(1) << exp) - 1); + return (x & mask) == 0; +#endif + } + + // Replace n by floor(n / 5^N) + // Returns true if and only if n is divisible by 5^N + // Precondition: n <= 2 * 5^(N+1) + template + struct check_divisibility_and_divide_by_pow5_info; + + template <> + struct check_divisibility_and_divide_by_pow5_info<1> { + static constexpr std::uint32_t magic_number = 0xcccd; + static constexpr int bits_for_comparison = 16; + static constexpr std::uint32_t threshold = 0x3333; + static constexpr int shift_amount = 18; + }; + + template <> + struct check_divisibility_and_divide_by_pow5_info<2> { + static constexpr std::uint32_t magic_number = 0xa429; + static constexpr int bits_for_comparison = 8; + static constexpr std::uint32_t threshold = 0x0a; + static constexpr int shift_amount = 20; + }; + + template + constexpr bool check_divisibility_and_divide_by_pow5(std::uint32_t& n) noexcept + { + // Make sure the computation for max_n does not overflow + static_assert(N + 1 <= log::floor_log5_pow2(31)); + assert(n <= compute_power(std::uint32_t(5)) * 2); + + using info = check_divisibility_and_divide_by_pow5_info; + n *= info::magic_number; + constexpr std::uint32_t comparison_mask = + info::bits_for_comparison >= 32 ? std::numeric_limits::max() : + std::uint32_t((std::uint32_t(1) << info::bits_for_comparison) - 1); + + if ((n & comparison_mask) <= info::threshold) { + n >>= info::shift_amount; + return true; + } + else { + n >>= info::shift_amount; + return false; + } + } + + // Compute floor(n / 10^N) for small n and N + // Precondition: n <= 10^(N+1) + template + struct small_division_by_pow10_info; + + template <> + struct small_division_by_pow10_info<1> { + static constexpr std::uint32_t magic_number = 0xcccd; + static constexpr int shift_amount = 19; + }; + + template <> + struct small_division_by_pow10_info<2> { + static constexpr std::uint32_t magic_number = 0xa3d8; + static constexpr int shift_amount = 22; + }; + + template + constexpr std::uint32_t small_division_by_pow10(std::uint32_t n) noexcept + { + assert(n <= compute_power(std::uint32_t(10))); + return (n * small_division_by_pow10_info::magic_number) + >> small_division_by_pow10_info::shift_amount; + } + + // Compute floor(n / 10^N) for small N + // Precondition: n <= 2^a * 5^b (a = max_pow2, b = max_pow5) + template + constexpr UInt divide_by_pow10(UInt n) noexcept + { + static_assert(N >= 0); + + // Ensure no overflow + static_assert(max_pow2 + (log::floor_log2_pow10(max_pow5) - max_pow5) < value_bits); + + // Specialize for 64bit division by 1000 + // Ensure that the correctness condition is met + if constexpr (std::is_same_v && N == 3 && + max_pow2 + (log::floor_log2_pow10(N + max_pow5) - (N + max_pow5)) < 70) + { + return wuint::umul128_upper64(n, 0x8312'6e97'8d4f'df3c) >> 9; + } + else { + constexpr auto divisor = compute_power(UInt(10)); + return n / divisor; + } + } + } + } + + //////////////////////////////////////////////////////////////////////////////////////// + // DIY floating-point data type + //////////////////////////////////////////////////////////////////////////////////////// + + template + struct fp_t; + + template + struct fp_t { + using float_type = Float; + using carrier_uint = typename ieee754_traits::carrier_uint; + + carrier_uint significand; + int exponent; + }; + + template + struct fp_t { + using float_type = Float; + using carrier_uint = typename ieee754_traits::carrier_uint; + + carrier_uint significand; + int exponent; + bool is_negative; + }; + + template + struct fp_t { + using float_type = Float; + using carrier_uint = typename ieee754_traits::carrier_uint; + + carrier_uint significand; + int exponent; + bool may_have_trailing_zeros; + }; + + template + struct fp_t { + using float_type = Float; + using carrier_uint = typename ieee754_traits::carrier_uint; + + carrier_uint significand; + int exponent; + bool is_negative; + bool may_have_trailing_zeros; + }; + + template + using unsigned_fp_t = fp_t; + + template + using signed_fp_t = fp_t; + + + //////////////////////////////////////////////////////////////////////////////////////// + // Computed cache entries + //////////////////////////////////////////////////////////////////////////////////////// + + namespace detail { + template + struct cache_holder; + + template <> + struct cache_holder { + using cache_entry_type = std::uint64_t; + static constexpr int cache_bits = 64; + static constexpr int min_k = -31; + static constexpr int max_k = 46; + static constexpr cache_entry_type cache[] = { + 0x81ceb32c4b43fcf5, + 0xa2425ff75e14fc32, + 0xcad2f7f5359a3b3f, + 0xfd87b5f28300ca0e, + 0x9e74d1b791e07e49, + 0xc612062576589ddb, + 0xf79687aed3eec552, + 0x9abe14cd44753b53, + 0xc16d9a0095928a28, + 0xf1c90080baf72cb2, + 0x971da05074da7bef, + 0xbce5086492111aeb, + 0xec1e4a7db69561a6, + 0x9392ee8e921d5d08, + 0xb877aa3236a4b44a, + 0xe69594bec44de15c, + 0x901d7cf73ab0acda, + 0xb424dc35095cd810, + 0xe12e13424bb40e14, + 0x8cbccc096f5088cc, + 0xafebff0bcb24aaff, + 0xdbe6fecebdedd5bf, + 0x89705f4136b4a598, + 0xabcc77118461cefd, + 0xd6bf94d5e57a42bd, + 0x8637bd05af6c69b6, + 0xa7c5ac471b478424, + 0xd1b71758e219652c, + 0x83126e978d4fdf3c, + 0xa3d70a3d70a3d70b, + 0xcccccccccccccccd, + 0x8000000000000000, + 0xa000000000000000, + 0xc800000000000000, + 0xfa00000000000000, + 0x9c40000000000000, + 0xc350000000000000, + 0xf424000000000000, + 0x9896800000000000, + 0xbebc200000000000, + 0xee6b280000000000, + 0x9502f90000000000, + 0xba43b74000000000, + 0xe8d4a51000000000, + 0x9184e72a00000000, + 0xb5e620f480000000, + 0xe35fa931a0000000, + 0x8e1bc9bf04000000, + 0xb1a2bc2ec5000000, + 0xde0b6b3a76400000, + 0x8ac7230489e80000, + 0xad78ebc5ac620000, + 0xd8d726b7177a8000, + 0x878678326eac9000, + 0xa968163f0a57b400, + 0xd3c21bcecceda100, + 0x84595161401484a0, + 0xa56fa5b99019a5c8, + 0xcecb8f27f4200f3a, + 0x813f3978f8940984, + 0xa18f07d736b90be5, + 0xc9f2c9cd04674ede, + 0xfc6f7c4045812296, + 0x9dc5ada82b70b59d, + 0xc5371912364ce305, + 0xf684df56c3e01bc6, + 0x9a130b963a6c115c, + 0xc097ce7bc90715b3, + 0xf0bdc21abb48db20, + 0x96769950b50d88f4, + 0xbc143fa4e250eb31, + 0xeb194f8e1ae525fd, + 0x92efd1b8d0cf37be, + 0xb7abc627050305ad, + 0xe596b7b0c643c719, + 0x8f7e32ce7bea5c6f, + 0xb35dbf821ae4f38b, + 0xe0352f62a19e306e + }; + }; + + template <> + struct cache_holder { + using cache_entry_type = wuint::uint128; + static constexpr int cache_bits = 128; + static constexpr int min_k = -292; + static constexpr int max_k = 326; + static constexpr cache_entry_type cache[] = { + { 0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7b }, + { 0x9faacf3df73609b1, 0x77b191618c54e9ad }, + { 0xc795830d75038c1d, 0xd59df5b9ef6a2418 }, + { 0xf97ae3d0d2446f25, 0x4b0573286b44ad1e }, + { 0x9becce62836ac577, 0x4ee367f9430aec33 }, + { 0xc2e801fb244576d5, 0x229c41f793cda740 }, + { 0xf3a20279ed56d48a, 0x6b43527578c11110 }, + { 0x9845418c345644d6, 0x830a13896b78aaaa }, + { 0xbe5691ef416bd60c, 0x23cc986bc656d554 }, + { 0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa9 }, + { 0x94b3a202eb1c3f39, 0x7bf7d71432f3d6aa }, + { 0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc54 }, + { 0xe858ad248f5c22c9, 0xd1b3400f8f9cff69 }, + { 0x91376c36d99995be, 0x23100809b9c21fa2 }, + { 0xb58547448ffffb2d, 0xabd40a0c2832a78b }, + { 0xe2e69915b3fff9f9, 0x16c90c8f323f516d }, + { 0x8dd01fad907ffc3b, 0xae3da7d97f6792e4 }, + { 0xb1442798f49ffb4a, 0x99cd11cfdf41779d }, + { 0xdd95317f31c7fa1d, 0x40405643d711d584 }, + { 0x8a7d3eef7f1cfc52, 0x482835ea666b2573 }, + { 0xad1c8eab5ee43b66, 0xda3243650005eed0 }, + { 0xd863b256369d4a40, 0x90bed43e40076a83 }, + { 0x873e4f75e2224e68, 0x5a7744a6e804a292 }, + { 0xa90de3535aaae202, 0x711515d0a205cb37 }, + { 0xd3515c2831559a83, 0x0d5a5b44ca873e04 }, + { 0x8412d9991ed58091, 0xe858790afe9486c3 }, + { 0xa5178fff668ae0b6, 0x626e974dbe39a873 }, + { 0xce5d73ff402d98e3, 0xfb0a3d212dc81290 }, + { 0x80fa687f881c7f8e, 0x7ce66634bc9d0b9a }, + { 0xa139029f6a239f72, 0x1c1fffc1ebc44e81 }, + { 0xc987434744ac874e, 0xa327ffb266b56221 }, + { 0xfbe9141915d7a922, 0x4bf1ff9f0062baa9 }, + { 0x9d71ac8fada6c9b5, 0x6f773fc3603db4aa }, + { 0xc4ce17b399107c22, 0xcb550fb4384d21d4 }, + { 0xf6019da07f549b2b, 0x7e2a53a146606a49 }, + { 0x99c102844f94e0fb, 0x2eda7444cbfc426e }, + { 0xc0314325637a1939, 0xfa911155fefb5309 }, + { 0xf03d93eebc589f88, 0x793555ab7eba27cb }, + { 0x96267c7535b763b5, 0x4bc1558b2f3458df }, + { 0xbbb01b9283253ca2, 0x9eb1aaedfb016f17 }, + { 0xea9c227723ee8bcb, 0x465e15a979c1cadd }, + { 0x92a1958a7675175f, 0x0bfacd89ec191eca }, + { 0xb749faed14125d36, 0xcef980ec671f667c }, + { 0xe51c79a85916f484, 0x82b7e12780e7401b }, + { 0x8f31cc0937ae58d2, 0xd1b2ecb8b0908811 }, + { 0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa16 }, + { 0xdfbdcece67006ac9, 0x67a791e093e1d49b }, + { 0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e1 }, + { 0xaecc49914078536d, 0x58fae9f773886e19 }, + { 0xda7f5bf590966848, 0xaf39a475506a899f }, + { 0x888f99797a5e012d, 0x6d8406c952429604 }, + { 0xaab37fd7d8f58178, 0xc8e5087ba6d33b84 }, + { 0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a65 }, + { 0x855c3be0a17fcd26, 0x5cf2eea09a550680 }, + { 0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481f }, + { 0xd0601d8efc57b08b, 0xf13b94daf124da27 }, + { 0x823c12795db6ce57, 0x76c53d08d6b70859 }, + { 0xa2cb1717b52481ed, 0x54768c4b0c64ca6f }, + { 0xcb7ddcdda26da268, 0xa9942f5dcf7dfd0a }, + { 0xfe5d54150b090b02, 0xd3f93b35435d7c4d }, + { 0x9efa548d26e5a6e1, 0xc47bc5014a1a6db0 }, + { 0xc6b8e9b0709f109a, 0x359ab6419ca1091c }, + { 0xf867241c8cc6d4c0, 0xc30163d203c94b63 }, + { 0x9b407691d7fc44f8, 0x79e0de63425dcf1e }, + { 0xc21094364dfb5636, 0x985915fc12f542e5 }, + { 0xf294b943e17a2bc4, 0x3e6f5b7b17b2939e }, + { 0x979cf3ca6cec5b5a, 0xa705992ceecf9c43 }, + { 0xbd8430bd08277231, 0x50c6ff782a838354 }, + { 0xece53cec4a314ebd, 0xa4f8bf5635246429 }, + { 0x940f4613ae5ed136, 0x871b7795e136be9a }, + { 0xb913179899f68584, 0x28e2557b59846e40 }, + { 0xe757dd7ec07426e5, 0x331aeada2fe589d0 }, + { 0x9096ea6f3848984f, 0x3ff0d2c85def7622 }, + { 0xb4bca50b065abe63, 0x0fed077a756b53aa }, + { 0xe1ebce4dc7f16dfb, 0xd3e8495912c62895 }, + { 0x8d3360f09cf6e4bd, 0x64712dd7abbbd95d }, + { 0xb080392cc4349dec, 0xbd8d794d96aacfb4 }, + { 0xdca04777f541c567, 0xecf0d7a0fc5583a1 }, + { 0x89e42caaf9491b60, 0xf41686c49db57245 }, + { 0xac5d37d5b79b6239, 0x311c2875c522ced6 }, + { 0xd77485cb25823ac7, 0x7d633293366b828c }, + { 0x86a8d39ef77164bc, 0xae5dff9c02033198 }, + { 0xa8530886b54dbdeb, 0xd9f57f830283fdfd }, + { 0xd267caa862a12d66, 0xd072df63c324fd7c }, + { 0x8380dea93da4bc60, 0x4247cb9e59f71e6e }, + { 0xa46116538d0deb78, 0x52d9be85f074e609 }, + { 0xcd795be870516656, 0x67902e276c921f8c }, + { 0x806bd9714632dff6, 0x00ba1cd8a3db53b7 }, + { 0xa086cfcd97bf97f3, 0x80e8a40eccd228a5 }, + { 0xc8a883c0fdaf7df0, 0x6122cd128006b2ce }, + { 0xfad2a4b13d1b5d6c, 0x796b805720085f82 }, + { 0x9cc3a6eec6311a63, 0xcbe3303674053bb1 }, + { 0xc3f490aa77bd60fc, 0xbedbfc4411068a9d }, + { 0xf4f1b4d515acb93b, 0xee92fb5515482d45 }, + { 0x991711052d8bf3c5, 0x751bdd152d4d1c4b }, + { 0xbf5cd54678eef0b6, 0xd262d45a78a0635e }, + { 0xef340a98172aace4, 0x86fb897116c87c35 }, + { 0x9580869f0e7aac0e, 0xd45d35e6ae3d4da1 }, + { 0xbae0a846d2195712, 0x8974836059cca10a }, + { 0xe998d258869facd7, 0x2bd1a438703fc94c }, + { 0x91ff83775423cc06, 0x7b6306a34627ddd0 }, + { 0xb67f6455292cbf08, 0x1a3bc84c17b1d543 }, + { 0xe41f3d6a7377eeca, 0x20caba5f1d9e4a94 }, + { 0x8e938662882af53e, 0x547eb47b7282ee9d }, + { 0xb23867fb2a35b28d, 0xe99e619a4f23aa44 }, + { 0xdec681f9f4c31f31, 0x6405fa00e2ec94d5 }, + { 0x8b3c113c38f9f37e, 0xde83bc408dd3dd05 }, + { 0xae0b158b4738705e, 0x9624ab50b148d446 }, + { 0xd98ddaee19068c76, 0x3badd624dd9b0958 }, + { 0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d7 }, + { 0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4d }, + { 0xd47487cc8470652b, 0x7647c32000696720 }, + { 0x84c8d4dfd2c63f3b, 0x29ecd9f40041e074 }, + { 0xa5fb0a17c777cf09, 0xf468107100525891 }, + { 0xcf79cc9db955c2cc, 0x7182148d4066eeb5 }, + { 0x81ac1fe293d599bf, 0xc6f14cd848405531 }, + { 0xa21727db38cb002f, 0xb8ada00e5a506a7d }, + { 0xca9cf1d206fdc03b, 0xa6d90811f0e4851d }, + { 0xfd442e4688bd304a, 0x908f4a166d1da664 }, + { 0x9e4a9cec15763e2e, 0x9a598e4e043287ff }, + { 0xc5dd44271ad3cdba, 0x40eff1e1853f29fe }, + { 0xf7549530e188c128, 0xd12bee59e68ef47d }, + { 0x9a94dd3e8cf578b9, 0x82bb74f8301958cf }, + { 0xc13a148e3032d6e7, 0xe36a52363c1faf02 }, + { 0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac2 }, + { 0x96f5600f15a7b7e5, 0x29ab103a5ef8c0ba }, + { 0xbcb2b812db11a5de, 0x7415d448f6b6f0e8 }, + { 0xebdf661791d60f56, 0x111b495b3464ad22 }, + { 0x936b9fcebb25c995, 0xcab10dd900beec35 }, + { 0xb84687c269ef3bfb, 0x3d5d514f40eea743 }, + { 0xe65829b3046b0afa, 0x0cb4a5a3112a5113 }, + { 0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ac }, + { 0xb3f4e093db73a093, 0x59ed216765690f57 }, + { 0xe0f218b8d25088b8, 0x306869c13ec3532d }, + { 0x8c974f7383725573, 0x1e414218c73a13fc }, + { 0xafbd2350644eeacf, 0xe5d1929ef90898fb }, + { 0xdbac6c247d62a583, 0xdf45f746b74abf3a }, + { 0x894bc396ce5da772, 0x6b8bba8c328eb784 }, + { 0xab9eb47c81f5114f, 0x066ea92f3f326565 }, + { 0xd686619ba27255a2, 0xc80a537b0efefebe }, + { 0x8613fd0145877585, 0xbd06742ce95f5f37 }, + { 0xa798fc4196e952e7, 0x2c48113823b73705 }, + { 0xd17f3b51fca3a7a0, 0xf75a15862ca504c6 }, + { 0x82ef85133de648c4, 0x9a984d73dbe722fc }, + { 0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebbb }, + { 0xcc963fee10b7d1b3, 0x318df905079926a9 }, + { 0xffbbcfe994e5c61f, 0xfdf17746497f7053 }, + { 0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa634 }, + { 0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc1 }, + { 0xf9bd690a1b68637b, 0x3dfdce7aa3c673b1 }, + { 0x9c1661a651213e2d, 0x06bea10ca65c084f }, + { 0xc31bfa0fe5698db8, 0x486e494fcff30a63 }, + { 0xf3e2f893dec3f126, 0x5a89dba3c3efccfb }, + { 0x986ddb5c6b3a76b7, 0xf89629465a75e01d }, + { 0xbe89523386091465, 0xf6bbb397f1135824 }, + { 0xee2ba6c0678b597f, 0x746aa07ded582e2d }, + { 0x94db483840b717ef, 0xa8c2a44eb4571cdd }, + { 0xba121a4650e4ddeb, 0x92f34d62616ce414 }, + { 0xe896a0d7e51e1566, 0x77b020baf9c81d18 }, + { 0x915e2486ef32cd60, 0x0ace1474dc1d122f }, + { 0xb5b5ada8aaff80b8, 0x0d819992132456bb }, + { 0xe3231912d5bf60e6, 0x10e1fff697ed6c6a }, + { 0x8df5efabc5979c8f, 0xca8d3ffa1ef463c2 }, + { 0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb3 }, + { 0xddd0467c64bce4a0, 0xac7cb3f6d05ddbdf }, + { 0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96c }, + { 0xad4ab7112eb3929d, 0x86c16c98d2c953c7 }, + { 0xd89d64d57a607744, 0xe871c7bf077ba8b8 }, + { 0x87625f056c7c4a8b, 0x11471cd764ad4973 }, + { 0xa93af6c6c79b5d2d, 0xd598e40d3dd89bd0 }, + { 0xd389b47879823479, 0x4aff1d108d4ec2c4 }, + { 0x843610cb4bf160cb, 0xcedf722a585139bb }, + { 0xa54394fe1eedb8fe, 0xc2974eb4ee658829 }, + { 0xce947a3da6a9273e, 0x733d226229feea33 }, + { 0x811ccc668829b887, 0x0806357d5a3f5260 }, + { 0xa163ff802a3426a8, 0xca07c2dcb0cf26f8 }, + { 0xc9bcff6034c13052, 0xfc89b393dd02f0b6 }, + { 0xfc2c3f3841f17c67, 0xbbac2078d443ace3 }, + { 0x9d9ba7832936edc0, 0xd54b944b84aa4c0e }, + { 0xc5029163f384a931, 0x0a9e795e65d4df12 }, + { 0xf64335bcf065d37d, 0x4d4617b5ff4a16d6 }, + { 0x99ea0196163fa42e, 0x504bced1bf8e4e46 }, + { 0xc06481fb9bcf8d39, 0xe45ec2862f71e1d7 }, + { 0xf07da27a82c37088, 0x5d767327bb4e5a4d }, + { 0x964e858c91ba2655, 0x3a6a07f8d510f870 }, + { 0xbbe226efb628afea, 0x890489f70a55368c }, + { 0xeadab0aba3b2dbe5, 0x2b45ac74ccea842f }, + { 0x92c8ae6b464fc96f, 0x3b0b8bc90012929e }, + { 0xb77ada0617e3bbcb, 0x09ce6ebb40173745 }, + { 0xe55990879ddcaabd, 0xcc420a6a101d0516 }, + { 0x8f57fa54c2a9eab6, 0x9fa946824a12232e }, + { 0xb32df8e9f3546564, 0x47939822dc96abfa }, + { 0xdff9772470297ebd, 0x59787e2b93bc56f8 }, + { 0x8bfbea76c619ef36, 0x57eb4edb3c55b65b }, + { 0xaefae51477a06b03, 0xede622920b6b23f2 }, + { 0xdab99e59958885c4, 0xe95fab368e45ecee }, + { 0x88b402f7fd75539b, 0x11dbcb0218ebb415 }, + { 0xaae103b5fcd2a881, 0xd652bdc29f26a11a }, + { 0xd59944a37c0752a2, 0x4be76d3346f04960 }, + { 0x857fcae62d8493a5, 0x6f70a4400c562ddc }, + { 0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb953 }, + { 0xd097ad07a71f26b2, 0x7e2000a41346a7a8 }, + { 0x825ecc24c873782f, 0x8ed400668c0c28c9 }, + { 0xa2f67f2dfa90563b, 0x728900802f0f32fb }, + { 0xcbb41ef979346bca, 0x4f2b40a03ad2ffba }, + { 0xfea126b7d78186bc, 0xe2f610c84987bfa9 }, + { 0x9f24b832e6b0f436, 0x0dd9ca7d2df4d7ca }, + { 0xc6ede63fa05d3143, 0x91503d1c79720dbc }, + { 0xf8a95fcf88747d94, 0x75a44c6397ce912b }, + { 0x9b69dbe1b548ce7c, 0xc986afbe3ee11abb }, + { 0xc24452da229b021b, 0xfbe85badce996169 }, + { 0xf2d56790ab41c2a2, 0xfae27299423fb9c4 }, + { 0x97c560ba6b0919a5, 0xdccd879fc967d41b }, + { 0xbdb6b8e905cb600f, 0x5400e987bbc1c921 }, + { 0xed246723473e3813, 0x290123e9aab23b69 }, + { 0x9436c0760c86e30b, 0xf9a0b6720aaf6522 }, + { 0xb94470938fa89bce, 0xf808e40e8d5b3e6a }, + { 0xe7958cb87392c2c2, 0xb60b1d1230b20e05 }, + { 0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c3 }, + { 0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af4 }, + { 0xe2280b6c20dd5232, 0x25c6da63c38de1b1 }, + { 0x8d590723948a535f, 0x579c487e5a38ad0f }, + { 0xb0af48ec79ace837, 0x2d835a9df0c6d852 }, + { 0xdcdb1b2798182244, 0xf8e431456cf88e66 }, + { 0x8a08f0f8bf0f156b, 0x1b8e9ecb641b5900 }, + { 0xac8b2d36eed2dac5, 0xe272467e3d222f40 }, + { 0xd7adf884aa879177, 0x5b0ed81dcc6abb10 }, + { 0x86ccbb52ea94baea, 0x98e947129fc2b4ea }, + { 0xa87fea27a539e9a5, 0x3f2398d747b36225 }, + { 0xd29fe4b18e88640e, 0x8eec7f0d19a03aae }, + { 0x83a3eeeef9153e89, 0x1953cf68300424ad }, + { 0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd8 }, + { 0xcdb02555653131b6, 0x3792f412cb06794e }, + { 0x808e17555f3ebf11, 0xe2bbd88bbee40bd1 }, + { 0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec5 }, + { 0xc8de047564d20a8b, 0xf245825a5a445276 }, + { 0xfb158592be068d2e, 0xeed6e2f0f0d56713 }, + { 0x9ced737bb6c4183d, 0x55464dd69685606c }, + { 0xc428d05aa4751e4c, 0xaa97e14c3c26b887 }, + { 0xf53304714d9265df, 0xd53dd99f4b3066a9 }, + { 0x993fe2c6d07b7fab, 0xe546a8038efe402a }, + { 0xbf8fdb78849a5f96, 0xde98520472bdd034 }, + { 0xef73d256a5c0f77c, 0x963e66858f6d4441 }, + { 0x95a8637627989aad, 0xdde7001379a44aa9 }, + { 0xbb127c53b17ec159, 0x5560c018580d5d53 }, + { 0xe9d71b689dde71af, 0xaab8f01e6e10b4a7 }, + { 0x9226712162ab070d, 0xcab3961304ca70e9 }, + { 0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d23 }, + { 0xe45c10c42a2b3b05, 0x8cb89a7db77c506b }, + { 0x8eb98a7a9a5b04e3, 0x77f3608e92adb243 }, + { 0xb267ed1940f1c61c, 0x55f038b237591ed4 }, + { 0xdf01e85f912e37a3, 0x6b6c46dec52f6689 }, + { 0x8b61313bbabce2c6, 0x2323ac4b3b3da016 }, + { 0xae397d8aa96c1b77, 0xabec975e0a0d081b }, + { 0xd9c7dced53c72255, 0x96e7bd358c904a22 }, + { 0x881cea14545c7575, 0x7e50d64177da2e55 }, + { 0xaa242499697392d2, 0xdde50bd1d5d0b9ea }, + { 0xd4ad2dbfc3d07787, 0x955e4ec64b44e865 }, + { 0x84ec3c97da624ab4, 0xbd5af13bef0b113f }, + { 0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58f }, + { 0xcfb11ead453994ba, 0x67de18eda5814af3 }, + { 0x81ceb32c4b43fcf4, 0x80eacf948770ced8 }, + { 0xa2425ff75e14fc31, 0xa1258379a94d028e }, + { 0xcad2f7f5359a3b3e, 0x096ee45813a04331 }, + { 0xfd87b5f28300ca0d, 0x8bca9d6e188853fd }, + { 0x9e74d1b791e07e48, 0x775ea264cf55347e }, + { 0xc612062576589dda, 0x95364afe032a819e }, + { 0xf79687aed3eec551, 0x3a83ddbd83f52205 }, + { 0x9abe14cd44753b52, 0xc4926a9672793543 }, + { 0xc16d9a0095928a27, 0x75b7053c0f178294 }, + { 0xf1c90080baf72cb1, 0x5324c68b12dd6339 }, + { 0x971da05074da7bee, 0xd3f6fc16ebca5e04 }, + { 0xbce5086492111aea, 0x88f4bb1ca6bcf585 }, + { 0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6 }, + { 0x9392ee8e921d5d07, 0x3aff322e62439fd0 }, + { 0xb877aa3236a4b449, 0x09befeb9fad487c3 }, + { 0xe69594bec44de15b, 0x4c2ebe687989a9b4 }, + { 0x901d7cf73ab0acd9, 0x0f9d37014bf60a11 }, + { 0xb424dc35095cd80f, 0x538484c19ef38c95 }, + { 0xe12e13424bb40e13, 0x2865a5f206b06fba }, + { 0x8cbccc096f5088cb, 0xf93f87b7442e45d4 }, + { 0xafebff0bcb24aafe, 0xf78f69a51539d749 }, + { 0xdbe6fecebdedd5be, 0xb573440e5a884d1c }, + { 0x89705f4136b4a597, 0x31680a88f8953031 }, + { 0xabcc77118461cefc, 0xfdc20d2b36ba7c3e }, + { 0xd6bf94d5e57a42bc, 0x3d32907604691b4d }, + { 0x8637bd05af6c69b5, 0xa63f9a49c2c1b110 }, + { 0xa7c5ac471b478423, 0x0fcf80dc33721d54 }, + { 0xd1b71758e219652b, 0xd3c36113404ea4a9 }, + { 0x83126e978d4fdf3b, 0x645a1cac083126ea }, + { 0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4 }, + { 0xcccccccccccccccc, 0xcccccccccccccccd }, + { 0x8000000000000000, 0x0000000000000000 }, + { 0xa000000000000000, 0x0000000000000000 }, + { 0xc800000000000000, 0x0000000000000000 }, + { 0xfa00000000000000, 0x0000000000000000 }, + { 0x9c40000000000000, 0x0000000000000000 }, + { 0xc350000000000000, 0x0000000000000000 }, + { 0xf424000000000000, 0x0000000000000000 }, + { 0x9896800000000000, 0x0000000000000000 }, + { 0xbebc200000000000, 0x0000000000000000 }, + { 0xee6b280000000000, 0x0000000000000000 }, + { 0x9502f90000000000, 0x0000000000000000 }, + { 0xba43b74000000000, 0x0000000000000000 }, + { 0xe8d4a51000000000, 0x0000000000000000 }, + { 0x9184e72a00000000, 0x0000000000000000 }, + { 0xb5e620f480000000, 0x0000000000000000 }, + { 0xe35fa931a0000000, 0x0000000000000000 }, + { 0x8e1bc9bf04000000, 0x0000000000000000 }, + { 0xb1a2bc2ec5000000, 0x0000000000000000 }, + { 0xde0b6b3a76400000, 0x0000000000000000 }, + { 0x8ac7230489e80000, 0x0000000000000000 }, + { 0xad78ebc5ac620000, 0x0000000000000000 }, + { 0xd8d726b7177a8000, 0x0000000000000000 }, + { 0x878678326eac9000, 0x0000000000000000 }, + { 0xa968163f0a57b400, 0x0000000000000000 }, + { 0xd3c21bcecceda100, 0x0000000000000000 }, + { 0x84595161401484a0, 0x0000000000000000 }, + { 0xa56fa5b99019a5c8, 0x0000000000000000 }, + { 0xcecb8f27f4200f3a, 0x0000000000000000 }, + { 0x813f3978f8940984, 0x4000000000000000 }, + { 0xa18f07d736b90be5, 0x5000000000000000 }, + { 0xc9f2c9cd04674ede, 0xa400000000000000 }, + { 0xfc6f7c4045812296, 0x4d00000000000000 }, + { 0x9dc5ada82b70b59d, 0xf020000000000000 }, + { 0xc5371912364ce305, 0x6c28000000000000 }, + { 0xf684df56c3e01bc6, 0xc732000000000000 }, + { 0x9a130b963a6c115c, 0x3c7f400000000000 }, + { 0xc097ce7bc90715b3, 0x4b9f100000000000 }, + { 0xf0bdc21abb48db20, 0x1e86d40000000000 }, + { 0x96769950b50d88f4, 0x1314448000000000 }, + { 0xbc143fa4e250eb31, 0x17d955a000000000 }, + { 0xeb194f8e1ae525fd, 0x5dcfab0800000000 }, + { 0x92efd1b8d0cf37be, 0x5aa1cae500000000 }, + { 0xb7abc627050305ad, 0xf14a3d9e40000000 }, + { 0xe596b7b0c643c719, 0x6d9ccd05d0000000 }, + { 0x8f7e32ce7bea5c6f, 0xe4820023a2000000 }, + { 0xb35dbf821ae4f38b, 0xdda2802c8a800000 }, + { 0xe0352f62a19e306e, 0xd50b2037ad200000 }, + { 0x8c213d9da502de45, 0x4526f422cc340000 }, + { 0xaf298d050e4395d6, 0x9670b12b7f410000 }, + { 0xdaf3f04651d47b4c, 0x3c0cdd765f114000 }, + { 0x88d8762bf324cd0f, 0xa5880a69fb6ac800 }, + { 0xab0e93b6efee0053, 0x8eea0d047a457a00 }, + { 0xd5d238a4abe98068, 0x72a4904598d6d880 }, + { 0x85a36366eb71f041, 0x47a6da2b7f864750 }, + { 0xa70c3c40a64e6c51, 0x999090b65f67d924 }, + { 0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d }, + { 0x82818f1281ed449f, 0xbff8f10e7a8921a4 }, + { 0xa321f2d7226895c7, 0xaff72d52192b6a0d }, + { 0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490 }, + { 0xfee50b7025c36a08, 0x02f236d04753d5b4 }, + { 0x9f4f2726179a2245, 0x01d762422c946590 }, + { 0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5 }, + { 0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2 }, + { 0x9b934c3b330c8577, 0x63cc55f49f88eb2f }, + { 0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb }, + { 0xf316271c7fc3908a, 0x8bef464e3945ef7a }, + { 0x97edd871cfda3a56, 0x97758bf0e3cbb5ac }, + { 0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317 }, + { 0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd }, + { 0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a }, + { 0xb975d6b6ee39e436, 0xb3e2fd538e122b44 }, + { 0xe7d34c64a9c85d44, 0x60dbbca87196b616 }, + { 0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd }, + { 0xb51d13aea4a488dd, 0x6babab6398bdbe41 }, + { 0xe264589a4dcdab14, 0xc696963c7eed2dd1 }, + { 0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2 }, + { 0xb0de65388cc8ada8, 0x3b25a55f43294bcb }, + { 0xdd15fe86affad912, 0x49ef0eb713f39ebe }, + { 0x8a2dbf142dfcc7ab, 0x6e3569326c784337 }, + { 0xacb92ed9397bf996, 0x49c2c37f07965404 }, + { 0xd7e77a8f87daf7fb, 0xdc33745ec97be906 }, + { 0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3 }, + { 0xa8acd7c0222311bc, 0xc40832ea0d68ce0c }, + { 0xd2d80db02aabd62b, 0xf50a3fa490c30190 }, + { 0x83c7088e1aab65db, 0x792667c6da79e0fa }, + { 0xa4b8cab1a1563f52, 0x577001b891185938 }, + { 0xcde6fd5e09abcf26, 0xed4c0226b55e6f86 }, + { 0x80b05e5ac60b6178, 0x544f8158315b05b4 }, + { 0xa0dc75f1778e39d6, 0x696361ae3db1c721 }, + { 0xc913936dd571c84c, 0x03bc3a19cd1e38e9 }, + { 0xfb5878494ace3a5f, 0x04ab48a04065c723 }, + { 0x9d174b2dcec0e47b, 0x62eb0d64283f9c76 }, + { 0xc45d1df942711d9a, 0x3ba5d0bd324f8394 }, + { 0xf5746577930d6500, 0xca8f44ec7ee36479 }, + { 0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb }, + { 0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e }, + { 0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e }, + { 0x95d04aee3b80ece5, 0xbba1f1d158724a12 }, + { 0xbb445da9ca61281f, 0x2a8a6e45ae8edc97 }, + { 0xea1575143cf97226, 0xf52d09d71a3293bd }, + { 0x924d692ca61be758, 0x593c2626705f9c56 }, + { 0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c }, + { 0xe498f455c38b997a, 0x0b6dfb9c0f956447 }, + { 0x8edf98b59a373fec, 0x4724bd4189bd5eac }, + { 0xb2977ee300c50fe7, 0x58edec91ec2cb657 }, + { 0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed }, + { 0x8b865b215899f46c, 0xbd79e0d20082ee74 }, + { 0xae67f1e9aec07187, 0xecd8590680a3aa11 }, + { 0xda01ee641a708de9, 0xe80e6f4820cc9495 }, + { 0x884134fe908658b2, 0x3109058d147fdcdd }, + { 0xaa51823e34a7eede, 0xbd4b46f0599fd415 }, + { 0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a }, + { 0x850fadc09923329e, 0x03e2cf6bc604ddb0 }, + { 0xa6539930bf6bff45, 0x84db8346b786151c }, + { 0xcfe87f7cef46ff16, 0xe612641865679a63 }, + { 0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e }, + { 0xa26da3999aef7749, 0xe3be5e330f38f09d }, + { 0xcb090c8001ab551c, 0x5cadf5bfd3072cc5 }, + { 0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6 }, + { 0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa }, + { 0xc646d63501a1511d, 0xb281e1fd541501b8 }, + { 0xf7d88bc24209a565, 0x1f225a7ca91a4226 }, + { 0x9ae757596946075f, 0x3375788de9b06958 }, + { 0xc1a12d2fc3978937, 0x0052d6b1641c83ae }, + { 0xf209787bb47d6b84, 0xc0678c5dbd23a49a }, + { 0x9745eb4d50ce6332, 0xf840b7ba963646e0 }, + { 0xbd176620a501fbff, 0xb650e5a93bc3d898 }, + { 0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe }, + { 0x93ba47c980e98cdf, 0xc66f336c36b10137 }, + { 0xb8a8d9bbe123f017, 0xb80b0047445d4184 }, + { 0xe6d3102ad96cec1d, 0xa60dc059157491e5 }, + { 0x9043ea1ac7e41392, 0x87c89837ad68db2f }, + { 0xb454e4a179dd1877, 0x29babe4598c311fb }, + { 0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a }, + { 0x8ce2529e2734bb1d, 0x1899e4a65f58660c }, + { 0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f }, + { 0xdc21a1171d42645d, 0x76707543f4fa1f73 }, + { 0x899504ae72497eba, 0x6a06494a791c53a8 }, + { 0xabfa45da0edbde69, 0x0487db9d17636892 }, + { 0xd6f8d7509292d603, 0x45a9d2845d3c42b6 }, + { 0x865b86925b9bc5c2, 0x0b8a2392ba45a9b2 }, + { 0xa7f26836f282b732, 0x8e6cac7768d7141e }, + { 0xd1ef0244af2364ff, 0x3207d795430cd926 }, + { 0x8335616aed761f1f, 0x7f44e6bd49e807b8 }, + { 0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6 }, + { 0xcd036837130890a1, 0x36dba887c37a8c0f }, + { 0x802221226be55a64, 0xc2494954da2c9789 }, + { 0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c }, + { 0xc83553c5c8965d3d, 0x6f92829494e5acc7 }, + { 0xfa42a8b73abbf48c, 0xcb772339ba1f17f9 }, + { 0x9c69a97284b578d7, 0xff2a760414536efb }, + { 0xc38413cf25e2d70d, 0xfef5138519684aba }, + { 0xf46518c2ef5b8cd1, 0x7eb258665fc25d69 }, + { 0x98bf2f79d5993802, 0xef2f773ffbd97a61 }, + { 0xbeeefb584aff8603, 0xaafb550ffacfd8fa }, + { 0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38 }, + { 0x952ab45cfa97a0b2, 0xdd945a747bf26183 }, + { 0xba756174393d88df, 0x94f971119aeef9e4 }, + { 0xe912b9d1478ceb17, 0x7a37cd5601aab85d }, + { 0x91abb422ccb812ee, 0xac62e055c10ab33a }, + { 0xb616a12b7fe617aa, 0x577b986b314d6009 }, + { 0xe39c49765fdf9d94, 0xed5a7e85fda0b80b }, + { 0x8e41ade9fbebc27d, 0x14588f13be847307 }, + { 0xb1d219647ae6b31c, 0x596eb2d8ae258fc8 }, + { 0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb }, + { 0x8aec23d680043bee, 0x25de7bb9480d5854 }, + { 0xada72ccc20054ae9, 0xaf561aa79a10ae6a }, + { 0xd910f7ff28069da4, 0x1b2ba1518094da04 }, + { 0x87aa9aff79042286, 0x90fb44d2f05d0842 }, + { 0xa99541bf57452b28, 0x353a1607ac744a53 }, + { 0xd3fa922f2d1675f2, 0x42889b8997915ce8 }, + { 0x847c9b5d7c2e09b7, 0x69956135febada11 }, + { 0xa59bc234db398c25, 0x43fab9837e699095 }, + { 0xcf02b2c21207ef2e, 0x94f967e45e03f4bb }, + { 0x8161afb94b44f57d, 0x1d1be0eebac278f5 }, + { 0xa1ba1ba79e1632dc, 0x6462d92a69731732 }, + { 0xca28a291859bbf93, 0x7d7b8f7503cfdcfe }, + { 0xfcb2cb35e702af78, 0x5cda735244c3d43e }, + { 0x9defbf01b061adab, 0x3a0888136afa64a7 }, + { 0xc56baec21c7a1916, 0x088aaa1845b8fdd0 }, + { 0xf6c69a72a3989f5b, 0x8aad549e57273d45 }, + { 0x9a3c2087a63f6399, 0x36ac54e2f678864b }, + { 0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd }, + { 0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5 }, + { 0x969eb7c47859e743, 0x9f644ae5a4b1b325 }, + { 0xbc4665b596706114, 0x873d5d9f0dde1fee }, + { 0xeb57ff22fc0c7959, 0xa90cb506d155a7ea }, + { 0x9316ff75dd87cbd8, 0x09a7f12442d588f2 }, + { 0xb7dcbf5354e9bece, 0x0c11ed6d538aeb2f }, + { 0xe5d3ef282a242e81, 0x8f1668c8a86da5fa }, + { 0x8fa475791a569d10, 0xf96e017d694487bc }, + { 0xb38d92d760ec4455, 0x37c981dcc395a9ac }, + { 0xe070f78d3927556a, 0x85bbe253f47b1417 }, + { 0x8c469ab843b89562, 0x93956d7478ccec8e }, + { 0xaf58416654a6babb, 0x387ac8d1970027b2 }, + { 0xdb2e51bfe9d0696a, 0x06997b05fcc0319e }, + { 0x88fcf317f22241e2, 0x441fece3bdf81f03 }, + { 0xab3c2fddeeaad25a, 0xd527e81cad7626c3 }, + { 0xd60b3bd56a5586f1, 0x8a71e223d8d3b074 }, + { 0x85c7056562757456, 0xf6872d5667844e49 }, + { 0xa738c6bebb12d16c, 0xb428f8ac016561db }, + { 0xd106f86e69d785c7, 0xe13336d701beba52 }, + { 0x82a45b450226b39c, 0xecc0024661173473 }, + { 0xa34d721642b06084, 0x27f002d7f95d0190 }, + { 0xcc20ce9bd35c78a5, 0x31ec038df7b441f4 }, + { 0xff290242c83396ce, 0x7e67047175a15271 }, + { 0x9f79a169bd203e41, 0x0f0062c6e984d386 }, + { 0xc75809c42c684dd1, 0x52c07b78a3e60868 }, + { 0xf92e0c3537826145, 0xa7709a56ccdf8a82 }, + { 0x9bbcc7a142b17ccb, 0x88a66076400bb691 }, + { 0xc2abf989935ddbfe, 0x6acff893d00ea435 }, + { 0xf356f7ebf83552fe, 0x0583f6b8c4124d43 }, + { 0x98165af37b2153de, 0xc3727a337a8b704a }, + { 0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c }, + { 0xeda2ee1c7064130c, 0x1162def06f79df73 }, + { 0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8 }, + { 0xb9a74a0637ce2ee1, 0x6d953e2bd7173692 }, + { 0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437 }, + { 0x910ab1d4db9914a0, 0x1d9c9892400a22a2 }, + { 0xb54d5e4a127f59c8, 0x2503beb6d00cab4b }, + { 0xe2a0b5dc971f303a, 0x2e44ae64840fd61d }, + { 0x8da471a9de737e24, 0x5ceaecfed289e5d2 }, + { 0xb10d8e1456105dad, 0x7425a83e872c5f47 }, + { 0xdd50f1996b947518, 0xd12f124e28f77719 }, + { 0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f }, + { 0xace73cbfdc0bfb7b, 0x636cc64d1001550b }, + { 0xd8210befd30efa5a, 0x3c47f7e05401aa4e }, + { 0x8714a775e3e95c78, 0x65acfaec34810a71 }, + { 0xa8d9d1535ce3b396, 0x7f1839a741a14d0d }, + { 0xd31045a8341ca07c, 0x1ede48111209a050 }, + { 0x83ea2b892091e44d, 0x934aed0aab460432 }, + { 0xa4e4b66b68b65d60, 0xf81da84d5617853f }, + { 0xce1de40642e3f4b9, 0x36251260ab9d668e }, + { 0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019 }, + { 0xa1075a24e4421730, 0xb24cf65b8612f81f }, + { 0xc94930ae1d529cfc, 0xdee033f26797b627 }, + { 0xfb9b7cd9a4a7443c, 0x169840ef017da3b1 }, + { 0x9d412e0806e88aa5, 0x8e1f289560ee864e }, + { 0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2 }, + { 0xf5b5d7ec8acb58a2, 0xae10af696774b1db }, + { 0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29 }, + { 0xbff610b0cc6edd3f, 0x17fd090a58d32af3 }, + { 0xeff394dcff8a948e, 0xddfc4b4cef07f5b0 }, + { 0x95f83d0a1fb69cd9, 0x4abdaf101564f98e }, + { 0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1 }, + { 0xea53df5fd18d5513, 0x84c86189216dc5ed }, + { 0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4 }, + { 0xb7118682dbb66a77, 0x3fbc8c33221dc2a1 }, + { 0xe4d5e82392a40515, 0x0fabaf3feaa5334a }, + { 0x8f05b1163ba6832d, 0x29cb4d87f2a7400e }, + { 0xb2c71d5bca9023f8, 0x743e20e9ef511012 }, + { 0xdf78e4b2bd342cf6, 0x914da9246b255416 }, + { 0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e }, + { 0xae9672aba3d0c320, 0xa184ac2473b529b1 }, + { 0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e }, + { 0x8865899617fb1871, 0x7e2fa67c7a658892 }, + { 0xaa7eebfb9df9de8d, 0xddbb901b98feeab7 }, + { 0xd51ea6fa85785631, 0x552a74227f3ea565 }, + { 0x8533285c936b35de, 0xd53a88958f87275f }, + { 0xa67ff273b8460356, 0x8a892abaf368f137 }, + { 0xd01fef10a657842c, 0x2d2b7569b0432d85 }, + { 0x8213f56a67f6b29b, 0x9c3b29620e29fc73 }, + { 0xa298f2c501f45f42, 0x8349f3ba91b47b8f }, + { 0xcb3f2f7642717713, 0x241c70a936219a73 }, + { 0xfe0efb53d30dd4d7, 0xed238cd383aa0110 }, + { 0x9ec95d1463e8a506, 0xf4363804324a40aa }, + { 0xc67bb4597ce2ce48, 0xb143c6053edcd0d5 }, + { 0xf81aa16fdc1b81da, 0xdd94b7868e94050a }, + { 0x9b10a4e5e9913128, 0xca7cf2b4191c8326 }, + { 0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0 }, + { 0xf24a01a73cf2dccf, 0xbc633b39673c8cec }, + { 0x976e41088617ca01, 0xd5be0503e085d813 }, + { 0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18 }, + { 0xec9c459d51852ba2, 0xddf8e7d60ed1219e }, + { 0x93e1ab8252f33b45, 0xcabb90e5c942b503 }, + { 0xb8da1662e7b00a17, 0x3d6a751f3b936243 }, + { 0xe7109bfba19c0c9d, 0x0cc512670a783ad4 }, + { 0x906a617d450187e2, 0x27fb2b80668b24c5 }, + { 0xb484f9dc9641e9da, 0xb1f9f660802dedf6 }, + { 0xe1a63853bbd26451, 0x5e7873f8a0396973 }, + { 0x8d07e33455637eb2, 0xdb0b487b6423e1e8 }, + { 0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62 }, + { 0xdc5c5301c56b75f7, 0x7641a140cc7810fb }, + { 0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d }, + { 0xac2820d9623bf429, 0x546345fa9fbdcd44 }, + { 0xd732290fbacaf133, 0xa97c177947ad4095 }, + { 0x867f59a9d4bed6c0, 0x49ed8eabcccc485d }, + { 0xa81f301449ee8c70, 0x5c68f256bfff5a74 }, + { 0xd226fc195c6a2f8c, 0x73832eec6fff3111 }, + { 0x83585d8fd9c25db7, 0xc831fd53c5ff7eab }, + { 0xa42e74f3d032f525, 0xba3e7ca8b77f5e55 }, + { 0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb }, + { 0x80444b5e7aa7cf85, 0x7980d163cf5b81b3 }, + { 0xa0555e361951c366, 0xd7e105bcc332621f }, + { 0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7 }, + { 0xfa856334878fc150, 0xb14f98f6f0feb951 }, + { 0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3 }, + { 0xc3b8358109e84f07, 0x0a862f80ec4700c8 }, + { 0xf4a642e14c6262c8, 0xcd27bb612758c0fa }, + { 0x98e7e9cccfbd7dbd, 0x8038d51cb897789c }, + { 0xbf21e44003acdd2c, 0xe0470a63e6bd56c3 }, + { 0xeeea5d5004981478, 0x1858ccfce06cac74 }, + { 0x95527a5202df0ccb, 0x0f37801e0c43ebc8 }, + { 0xbaa718e68396cffd, 0xd30560258f54e6ba }, + { 0xe950df20247c83fd, 0x47c6b82ef32a2069 }, + { 0x91d28b7416cdd27e, 0x4cdc331d57fa5441 }, + { 0xb6472e511c81471d, 0xe0133fe4adf8e952 }, + { 0xe3d8f9e563a198e5, 0x58180fddd97723a6 }, + { 0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648 }, + { 0xb201833b35d63f73, 0x2cd2cc6551e513da }, + { 0xde81e40a034bcf4f, 0xf8077f7ea65e58d1 }, + { 0x8b112e86420f6191, 0xfb04afaf27faf782 }, + { 0xadd57a27d29339f6, 0x79c5db9af1f9b563 }, + { 0xd94ad8b1c7380874, 0x18375281ae7822bc }, + { 0x87cec76f1c830548, 0x8f2293910d0b15b5 }, + { 0xa9c2794ae3a3c69a, 0xb2eb3875504ddb22 }, + { 0xd433179d9c8cb841, 0x5fa60692a46151eb }, + { 0x849feec281d7f328, 0xdbc7c41ba6bcd333 }, + { 0xa5c7ea73224deff3, 0x12b9b522906c0800 }, + { 0xcf39e50feae16bef, 0xd768226b34870a00 }, + { 0x81842f29f2cce375, 0xe6a1158300d46640 }, + { 0xa1e53af46f801c53, 0x60495ae3c1097fd0 }, + { 0xca5e89b18b602368, 0x385bb19cb14bdfc4 }, + { 0xfcf62c1dee382c42, 0x46729e03dd9ed7b5 }, + { 0x9e19db92b4e31ba9, 0x6c07a2c26a8346d1 }, + { 0xc5a05277621be293, 0xc7098b7305241885 }, + { 0xf70867153aa2db38, 0xb8cbee4fc66d1ea7 } + }; + }; + + // Compressed cache for double + struct compressed_cache_detail { + static constexpr int compression_ratio = 27; + static constexpr std::size_t compressed_table_size = + (cache_holder::max_k - + cache_holder::min_k + compression_ratio) / compression_ratio; + + struct cache_holder_t { + wuint::uint128 table[compressed_table_size]; + }; + static constexpr cache_holder_t cache = [] { + cache_holder_t res{}; + for (std::size_t i = 0; i < compressed_table_size; ++i) { + res.table[i] = cache_holder::cache[i * compression_ratio]; + } + return res; + }(); + + struct pow5_holder_t { + std::uint64_t table[compression_ratio]; + }; + static constexpr pow5_holder_t pow5 = [] { + pow5_holder_t res{}; + std::uint64_t p = 1; + for (std::size_t i = 0; i < compression_ratio; ++i) { + res.table[i] = p; + p *= 5; + } + return res; + }(); + + static constexpr std::uint32_t errors[] = { + 0x50001400, 0x54044100, 0x54014555, 0x55954415, 0x54115555, + 0x00000001, 0x50000000, 0x00104000, 0x54010004, 0x05004001, + 0x55555544, 0x41545555, 0x54040551, 0x15445545, 0x51555514, + 0x10000015, 0x00101100, 0x01100015, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x04450514, 0x45414110, 0x55555145, + 0x50544050, 0x15040155, 0x11054140, 0x50111514, 0x11451454, + 0x00400541, 0x00000000, 0x55555450, 0x10056551, 0x10054011, + 0x55551014, 0x69514555, 0x05151109, 0x00155555 + }; + }; + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // Policies + //////////////////////////////////////////////////////////////////////////////////////// + + namespace detail { + // Forward declare the implementation class + template + struct impl; + + namespace policy_impl { + // Sign policy + namespace sign { + struct base {}; + + struct ignore : base { + using sign_policy = ignore; + static constexpr bool return_has_sign = false; + + template + static constexpr void handle_sign(ieee754_bits, Fp&) noexcept {} + }; + + struct return_sign : base { + using sign_policy = return_sign; + static constexpr bool return_has_sign = true; + + template + static constexpr void handle_sign(ieee754_bits br, Fp& fp) noexcept { + fp.is_negative = br.is_negative(); + } + }; + } + + // Trailing zero policy + namespace trailing_zero { + struct base {}; + + struct ignore : base { + using trailing_zero_policy = ignore; + static constexpr bool report_trailing_zeros = false; + + template + static constexpr void on_trailing_zeros(Fp&) noexcept {} + + template + static constexpr void no_trailing_zeros(Fp&) noexcept {} + }; + + struct remove : base { + using trailing_zero_policy = remove; + static constexpr bool report_trailing_zeros = false; + + template + static constexpr void on_trailing_zeros(Fp& fp) noexcept { + fp.exponent += + impl::remove_trailing_zeros(fp.significand); + } + + template + static constexpr void no_trailing_zeros(Fp&) noexcept {} + }; + + struct report : base { + using trailing_zero_policy = report; + static constexpr bool report_trailing_zeros = true; + + template + static constexpr void on_trailing_zeros(Fp& fp) noexcept { + fp.may_have_trailing_zeros = true; + } + + template + static constexpr void no_trailing_zeros(Fp& fp) noexcept { + fp.may_have_trailing_zeros = false; + } + }; + } + + // Rounding mode policy + namespace rounding_mode { + struct base {}; + + enum class tag_t { + to_nearest, + left_closed_directed, + right_closed_directed + }; + namespace interval_type { + struct symmetric_boundary { + static constexpr bool is_symmetric = true; + bool is_closed; + constexpr bool include_left_endpoint() const noexcept { + return is_closed; + } + constexpr bool include_right_endpoint() const noexcept { + return is_closed; + } + }; + struct asymmetric_boundary { + static constexpr bool is_symmetric = false; + bool is_left_closed; + constexpr bool include_left_endpoint() const noexcept { + return is_left_closed; + } + constexpr bool include_right_endpoint() const noexcept { + return !is_left_closed; + } + }; + struct closed { + static constexpr bool is_symmetric = true; + static constexpr bool include_left_endpoint() noexcept { + return true; + } + static constexpr bool include_right_endpoint() noexcept { + return true; + } + }; + struct open { + static constexpr bool is_symmetric = true; + static constexpr bool include_left_endpoint() noexcept { + return false; + } + static constexpr bool include_right_endpoint() noexcept { + return false; + } + }; + struct left_closed_right_open { + static constexpr bool is_symmetric = false; + static constexpr bool include_left_endpoint() noexcept { + return true; + } + static constexpr bool include_right_endpoint() noexcept { + return false; + } + }; + struct right_closed_left_open { + static constexpr bool is_symmetric = false; + static constexpr bool include_left_endpoint() noexcept { + return false; + } + static constexpr bool include_right_endpoint() noexcept { + return true; + } + }; + } + + struct nearest_to_even : base { + using rounding_mode_policy = nearest_to_even; + static constexpr auto tag = tag_t::to_nearest; + + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(nearest_to_even{}); + } + + template + static constexpr interval_type::symmetric_boundary + interval_type_normal(ieee754_bits br) noexcept + { + return{ br.u % 2 == 0 }; + } + template + static constexpr interval_type::closed + interval_type_shorter(ieee754_bits) noexcept + { + return{}; + } + }; + struct nearest_to_odd : base { + using rounding_mode_policy = nearest_to_odd; + static constexpr auto tag = tag_t::to_nearest; + + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(nearest_to_odd{}); + } + + template + static constexpr interval_type::symmetric_boundary + interval_type_normal(ieee754_bits br) noexcept + { + return{ br.u % 2 != 0 }; + } + template + static constexpr interval_type::closed + interval_type_shorter(ieee754_bits) noexcept + { + return{}; + } + }; + struct nearest_toward_plus_infinity : base { + using rounding_mode_policy = nearest_toward_plus_infinity; + static constexpr auto tag = tag_t::to_nearest; + + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(nearest_toward_plus_infinity{}); + } + + template + static constexpr interval_type::asymmetric_boundary + interval_type_normal(ieee754_bits br) noexcept + { + return{ !br.is_negative() }; + } + template + static constexpr interval_type::asymmetric_boundary + interval_type_shorter(ieee754_bits br) noexcept + { + return{ !br.is_negative() }; + } + }; + struct nearest_toward_minus_infinity : base { + using rounding_mode_policy = nearest_toward_minus_infinity; + static constexpr auto tag = tag_t::to_nearest; + + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(nearest_toward_minus_infinity{}); + } + + template + static constexpr interval_type::asymmetric_boundary + interval_type_normal(ieee754_bits br) noexcept + { + return{ br.is_negative() }; + } + template + static constexpr interval_type::asymmetric_boundary + interval_type_shorter(ieee754_bits br) noexcept + { + return{ br.is_negative() }; + } + }; + struct nearest_toward_zero : base { + using rounding_mode_policy = nearest_toward_zero; + static constexpr auto tag = tag_t::to_nearest; + + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(nearest_toward_zero{}); + } + template + static constexpr interval_type::right_closed_left_open + interval_type_normal(ieee754_bits) noexcept + { + return{}; + } + template + static constexpr interval_type::right_closed_left_open + interval_type_shorter(ieee754_bits) noexcept + { + return{}; + } + }; + struct nearest_away_from_zero : base { + using rounding_mode_policy = nearest_away_from_zero; + static constexpr auto tag = tag_t::to_nearest; + + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(nearest_away_from_zero{}); + } + template + static constexpr interval_type::left_closed_right_open + interval_type_normal(ieee754_bits) noexcept + { + return{}; + } + template + static constexpr interval_type::left_closed_right_open + interval_type_shorter(ieee754_bits) noexcept + { + return{}; + } + }; + + namespace detail { + struct nearest_always_closed { + static constexpr auto tag = tag_t::to_nearest; + + template + static constexpr interval_type::closed + interval_type_normal(ieee754_bits) noexcept + { + return{}; + } + template + static constexpr interval_type::closed + interval_type_shorter(ieee754_bits) noexcept + { + return{}; + } + }; + struct nearest_always_open { + static constexpr auto tag = tag_t::to_nearest; + + template + static constexpr interval_type::open + interval_type_normal(ieee754_bits) noexcept + { + return{}; + } + template + static constexpr interval_type::open + interval_type_shorter(ieee754_bits) noexcept + { + return{}; + } + }; + } + + struct nearest_to_even_static_boundary : base { + using rounding_mode_policy = nearest_to_even_static_boundary; + template + static auto delegate(ieee754_bits br, Func&& f) noexcept { + if (br.u % 2 == 0) { + return f(detail::nearest_always_closed{}); + } + else { + return f(detail::nearest_always_open{}); + } + } + }; + struct nearest_to_odd_static_boundary : base { + using rounding_mode_policy = nearest_to_odd_static_boundary; + template + static auto delegate(ieee754_bits br, Func&& f) noexcept { + if (br.u % 2 == 0) { + return f(detail::nearest_always_open{}); + } + else { + return f(detail::nearest_always_closed{}); + } + } + }; + struct nearest_toward_plus_infinity_static_boundary : base { + using rounding_mode_policy = nearest_toward_plus_infinity_static_boundary; + template + static auto delegate(ieee754_bits br, Func&& f) noexcept { + if (br.is_negative()) { + return f(nearest_toward_zero{}); + } + else { + return f(nearest_away_from_zero{}); + } + } + }; + struct nearest_toward_minus_infinity_static_boundary : base { + using rounding_mode_policy = nearest_toward_minus_infinity_static_boundary; + template + static auto delegate(ieee754_bits br, Func&& f) noexcept { + if (br.is_negative()) { + return f(nearest_away_from_zero{}); + } + else { + return f(nearest_toward_zero{}); + } + } + }; + + namespace detail { + struct left_closed_directed { + static constexpr auto tag = tag_t::left_closed_directed; + + template + static constexpr interval_type::left_closed_right_open + interval_type_normal(ieee754_bits) noexcept + { + return{}; + } + }; + struct right_closed_directed { + static constexpr auto tag = tag_t::right_closed_directed; + + template + static constexpr interval_type::right_closed_left_open + interval_type_normal(ieee754_bits) noexcept + { + return{}; + } + }; + } + + struct toward_plus_infinity : base { + using rounding_mode_policy = toward_plus_infinity; + template + static auto delegate(ieee754_bits br, Func&& f) noexcept { + if (br.is_negative()) { + return f(detail::left_closed_directed{}); + } + else { + return f(detail::right_closed_directed{}); + } + } + }; + struct toward_minus_infinity : base { + using rounding_mode_policy = toward_minus_infinity; + template + static auto delegate(ieee754_bits br, Func&& f) noexcept { + if (br.is_negative()) { + return f(detail::right_closed_directed{}); + } + else { + return f(detail::left_closed_directed{}); + } + } + }; + struct toward_zero : base { + using rounding_mode_policy = toward_zero; + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(detail::left_closed_directed{}); + } + }; + struct away_from_zero : base { + using rounding_mode_policy = away_from_zero; + template + static auto delegate(ieee754_bits, Func&& f) noexcept { + return f(detail::right_closed_directed{}); + } + }; + } + + // Correct rounding policy + namespace correct_rounding { + struct base {}; + + enum class tag_t { + do_not_care, + to_even, + to_odd, + away_from_zero, + toward_zero + }; + + struct do_not_care : base { + using correct_rounding_policy = do_not_care; + static constexpr auto tag = tag_t::do_not_care; + + template + static constexpr void break_rounding_tie(Fp&) noexcept {} + }; + + struct to_even : base { + using correct_rounding_policy = to_even; + static constexpr auto tag = tag_t::to_even; + + template + static constexpr void break_rounding_tie(Fp& fp) noexcept + { + fp.significand = fp.significand % 2 == 0 ? + fp.significand : fp.significand - 1; + } + }; + + struct to_odd : base { + using correct_rounding_policy = to_odd; + static constexpr auto tag = tag_t::to_odd; + + template + static constexpr void break_rounding_tie(Fp& fp) noexcept + { + fp.significand = fp.significand % 2 != 0 ? + fp.significand : fp.significand - 1; + } + }; + + struct away_from_zero : base { + using correct_rounding_policy = away_from_zero; + static constexpr auto tag = tag_t::away_from_zero; + + template + static constexpr void break_rounding_tie(Fp& /*fp*/) noexcept {} + }; + + struct toward_zero : base { + using correct_rounding_policy = toward_zero; + static constexpr auto tag = tag_t::toward_zero; + + template + static constexpr void break_rounding_tie(Fp& fp) noexcept + { + --fp.significand; + } + }; + } + + namespace cache { + struct base {}; + + struct normal : base { + using cache_policy = normal; + template + static constexpr typename cache_holder::cache_entry_type get_cache(int k) noexcept { + assert(k >= cache_holder::min_k && k <= cache_holder::max_k); + return cache_holder::cache[std::size_t(k - cache_holder::min_k)]; + } + }; + + struct compressed : base { + using cache_policy = compressed; + template + static constexpr typename cache_holder::cache_entry_type get_cache(int k) noexcept { + assert(k >= cache_holder::min_k && k <= cache_holder::max_k); + + if constexpr (format == ieee754_format::binary64) + { + // Compute base index + auto cache_index = (k - cache_holder::min_k) / + compressed_cache_detail::compression_ratio; + auto kb = cache_index * compressed_cache_detail::compression_ratio + + cache_holder::min_k; + auto offset = k - kb; + + // Get base cache + auto base_cache = compressed_cache_detail::cache.table[cache_index]; + + if (offset == 0) { + return base_cache; + } + else { + // Compute the required amount of bit-shift + auto alpha = log::floor_log2_pow10(kb + offset) - log::floor_log2_pow10(kb) - offset; + assert(alpha > 0 && alpha < 64); + + // Try to recover the real cache + auto pow5 = compressed_cache_detail::pow5.table[offset]; + auto recovered_cache = wuint::umul128(base_cache.high(), pow5); + auto middle_low = wuint::umul128(base_cache.low() - (kb < 0 ? 1 : 0), pow5); + + recovered_cache += middle_low.high(); + + auto high_to_middle = recovered_cache.high() << (64 - alpha); + auto middle_to_low = recovered_cache.low() << (64 - alpha); + + recovered_cache = wuint::uint128{ + (recovered_cache.low() >> alpha) | high_to_middle, + ((middle_low.low() >> alpha) | middle_to_low) + }; + + if (kb < 0) { + recovered_cache += 1; + } + + // Get error + auto error_idx = (k - cache_holder::min_k) / 16; + auto error = (compressed_cache_detail::errors[error_idx] >> + ((k - cache_holder::min_k) % 16) * 2) & 0x3; + + // Add the error back + assert(recovered_cache.low() + error >= recovered_cache.low()); + recovered_cache = { + recovered_cache.high(), + recovered_cache.low() + error + }; + + return recovered_cache; + } + } + else + { + return cache_holder::cache[std::size_t(k - cache_holder::min_k)]; + } + } + }; + } + + namespace input_validation { + struct base {}; + + struct assert_finite : base { + using input_validation_policy = assert_finite; + template + static void validate_input([[maybe_unused]] ieee754_bits br) noexcept + { + assert(br.is_finite()); + } + }; + + struct do_nothing : base { + using input_validation_policy = do_nothing; + template + static void validate_input(ieee754_bits) noexcept {} + }; + } + } + } + + namespace policy { + namespace sign { + inline constexpr auto ignore = detail::policy_impl::sign::ignore{}; + inline constexpr auto return_sign = detail::policy_impl::sign::return_sign{}; + } + + namespace trailing_zero { + inline constexpr auto ignore = detail::policy_impl::trailing_zero::ignore{}; + inline constexpr auto remove = detail::policy_impl::trailing_zero::remove{}; + inline constexpr auto report = detail::policy_impl::trailing_zero::report{}; + } + + namespace rounding_mode { + inline constexpr auto nearest_to_even = + detail::policy_impl::rounding_mode::nearest_to_even{}; + inline constexpr auto nearest_to_odd = + detail::policy_impl::rounding_mode::nearest_to_odd{}; + inline constexpr auto nearest_toward_plus_infinity = + detail::policy_impl::rounding_mode::nearest_toward_plus_infinity{}; + inline constexpr auto nearest_toward_minus_infinity = + detail::policy_impl::rounding_mode::nearest_toward_minus_infinity{}; + inline constexpr auto nearest_toward_zero = + detail::policy_impl::rounding_mode::nearest_toward_zero{}; + inline constexpr auto nearest_away_from_zero = + detail::policy_impl::rounding_mode::nearest_away_from_zero{}; + + inline constexpr auto nearest_to_even_static_boundary = + detail::policy_impl::rounding_mode::nearest_to_even_static_boundary{}; + inline constexpr auto nearest_to_odd_static_boundary = + detail::policy_impl::rounding_mode::nearest_to_odd_static_boundary{}; + inline constexpr auto nearest_toward_plus_infinity_static_boundary = + detail::policy_impl::rounding_mode::nearest_toward_plus_infinity_static_boundary{}; + inline constexpr auto nearest_toward_minus_infinity_static_boundary = + detail::policy_impl::rounding_mode::nearest_toward_minus_infinity_static_boundary{}; + + inline constexpr auto toward_plus_infinity = + detail::policy_impl::rounding_mode::toward_plus_infinity{}; + inline constexpr auto toward_minus_infinity = + detail::policy_impl::rounding_mode::toward_minus_infinity{}; + inline constexpr auto toward_zero = + detail::policy_impl::rounding_mode::toward_zero{}; + inline constexpr auto away_from_zero = + detail::policy_impl::rounding_mode::away_from_zero{}; + } + + namespace correct_rounding { + inline constexpr auto do_not_care = detail::policy_impl::correct_rounding::do_not_care{}; + inline constexpr auto to_even = detail::policy_impl::correct_rounding::to_even{}; + inline constexpr auto to_odd = detail::policy_impl::correct_rounding::to_odd{}; + inline constexpr auto away_from_zero = detail::policy_impl::correct_rounding::away_from_zero{}; + inline constexpr auto toward_zero = detail::policy_impl::correct_rounding::toward_zero{}; + } + + namespace cache { + inline constexpr auto normal = detail::policy_impl::cache::normal{}; + inline constexpr auto compressed = detail::policy_impl::cache::compressed{}; + } + + namespace input_validation { + inline constexpr auto assert_finite = detail::policy_impl::input_validation::assert_finite{}; + inline constexpr auto do_nothing = detail::policy_impl::input_validation::do_nothing{}; + } + } + + namespace detail { + //////////////////////////////////////////////////////////////////////////////////////// + // The main algorithm + //////////////////////////////////////////////////////////////////////////////////////// + + // Get sign/decimal significand/decimal exponent from + // the bit representation of a floating-point number + template + struct impl : private ieee754_traits, + private ieee754_format_info::format> + { + using carrier_uint = typename ieee754_traits::carrier_uint; + + using ieee754_traits::format; + using ieee754_traits::carrier_bits; + using ieee754_format_info::significand_bits; + using ieee754_format_info::min_exponent; + using ieee754_format_info::max_exponent; + using ieee754_format_info::exponent_bias; + using ieee754_format_info::decimal_digits; + + static constexpr int kappa = format == ieee754_format::binary32 ? 1 : 2; + static_assert(kappa >= 1); + static_assert(carrier_bits >= significand_bits + 2 + log::floor_log2_pow10(kappa + 1)); + + static constexpr int min_k = [] { + constexpr auto a = -log::floor_log10_pow2_minus_log10_4_over_3( + int(max_exponent - significand_bits)); + constexpr auto b = -log::floor_log10_pow2( + int(max_exponent - significand_bits)) + kappa; + return a < b ? a : b; + }(); + static_assert(min_k >= cache_holder::min_k); + + static constexpr int max_k = [] { + constexpr auto a = -log::floor_log10_pow2_minus_log10_4_over_3( + int(min_exponent - significand_bits + 1)); + constexpr auto b = -log::floor_log10_pow2( + int(min_exponent - significand_bits)) + kappa; + return a > b ? a : b; + }(); + static_assert(max_k <= cache_holder::max_k); + + using cache_entry_type = + typename cache_holder::cache_entry_type; + static constexpr auto cache_bits = + cache_holder::cache_bits; + + static constexpr int max_power_of_factor_of_5 = log::floor_log5_pow2(int(significand_bits + 2)); + static constexpr int divisibility_check_by_5_threshold = + log::floor_log2_pow10(max_power_of_factor_of_5 + kappa + 1); + + static constexpr int case_fc_pm_half_lower_threshold = -kappa - log::floor_log5_pow2(kappa); + static constexpr int case_fc_pm_half_upper_threshold = log::floor_log2_pow10(kappa + 1); + + static constexpr int case_fc_lower_threshold = -kappa - 1 - log::floor_log5_pow2(kappa + 1); + static constexpr int case_fc_upper_threshold = log::floor_log2_pow10(kappa + 1); + + static constexpr int case_shorter_interval_left_endpoint_lower_threshold = 2; + static constexpr int case_shorter_interval_left_endpoint_upper_threshold = 2 + + log::floor_log2(compute_power< + count_factors<5>((carrier_uint(1) << (significand_bits + 2)) - 1) + 1 + >(10) / 3); + + static constexpr int case_shorter_interval_right_endpoint_lower_threshold = 0; + static constexpr int case_shorter_interval_right_endpoint_upper_threshold = 2 + + log::floor_log2(compute_power< + count_factors<5>((carrier_uint(1) << (significand_bits + 1)) + 1) + 1 + >(10) / 3); + + static constexpr int shorter_interval_tie_lower_threshold = + -log::floor_log5_pow2_minus_log5_3(significand_bits + 4) - 2 - significand_bits; + static constexpr int shorter_interval_tie_upper_threshold = + -log::floor_log5_pow2(significand_bits + 2) - 2 - significand_bits; + + //// The main algorithm assumes the input is a normal/subnormal finite number + + template + JKJ_SAFEBUFFERS static ReturnType compute_nearest(ieee754_bits const br) noexcept + { + ////////////////////////////////////////////////////////////////////// + // Step 1: integer promotion & Schubfach multiplier calculation + ////////////////////////////////////////////////////////////////////// + + ReturnType ret_value; + + SignPolicy::handle_sign(br, ret_value); + + auto significand = br.extract_significand_bits(); + auto exponent = int(br.extract_exponent_bits()); + + // Deal with normal/subnormal dichotomy + if (exponent != 0) { + exponent += exponent_bias - significand_bits; + + // Shorter interval case; proceed like Schubfach + if (significand == 0) { + shorter_interval_case( + ret_value, exponent, + IntervalTypeProvider::interval_type_shorter(br)); + return ret_value; + } + + significand |= (carrier_uint(1) << significand_bits); + } + // Subnormal case; interval is always regular + else { + exponent = min_exponent - significand_bits; + } + + auto const interval_type = IntervalTypeProvider::interval_type_normal(br); + + // Compute k and beta + int const minus_k = log::floor_log10_pow2(exponent) - kappa; + auto const cache = CachePolicy::template get_cache(-minus_k); + int const beta_minus_1 = exponent + log::floor_log2_pow10(-minus_k); + + // Compute zi and deltai + // 10^kappa <= deltai < 10^(kappa + 1) + auto const deltai = compute_delta(cache, beta_minus_1); + carrier_uint const two_fc = significand << 1; + carrier_uint const two_fr = two_fc | 1; + carrier_uint const zi = compute_mul(two_fr << beta_minus_1, cache); + + + ////////////////////////////////////////////////////////////////////// + // Step 2: Try larger divisor; remove trailing zeros if necessary + ////////////////////////////////////////////////////////////////////// + + constexpr auto big_divisor = compute_power(std::uint32_t(10)); + constexpr auto small_divisor = compute_power(std::uint32_t(10)); + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here + ret_value.significand = div::divide_by_pow10(zi); + auto r = std::uint32_t(zi - big_divisor * ret_value.significand); + + if (r > deltai) { + goto small_divisor_case_label; + } + else if (r < deltai) { + // Exclude the right endpoint if necessary + if (r == 0 && !interval_type.include_right_endpoint() && + is_product_integer(two_fr, exponent, minus_k)) + { + if constexpr (CorrectRoundingPolicy::tag == + policy_impl::correct_rounding::tag_t::do_not_care) + { + ret_value.significand *= 10; + ret_value.exponent = minus_k + kappa; + --ret_value.significand; + return ret_value; + } + else { + --ret_value.significand; + r = big_divisor; + goto small_divisor_case_label; + } + } + } + else { + // r == deltai; compare fractional parts + // Check conditions in the order different from the paper + // to take advantage of short-circuiting + auto const two_fl = two_fc - 1; + if ((!interval_type.include_left_endpoint() || + !is_product_integer( + two_fl, exponent, minus_k)) && + !compute_mul_parity(two_fl, cache, beta_minus_1)) + { + goto small_divisor_case_label; + } + } + ret_value.exponent = minus_k + kappa + 1; + + // We may need to remove trailing zeros + TrailingZeroPolicy::on_trailing_zeros(ret_value); + return ret_value; + + + ////////////////////////////////////////////////////////////////////// + // Step 3: Find the significand with the smaller divisor + ////////////////////////////////////////////////////////////////////// + + small_divisor_case_label: + TrailingZeroPolicy::no_trailing_zeros(ret_value); + ret_value.significand *= 10; + ret_value.exponent = minus_k + kappa; + + constexpr auto mask = (std::uint32_t(1) << kappa) - 1; + + if constexpr (CorrectRoundingPolicy::tag == + policy_impl::correct_rounding::tag_t::do_not_care) + { + // Normally, we want to compute + // ret_value.significand += r / small_divisor + // and return, but we need to take care of the case that the resulting + // value is exactly the right endpoint, while that is not included in the interval + if (!interval_type.include_right_endpoint()) { + // Is r divisible by 2^kappa? + if ((r & mask) == 0) { + r >>= kappa; + + // Is r divisible by 5^kappa? + if (div::check_divisibility_and_divide_by_pow5(r) && + is_product_integer(two_fr, exponent, minus_k)) + { + // This should be in the interval + ret_value.significand += r - 1; + } + else { + ret_value.significand += r; + } + } + else { + ret_value.significand += div::small_division_by_pow10(r); + } + } + else { + ret_value.significand += div::small_division_by_pow10(r); + } + } + else + { + auto dist = r - (deltai / 2) + (small_divisor / 2); + + // Is dist divisible by 2^kappa? + if ((dist & mask) == 0) { + bool const approx_y_parity = ((dist ^ (small_divisor / 2)) & 1) != 0; + dist >>= kappa; + + // Is dist divisible by 5^kappa? + if (div::check_divisibility_and_divide_by_pow5(dist)) { + ret_value.significand += dist; + + // Check z^(f) >= epsilon^(f) + // We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + // where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + // Since there are only 2 possibilities, we only need to care about the parity + // Also, zi and r should have the same parity since the divisor + // is an even number + if (compute_mul_parity(two_fc, cache, beta_minus_1) != approx_y_parity) { + --ret_value.significand; + } + else { + // If z^(f) >= epsilon^(f), we might have a tie + // when z^(f) == epsilon^(f), or equivalently, when y is an integer + // For tie-to-up case, we can just choose the upper one + if constexpr (CorrectRoundingPolicy::tag != + policy_impl::correct_rounding::tag_t::away_from_zero) + { + if (is_product_integer( + two_fc, exponent, minus_k)) + { + CorrectRoundingPolicy::break_rounding_tie(ret_value); + } + } + } + } + // Is dist not divisible by 5^kappa? + else { + ret_value.significand += dist; + } + } + // Is dist not divisible by 2^kappa? + else { + // Since we know dist is small, we might be able to optimize the division + // better than the compiler; we are computing dist / small_divisor here + ret_value.significand += div::small_division_by_pow10(dist); + } + } + return ret_value; + } + + template + JKJ_FORCEINLINE JKJ_SAFEBUFFERS static void shorter_interval_case( + ReturnType& ret_value, int const exponent, IntervalType const interval_type) noexcept + { + // Compute k and beta + int const minus_k = log::floor_log10_pow2_minus_log10_4_over_3(exponent); + int const beta_minus_1 = exponent + log::floor_log2_pow10(-minus_k); + + // Compute xi and zi + auto const cache = CachePolicy::template get_cache(-minus_k); + + auto xi = compute_left_endpoint_for_shorter_interval_case(cache, beta_minus_1); + auto zi = compute_right_endpoint_for_shorter_interval_case(cache, beta_minus_1); + + // If we don't accept the right endpoint and + // if the right endpoint is an integer, decrease it + if (!interval_type.include_right_endpoint() && + is_right_endpoint_integer_shorter_interval(exponent)) + { + --zi; + } + // If we don't accept the left endpoint or + // if the left endpoint is not an integer, increase it + if (!interval_type.include_left_endpoint() || + !is_left_endpoint_integer_shorter_interval(exponent)) + { + ++xi; + } + + // Try bigger divisor + ret_value.significand = zi / 10; + + // If succeed, remove trailing zeros if necessary and return + if (ret_value.significand * 10 >= xi) { + ret_value.exponent = minus_k + 1; + TrailingZeroPolicy::on_trailing_zeros(ret_value); + return; + } + + // Otherwise, compute the round-up of y + TrailingZeroPolicy::no_trailing_zeros(ret_value); + ret_value.significand = compute_round_up_for_shorter_interval_case(cache, beta_minus_1); + ret_value.exponent = minus_k; + + // When tie occurs, choose one of them according to the rule + if constexpr (CorrectRoundingPolicy::tag != + policy_impl::correct_rounding::tag_t::do_not_care && + CorrectRoundingPolicy::tag != + policy_impl::correct_rounding::tag_t::away_from_zero) + { + if (exponent >= shorter_interval_tie_lower_threshold && + exponent <= shorter_interval_tie_upper_threshold) + { + CorrectRoundingPolicy::break_rounding_tie(ret_value); + } + else if (ret_value.significand < xi) { + ++ret_value.significand; + } + } + else + { + if (ret_value.significand < xi) { + ++ret_value.significand; + } + } + } + + template + JKJ_SAFEBUFFERS static ReturnType + compute_left_closed_directed(ieee754_bits const br) noexcept + { + ////////////////////////////////////////////////////////////////////// + // Step 1: integer promotion & Schubfach multiplier calculation + ////////////////////////////////////////////////////////////////////// + + ReturnType ret_value; + + SignPolicy::handle_sign(br, ret_value); + + auto significand = br.extract_significand_bits(); + auto exponent = int(br.extract_exponent_bits()); + + // Deal with normal/subnormal dichotomy + if (exponent != 0) { + exponent += exponent_bias - significand_bits; + significand |= (carrier_uint(1) << significand_bits); + } + // Subnormal case; interval is always regular + else { + exponent = min_exponent - significand_bits; + } + + // Compute k and beta + int const minus_k = log::floor_log10_pow2(exponent) - kappa; + auto const cache = CachePolicy::template get_cache(-minus_k); + int const beta = exponent + log::floor_log2_pow10(-minus_k) + 1; + + // Compute xi and deltai + // 10^kappa <= deltai < 10^(kappa + 1) + auto const deltai = compute_delta(cache, beta - 1); + carrier_uint xi = compute_mul(significand << beta, cache); + + if (!is_product_integer(significand, exponent + 1, minus_k)) { + ++xi; + } + + ////////////////////////////////////////////////////////////////////// + // Step 2: Try larger divisor; remove trailing zeros if necessary + ////////////////////////////////////////////////////////////////////// + + constexpr auto big_divisor = compute_power(std::uint32_t(10)); + constexpr auto small_divisor = compute_power(std::uint32_t(10)); + + // Using an upper bound on xi, we might be able to optimize the division + // better than the compiler; we are computing xi / big_divisor here + ret_value.significand = div::divide_by_pow10(xi); + auto r = std::uint32_t(xi - big_divisor * ret_value.significand); + + if (r != 0) { + ++ret_value.significand; + r = big_divisor - r; + } + + if (r > deltai) { + goto small_divisor_case_label; + } + else if (r == deltai) { + // Compare the fractional parts + if (compute_mul_parity(significand + 1, cache, beta) || + is_product_integer(significand + 1, exponent + 1, minus_k)) + { + goto small_divisor_case_label; + } + + } + + // The ceiling is inside, so we are done + ret_value.exponent = minus_k + kappa + 1; + TrailingZeroPolicy::on_trailing_zeros(ret_value); + return ret_value; + + + ////////////////////////////////////////////////////////////////////// + // Step 3: Find the significand with the smaller divisor + ////////////////////////////////////////////////////////////////////// + + small_divisor_case_label: + ret_value.significand *= 10; + ret_value.significand -= div::small_division_by_pow10(r); + ret_value.exponent = minus_k + kappa; + TrailingZeroPolicy::no_trailing_zeros(ret_value); + return ret_value; + } + + template + JKJ_SAFEBUFFERS static ReturnType + compute_right_closed_directed(ieee754_bits const br) noexcept + { + ////////////////////////////////////////////////////////////////////// + // Step 1: integer promotion & Schubfach multiplier calculation + ////////////////////////////////////////////////////////////////////// + + ReturnType ret_value; + + SignPolicy::handle_sign(br, ret_value); + + auto significand = br.extract_significand_bits(); + auto exponent = int(br.extract_exponent_bits()); + + // Deal with normal/subnormal dichotomy + bool closer_boundary = false; + if (exponent != 0) { + exponent += exponent_bias - significand_bits; + if (significand == 0) { + closer_boundary = true; + } + significand |= (carrier_uint(1) << significand_bits); + } + // Subnormal case; interval is always regular + else { + exponent = min_exponent - significand_bits; + } + + // Compute k and beta + int const minus_k = log::floor_log10_pow2(exponent - (closer_boundary ? 1 : 0)) - kappa; + auto const cache = CachePolicy::template get_cache(-minus_k); + int const beta = exponent + log::floor_log2_pow10(-minus_k) + 1; + + // Compute zi and deltai + // 10^kappa <= deltai < 10^(kappa + 1) + auto const deltai = closer_boundary ? + compute_delta(cache, beta - 2) : + compute_delta(cache, beta - 1); + carrier_uint const zi = compute_mul(significand << beta, cache); + + + ////////////////////////////////////////////////////////////////////// + // Step 2: Try larger divisor; remove trailing zeros if necessary + ////////////////////////////////////////////////////////////////////// + + constexpr auto big_divisor = compute_power(std::uint32_t(10)); + constexpr auto small_divisor = compute_power(std::uint32_t(10)); + + // Using an upper bound on zi, we might be able to optimize the division + // better than the compiler; we are computing zi / big_divisor here + ret_value.significand = div::divide_by_pow10(zi); + auto const r = std::uint32_t(zi - big_divisor * ret_value.significand); + + if (r > deltai) { + goto small_divisor_case_label; + } + else if (r == deltai) { + // Compare the fractional parts + if (closer_boundary) { + if (!compute_mul_parity((significand * 2) - 1, cache, beta - 1)) + { + goto small_divisor_case_label; + } + } + else { + if (!compute_mul_parity(significand - 1, cache, beta)) + { + goto small_divisor_case_label; + } + } + } + + // The floor is inside, so we are done + ret_value.exponent = minus_k + kappa + 1; + TrailingZeroPolicy::on_trailing_zeros(ret_value); + return ret_value; + + + ////////////////////////////////////////////////////////////////////// + // Step 3: Find the significand with the small divisor + ////////////////////////////////////////////////////////////////////// + + small_divisor_case_label: + ret_value.significand *= 10; + ret_value.significand += div::small_division_by_pow10(r); + ret_value.exponent = minus_k + kappa; + TrailingZeroPolicy::no_trailing_zeros(ret_value); + return ret_value; + } + + // Remove trailing zeros from n and return the number of zeros removed + JKJ_FORCEINLINE static int remove_trailing_zeros(carrier_uint& n) noexcept { + constexpr auto max_power = [] { + auto max_possible_significand = + std::numeric_limits::max() / + compute_power(std::uint32_t(10)); + + int k = 0; + carrier_uint p = 1; + while (p < max_possible_significand / 10) { + p *= 10; + ++k; + } + return k; + }(); + + auto t = bits::countr_zero(n); + if (t > max_power) { + t = max_power; + } + + if constexpr (format == ieee754_format::binary32) { + constexpr auto const& divtable = + div::table_holder::table; + + int s = 0; + for (; s < t - 1; s += 2) { + if (n * divtable.mod_inv[2] > divtable.max_quotients[2]) { + break; + } + n *= divtable.mod_inv[2]; + } + if (s < t && n * divtable.mod_inv[1] <= divtable.max_quotients[1]) + { + n *= divtable.mod_inv[1]; + ++s; + } + n >>= s; + return s; + } + else { + static_assert(format == ieee754_format::binary64); + static_assert(kappa >= 2); + + // Divide by 10^8 and reduce to 32-bits + // Since ret_value.significand <= (2^64 - 1) / 1000 < 10^17, + // both of the quotient and the r should fit in 32-bits + + constexpr auto const& divtable = + div::table_holder::table; + + // If the number is divisible by 1'0000'0000, work with the quotient + if (t >= 8) { + auto quotient_candidate = n * divtable.mod_inv[8]; + + if (quotient_candidate <= divtable.max_quotients[8]) { + auto quotient = std::uint32_t(quotient_candidate >> 8); + + constexpr auto mod_inverse = std::uint32_t(divtable.mod_inv[1]); + constexpr auto max_quotient = + std::numeric_limits::max() / 5; + + int s = 8; + for (; s < t; ++s) { + if (quotient * mod_inverse > max_quotient) { + break; + } + quotient *= mod_inverse; + } + quotient >>= (s - 8); + n = quotient; + return s; + } + } + + // Otherwise, work with the remainder + auto quotient = std::uint32_t(div::divide_by_pow10<8, 54, 0>(n)); + auto remainder = std::uint32_t(n - 1'0000'0000 * quotient); + + constexpr auto mod_inverse = std::uint32_t(divtable.mod_inv[1]); + constexpr auto max_quotient = + std::numeric_limits::max() / 5; + + if (t == 0 || remainder * mod_inverse > max_quotient) { + return 0; + } + remainder *= mod_inverse; + + if (t == 1 || remainder * mod_inverse > max_quotient) { + n = (remainder >> 1) + + quotient * carrier_uint(1000'0000); + return 1; + } + remainder *= mod_inverse; + + if (t == 2 || remainder * mod_inverse > max_quotient) { + n = (remainder >> 2) + + quotient * carrier_uint(100'0000); + return 2; + } + remainder *= mod_inverse; + + if (t == 3 || remainder * mod_inverse > max_quotient) { + n = (remainder >> 3) + + quotient * carrier_uint(10'0000); + return 3; + } + remainder *= mod_inverse; + + if (t == 4 || remainder * mod_inverse > max_quotient) { + n = (remainder >> 4) + + quotient * carrier_uint(1'0000); + return 4; + } + remainder *= mod_inverse; + + if (t == 5 || remainder * mod_inverse > max_quotient) { + n = (remainder >> 5) + + quotient * carrier_uint(1000); + return 5; + } + remainder *= mod_inverse; + + if (t == 6 || remainder * mod_inverse > max_quotient) { + n = (remainder >> 6) + + quotient * carrier_uint(100); + return 6; + } + remainder *= mod_inverse; + + n = (remainder >> 7) + + quotient * carrier_uint(10); + return 7; + } + } + + static carrier_uint compute_mul(carrier_uint u, cache_entry_type const& cache) noexcept + { + if constexpr (format == ieee754_format::binary32) { + return wuint::umul96_upper32(u, cache); + } + else { + return wuint::umul192_upper64(u, cache); + } + } + + static std::uint32_t compute_delta(cache_entry_type const& cache, int beta_minus_1) noexcept + { + if constexpr (format == ieee754_format::binary32) { + return std::uint32_t(cache >> (cache_bits - 1 - beta_minus_1)); + } + else { + return std::uint32_t(cache.high() >> (carrier_bits - 1 - beta_minus_1)); + } + } + + static bool compute_mul_parity(carrier_uint two_f, cache_entry_type const& cache, int beta_minus_1) noexcept + { + assert(beta_minus_1 >= 1); + assert(beta_minus_1 < 64); + + if constexpr (format == ieee754_format::binary32) { + return ((wuint::umul96_lower64(two_f, cache) >> + (64 - beta_minus_1)) & 1) != 0; + } + else { + return ((wuint::umul192_middle64(two_f, cache) >> + (64 - beta_minus_1)) & 1) != 0; + } + } + + static carrier_uint compute_left_endpoint_for_shorter_interval_case( + cache_entry_type const& cache, int beta_minus_1) noexcept + { + if constexpr (format == ieee754_format::binary32) { + return carrier_uint( + (cache - (cache >> (significand_bits + 2))) >> + (cache_bits - significand_bits - 1 - beta_minus_1)); + } + else { + return (cache.high() - (cache.high() >> (significand_bits + 2))) >> + (carrier_bits - significand_bits - 1 - beta_minus_1); + } + } + + static carrier_uint compute_right_endpoint_for_shorter_interval_case( + cache_entry_type const& cache, int beta_minus_1) noexcept + { + if constexpr (format == ieee754_format::binary32) { + return carrier_uint( + (cache + (cache >> (significand_bits + 1))) >> + (cache_bits - significand_bits - 1 - beta_minus_1)); + } + else { + return (cache.high() + (cache.high() >> (significand_bits + 1))) >> + (carrier_bits - significand_bits - 1 - beta_minus_1); + } + } + + static carrier_uint compute_round_up_for_shorter_interval_case( + cache_entry_type const& cache, int beta_minus_1) noexcept + { + if constexpr (format == ieee754_format::binary32) { + return (carrier_uint(cache >> (cache_bits - significand_bits - 2 - beta_minus_1)) + 1) / 2; + } + else { + return ((cache.high() >> (carrier_bits - significand_bits - 2 - beta_minus_1)) + 1) / 2; + } + } + + static bool is_right_endpoint_integer_shorter_interval(int exponent) noexcept { + return exponent >= case_shorter_interval_right_endpoint_lower_threshold && + exponent <= case_shorter_interval_right_endpoint_upper_threshold; + } + + static bool is_left_endpoint_integer_shorter_interval(int exponent) noexcept { + return exponent >= case_shorter_interval_left_endpoint_lower_threshold && + exponent <= case_shorter_interval_left_endpoint_upper_threshold; + } + + enum class integer_check_case_id { + fc_pm_half, + fc + }; + template + static bool is_product_integer(carrier_uint two_f, int exponent, int minus_k) noexcept + { + // Case I: f = fc +- 1/2 + if constexpr (case_id == integer_check_case_id::fc_pm_half) + { + if (exponent < case_fc_pm_half_lower_threshold) { + return false; + } + // For k >= 0 + else if (exponent <= case_fc_pm_half_upper_threshold) { + return true; + } + // For k < 0 + else if (exponent > divisibility_check_by_5_threshold) { + return false; + } + else { + return div::divisible_by_power_of_5(two_f, minus_k); + } + } + // Case II: f = fc + 1 + // Case III: f = fc + else + { + // Exponent for 5 is negative + if (exponent > divisibility_check_by_5_threshold) { + return false; + } + else if (exponent > case_fc_upper_threshold) { + return div::divisible_by_power_of_5(two_f, minus_k); + } + // Both exponents are nonnegative + else if (exponent >= case_fc_lower_threshold) { + return true; + } + // Exponent for 2 is negative + else { + return div::divisible_by_power_of_2(two_f, minus_k - exponent + 1); + } + } + } + }; + + + //////////////////////////////////////////////////////////////////////////////////////// + // Policy holder + //////////////////////////////////////////////////////////////////////////////////////// + + namespace policy_impl { + // The library will specify a list of accepted kinds of policies and their defaults, + // and the user will pass a list of policies. The aim of helper classes/functions here + // is to do the following: + // 1. Check if the policy parameters given by the user are all valid; that means, + // each of them should be of the kinds specified by the library. + // If that's not the case, then the compilation fails. + // 2. Check if multiple policy parameters for the same kind is specified by the user. + // If that's the case, then the compilation fails. + // 3. Build a class deriving from all policies the user have given, and also from + // the default policies if the user did not specify one for some kinds. + // A policy belongs to a certain kind if it is deriving from a base class. + + // For a given kind, find a policy belonging to that kind. + // Check if there are more than one such policies. + enum class policy_found_info { + not_found, unique, repeated + }; + template + struct found_policy_pair { + using policy = Policy; + static constexpr auto found_info = info; + }; + + template + struct base_default_pair { + using base = Base; + + template + static constexpr FoundPolicyInfo get_policy_impl(FoundPolicyInfo) { + return{}; + } + template + static constexpr auto get_policy_impl(FoundPolicyInfo, FirstPolicy, RemainingPolicies... remainings) { + if constexpr (std::is_base_of_v) { + if constexpr (FoundPolicyInfo::found_info == policy_found_info::not_found) { + return get_policy_impl( + found_policy_pair{}, + remainings...); + } + else { + return get_policy_impl( + found_policy_pair{}, + remainings...); + } + } + else { + return get_policy_impl(FoundPolicyInfo{}, + remainings...); + } + } + + template + static constexpr auto get_policy(Policies... policies) { + return get_policy_impl( + found_policy_pair{}, + policies...); + } + }; + template + struct base_default_pair_list {}; + + // Check if a given policy belongs to one of the kinds specified by the library + template + constexpr bool check_policy_validity(Policy, base_default_pair_list<>) + { + return false; + } + template + constexpr bool check_policy_validity(Policy, + base_default_pair_list) + { + return std::is_base_of_v || + check_policy_validity(Policy{}, base_default_pair_list< RemainingBaseDefaultPairs...>{}); + } + + template + constexpr bool check_policy_list_validity(BaseDefaultPairList) { + return true; + } + + template + constexpr bool check_policy_list_validity(BaseDefaultPairList, + FirstPolicy, RemainingPolicies... remaining_policies) + { + return check_policy_validity(FirstPolicy{}, BaseDefaultPairList{}) && + check_policy_list_validity(BaseDefaultPairList{}, remaining_policies...); + } + + // Build policy_holder + template + struct found_policy_pair_list { + static constexpr bool repeated = repeated_; + }; + + template + struct policy_holder : Policies... {}; + + template + constexpr auto make_policy_holder_impl( + base_default_pair_list<>, + found_policy_pair_list, + Policies...) + { + return found_policy_pair_list{}; + } + + template + constexpr auto make_policy_holder_impl( + base_default_pair_list, + found_policy_pair_list, + Policies... policies) + { + using new_found_policy_pair = decltype(FirstBaseDefaultPair::get_policy(policies...)); + + return make_policy_holder_impl( + base_default_pair_list{}, + found_policy_pair_list< + repeated || new_found_policy_pair::found_info == policy_found_info::repeated, + new_found_policy_pair, FoundPolicyPairs... + >{}, policies...); + } + + template + constexpr auto convert_to_policy_holder(found_policy_pair_list, RawPolicies...) { + return policy_holder{}; + } + + template + constexpr auto convert_to_policy_holder( + found_policy_pair_list, RawPolicies... policies) + { + return convert_to_policy_holder(found_policy_pair_list{}, + typename FirstFoundPolicyPair::policy{}, policies...); + } + + template + constexpr auto make_policy_holder(BaseDefaultPairList, Policies... policies) { + static_assert(check_policy_list_validity(BaseDefaultPairList{}, Policies{}...), + "jkj::dragonbox: an invalid policy is specified"); + + using policy_pair_list = decltype(make_policy_holder_impl(BaseDefaultPairList{}, + found_policy_pair_list{}, policies...)); + + static_assert(!policy_pair_list::repeated, + "jkj::dragonbox: each policy should be specified at most once"); + + return convert_to_policy_holder(policy_pair_list{}); + } + } + } + + + //////////////////////////////////////////////////////////////////////////////////////// + // The interface function + //////////////////////////////////////////////////////////////////////////////////////// + + template + JKJ_SAFEBUFFERS JKJ_FORCEINLINE auto to_decimal(Float x, Policies... policies) + { + // Build policy holder type + using namespace detail::policy_impl; + using policy_holder = decltype(make_policy_holder( + base_default_pair_list< + base_default_pair, + base_default_pair, + base_default_pair, + base_default_pair, + base_default_pair, + base_default_pair + >{}, policies...)); + + using return_type = fp_t; + + auto br = ieee754_bits(x); + policy_holder::validate_input(br); + + return policy_holder::delegate(br, + [br](auto interval_type_provider) { + constexpr auto tag = decltype(interval_type_provider)::tag; + + if constexpr (tag == rounding_mode::tag_t::to_nearest) { + return detail::impl::template + compute_nearest(br); + } + else if constexpr (tag == rounding_mode::tag_t::left_closed_directed) { + return detail::impl::template + compute_left_closed_directed(br); + } + else { + return detail::impl::template + compute_right_closed_directed(br); + } + }); + } +} + +#undef JKJ_HAS_COUNTR_ZERO_INTRINSIC +#undef JKJ_FORCEINLINE +#undef JKJ_SAFEBUFFERS + +#endif + diff --git a/lib/vendor/dragonbox/include/dragonbox/dragonbox_to_chars.h b/lib/vendor/dragonbox/include/dragonbox/dragonbox_to_chars.h new file mode 100644 index 000000000000..15533aa01728 --- /dev/null +++ b/lib/vendor/dragonbox/include/dragonbox/dragonbox_to_chars.h @@ -0,0 +1,95 @@ +// Copyright 2020 Junekey Jeon +// +// The contents of this file may be used under the terms of +// the Apache License v2.0 with LLVM Exceptions. +// +// (See accompanying file LICENSE-Apache or copy at +// https://llvm.org/foundation/relicensing/LICENSE.txt) +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE-Boost or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. + +#ifndef JKJ_DRAGONBOX_TO_CHARS +#define JKJ_DRAGONBOX_TO_CHARS + +#include "dragonbox/dragonbox.h" + +namespace jkj::dragonbox { + namespace to_chars_detail { + char* to_chars(unsigned_fp_t v, char* buffer); + char* to_chars(unsigned_fp_t v, char* buffer); + } + + // Returns the next-to-end position + template + char* to_chars_n(Float x, char* buffer, Policies... policies) + { + using namespace jkj::dragonbox::detail::policy_impl; + using policy_holder = decltype(make_policy_holder( + base_default_pair_list< + base_default_pair, + base_default_pair, + base_default_pair, + base_default_pair + >{}, policies...)); + + static_assert(!policy_holder::report_trailing_zeros, + "jkj::dragonbox::policy::trailing_zeros::report is not valid for to_chars & to_chars_n"); + + using ieee754_format_info = ieee754_format_info::format>; + + auto br = ieee754_bits(x); + if (br.is_finite()) { + if (br.is_negative()) { + *buffer = '-'; + ++buffer; + } + if (br.is_nonzero()) { + return to_chars_detail::to_chars(to_decimal(x, + policy::sign::ignore, + typename policy_holder::trailing_zero_policy{}, + typename policy_holder::rounding_mode_policy{}, + typename policy_holder::correct_rounding_policy{}, + typename policy_holder::cache_policy{}), + buffer); + } + else { + std::memcpy(buffer, "0E0", 3); + return buffer + 3; + } + } + else { + if ((br.u << (ieee754_format_info::exponent_bits + 1)) != 0) + { + std::memcpy(buffer, "NaN", 3); + return buffer + 3; + } + else { + if (br.is_negative()) { + *buffer = '-'; + ++buffer; + } + std::memcpy(buffer, "Infinity", 8); + return buffer + 8; + } + } + } + + // Null-terminate and bypass the return value of fp_to_chars_n + template + char* to_chars(Float x, char* buffer, Policies... policies) + { + auto ptr = to_chars_n(x, buffer, policies...); + *ptr = '\0'; + return ptr; + } +} + +#endif + diff --git a/lib/vendor/readme.md b/lib/vendor/readme.md new file mode 100644 index 000000000000..f44eba297059 --- /dev/null +++ b/lib/vendor/readme.md @@ -0,0 +1,5 @@ +# third party files go here + +difference with `$nim/dist`: +`$nim/dist` is for automatically imported third party files +this directory is for third party files we check in this repo diff --git a/tests/benchmarks/tstrfloats_bench.nim b/tests/benchmarks/tstrfloats_bench.nim new file mode 100644 index 000000000000..5a73f5e0ab45 --- /dev/null +++ b/tests/benchmarks/tstrfloats_bench.nim @@ -0,0 +1,77 @@ +#[ +on OSX: +nim r -d:danger -d:numIter:100_000_00 tests/benchmarks/tstrfloats_bench.nim + +toStringSprintf genFloatCast 11.648684 +toStringSprintf genFloatConv 1.5726399999999998 +toStringSprintf genFloat32Cast 4.996139999999999 +toStringSprintf genFloat32Conv 1.6002259999999993 +toStringDragonbox genFloatCast 0.18426399999999887 +toStringDragonbox genFloatConv 0.1614930000000001 +toStringDragonbox genFloat32Cast 0.1921079999999975 +toStringDragonbox genFloat32Conv 0.12435800000000086 +toStringSchubfach genFloatCast 0.254999999999999 +toStringSchubfach genFloatConv 0.07917600000000036 +toStringSchubfach genFloat32Cast 0.20187899999999814 +toStringSchubfach genFloat32Conv 0.13615599999999972 +toStringDragonbox0 genFloatCast 0.2024349999999977 +toStringDragonbox0 genFloatConv 0.21805200000000013 +toStringDragonbox0 genFloat32Cast 0.1467919999999978 +toStringDragonbox0 genFloat32Conv 0.14451999999999998 +]# + +import std/[times, strfloats, strutils, math, random] +import std/private/asciitables +const numIter {.intdefine.} = 10 + +var msg = "" + +template gen(algo, genFloat) = + proc main {.gensym.} = + var buf: array[strFloatBufLen, char] + var c = 0 + let t = cpuTime() + for i in 0.. json => T2 => json2` is such that json2 = json let j = %t doAssert $j == expected, $j doAssert %(j.to(T)) == j +proc testRoundtripVal[T](t: T, expected: string) = + # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` + # note that this isn't always possible, e.g. for pointer-like types. + let j = %t + doAssert $j == expected, $j + let j2 = ($j).parseJson + doAssert $j2 == expected, $(j2, t) + let t2 = j2.to(T) + doAssert t2 == t + doAssert $(%* t2) == expected # sanity check, because -0.0 = 0.0 but their json representation differs + let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}""" # nil passthrough doAssert(testJson{"doesnt_exist"}{"anything"}.isNil) @@ -301,6 +315,20 @@ block: # bug #17383 testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(uint64.high): "18446744073709551615" +block: # bug #15397, bug #13196 + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + +block: + whenRuntimeJs: discard # pending https://github.com/nim-lang/Nim/issues/18009 + do: + testRoundtripVal(0.0): "0.0" + testRoundtripVal(-0.0): "-0.0" + +when false: # xxx pending https://github.com/nim-lang/Nim/issues/18007: + testRoundtripVal(Inf): "inf" + testRoundtripVal(-Inf): "-inf" + testRoundtripVal(NaN): "nan" block: let a = "18446744073709551615" diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 18d0aa325fb0..1250e7b40fcd 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -4,8 +4,10 @@ discard """ import std/jsonutils import std/json +from std/fenv import epsilon proc testRoundtrip[T](t: T, expected: string) = + # checks that `T => json => T2 => json2` is such that json2 = json let j = t.toJson doAssert $j == expected, $j doAssert j.jsonTo(T).toJson == j @@ -13,6 +15,17 @@ proc testRoundtrip[T](t: T, expected: string) = t2.fromJson(j) doAssert t2.toJson == j +proc testRoundtripVal[T](t: T, expected: string) = + # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` + # note that this isn't always possible, e.g. for pointer-like types. + let j = t.toJson + let j2 = $j + doAssert j2 == expected, j2 + let j3 = j2.parseJson + let t2 = j3.jsonTo(T) + doAssert t2 == t # xxx handle NaN specially, using isNaN once https://github.com/nim-lang/Nim/issues/18007 is fixed + doAssert $t2.toJson == j2 # still needed, because -0.0 = 0.0 but their json representation differs + import tables, sets, algorithm, sequtils, options, strtabs from strutils import contains @@ -91,6 +104,25 @@ template fn() = else: testRoundtrip(a): "[9223372036854775807,18446744073709551615]" + block: # bug #15397, bug #13196 + let a = 0.1 + let x = 0.12345678901234567890123456789 + let b = (a + 0.2, 0.3, x) + testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" + + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + + block: + testRoundtripVal(0.0): "0.0" + testRoundtripVal(-0.0): "-0.0" + + when false: # xxx pending https://github.com/nim-lang/Nim/issues/18007: + testRoundtripVal(Inf): "inf" + testRoundtripVal(-Inf): "-inf" + testRoundtripVal(NaN): "nan" + block: # case object type Foo = object x0: float diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 905e5143aaa4..e6c5d970058b 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,6 +1,8 @@ discard """ output: ''' +[Suite] RST parsing + [Suite] RST indentation [Suite] RST include directive @@ -55,6 +57,30 @@ proc toAst(input: string, except EParseError: discard +suite "RST parsing": + test "option list has priority over definition list": + check(dedent""" + --defusages + file + -o set + """.toAst == + dedent""" + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '--' + rnLeaf 'defusages' + rnDescription + rnInner + rnLeaf 'file' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'o' + rnDescription + rnLeaf 'set' + """) + suite "RST indentation": test "nested bullet lists": let input = dedent """ diff --git a/tests/stdlib/tstrfloats.nim b/tests/stdlib/tstrfloats.nim new file mode 100644 index 000000000000..95b51d39310e --- /dev/null +++ b/tests/stdlib/tstrfloats.nim @@ -0,0 +1,80 @@ +discard """ + targets: "c cpp js" +""" + +#[ +xxx deduplicate with tdollars, tests/float/tfloat4.nim, tests/float/tfloat6.nim etc +]# + +import stdtest/testutils + +from std/math import PI +from std/fenv import epsilon + +template main = + var res = newStringOfCap(24) + template toStr(x): untyped = + let x2 = x # prevents const folding + res.setLen(0) + res.addFloat x2 + doAssert res == $x2 # sanity check + res + + block: # addFloat + var s = "prefix" + s.addFloat(0.1) + assertAll: + s == "prefix0.1" + 0.0.toStr == "0.0" + 1.0.toStr == "1.0" + -1.0.toStr == "-1.0" + 0.3.toStr == "0.3" + + 0.1 + 0.2 != 0.3 + 0.1 + 0.2 == 0.30000000000000004 + toStr(0.1 + 0.2) == "0.30000000000000004" # maybe const-folding here + let a = 0.1 + toStr(a + 0.2) == "0.30000000000000004" # no const-folding here + + toStr(-0.0) == "-0.0" + toStr(1000000000000000.0) == "1000000000000000.0" + toStr(PI) == "3.141592653589793" + toStr(1.23e-8) == "1.23e-8" + toStr(10.23e-9) == "1.023e-8" + toStr(-10.23e-9) == "-1.023e-8" + toStr(5e-324) == "5e-324" + toStr(50e22) == "5e+23" + toStr(5e22) == "5e+22" + toStr(-5e22) == "-5e+22" + toStr(50e20) == "5e+21" + toStr(1.0 + epsilon(float64)) == "1.0000000000000002" + + block: # nan, inf + cases that differ in js RT + whenRuntimeJs: + assertAll: + toStr(NaN) == "NaN" + toStr(Inf) == "Infinity" + toStr(1.0 / 0.0) == "Infinity" + toStr(-1.0 / 0.0) == "-Infinity" + toStr(50e18) == "50000000000000000000.0" + do: + assertAll: + toStr(NaN) == "nan" + toStr(Inf) == "inf" + toStr(1.0 / 0.0) == "inf" + toStr(-1.0 / 0.0) == "-inf" + toStr(50e18) == "5e+19" + + block: + let a = 123456789 + when not defined(js): + # xxx in VM, gives: Error: VM does not support 'cast' from tyInt to tyFloat + let b = cast[float](a) + assertAll: + # xxx in js RT, this shows 123456789.0, ie, the cast is interpreted as a conversion + toStr(b) == "6.0995758e-316" # nim 1.4 would produce 6.09957581907715e-316 + b == 6.0995758e-316 + cast[int](b) == a + +static: main() +main() diff --git a/tests/system/tstrmantle.nim b/tests/system/tstrmantle.nim index 1f195adde1f1..1d3bc098033c 100644 --- a/tests/system/tstrmantle.nim +++ b/tests/system/tstrmantle.nim @@ -1,46 +1,41 @@ -var res = newStringOfCap(24) - -for i in 0 .. 9: - res.addInt int64(i) - -doAssert res == "0123456789" - -res.setLen(0) - -for i in -9 .. 0: - res.addInt int64(i) - -doAssert res == "-9-8-7-6-5-4-3-2-10" - -res.setLen(0) -res.addInt high(int64) -doAssert res == "9223372036854775807" - -res.setLen(0) -res.addInt low(int64) -doAssert res == "-9223372036854775808" - -res.setLen(0) -res.addInt high(int32) -doAssert res == "2147483647" - -res.setLen(0) -res.addInt low(int32) -doAssert res == "-2147483648" - -res.setLen(0) -res.addInt high(int16) -doAssert res == "32767" - -res.setLen(0) -res.addInt low(int16) -doAssert res == "-32768" - - -res.setLen(0) -res.addInt high(int8) -doAssert res == "127" - -res.setLen(0) -res.addInt low(int8) -doAssert res == "-128" +discard """ + targets: "c cpp js" +""" + +import stdtest/testutils + +template main = + var res = newStringOfCap(24) + template toStr(x): untyped = + res.setLen(0) + res.addInt x + doAssert res == $x # sanity check + res + + block: # addInt + for i in 0 .. 9: + res.addInt int64(i) + + doAssert res == "0123456789" + res.setLen(0) + for i in -9 .. 0: + res.addInt int64(i) + doAssert res == "-9-8-7-6-5-4-3-2-10" + + assertAll: + 0.toStr == "0" + (-0).toStr == "0" + high(int8).toStr == "127" + low(int8).toStr == "-128" + high(int16).toStr == "32767" + low(int16).toStr == "-32768" + high(int32).toStr == "2147483647" + low(int32).toStr == "-2147483648" + + when not defined(js): + assertAll: + high(int64).toStr == "9223372036854775807" + low(int64).toStr == "-9223372036854775808" + +static: main() +main()