schubfach.nim 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. ## Copyright 2020 Alexander Bolz
  2. ##
  3. ## Distributed under the Boost Software License, Version 1.0.
  4. ## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
  5. # --------------------------------------------------------------------------------------------------
  6. ## This file contains an implementation of the Schubfach algorithm as described in
  7. ##
  8. ## \[1] Raffaello Giulietti, "The Schubfach way to render doubles",
  9. ## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN
  10. # --------------------------------------------------------------------------------------------------
  11. import std/private/digitsutils
  12. when defined(nimPreviewSlimSystem):
  13. import std/assertions
  14. template sf_Assert(x: untyped): untyped =
  15. assert(x)
  16. # ==================================================================================================
  17. #
  18. # ==================================================================================================
  19. type
  20. ValueType = float32
  21. BitsType = uint32
  22. Single {.bycopy.} = object
  23. bits: BitsType
  24. const
  25. significandSize: int32 = 24
  26. MaxExponent = 128
  27. exponentBias: int32 = MaxExponent - 1 + (significandSize - 1)
  28. maxIeeeExponent: BitsType = BitsType(2 * MaxExponent - 1)
  29. hiddenBit: BitsType = BitsType(1) shl (significandSize - 1)
  30. significandMask: BitsType = hiddenBit - 1
  31. exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1)
  32. signMask: BitsType = not (not BitsType(0) shr 1)
  33. proc constructSingle(bits: BitsType): Single =
  34. result.bits = bits
  35. proc constructSingle(value: ValueType): Single =
  36. result.bits = cast[typeof(result.bits)](value)
  37. proc physicalSignificand(this: Single): BitsType {.noSideEffect.} =
  38. return this.bits and significandMask
  39. proc physicalExponent(this: Single): BitsType {.noSideEffect.} =
  40. return (this.bits and exponentMask) shr (significandSize - 1)
  41. proc isFinite(this: Single): bool {.noSideEffect.} =
  42. return (this.bits and exponentMask) != exponentMask
  43. proc isInf(this: Single): bool {.noSideEffect.} =
  44. return (this.bits and exponentMask) == exponentMask and
  45. (this.bits and significandMask) == 0
  46. proc isNaN(this: Single): bool {.noSideEffect.} =
  47. return (this.bits and exponentMask) == exponentMask and
  48. (this.bits and significandMask) != 0
  49. proc isZero(this: Single): bool {.noSideEffect.} =
  50. return (this.bits and not signMask) == 0
  51. proc signBit(this: Single): int {.noSideEffect.} =
  52. return int((this.bits and signMask) != 0)
  53. # ==================================================================================================
  54. ## Returns floor(x / 2^n).
  55. ##
  56. ## Technically, right-shift of negative integers is implementation defined...
  57. ## Should easily be optimized into SAR (or equivalent) instruction.
  58. proc floorDivPow2(x: int32; n: int32): int32 {.inline.} =
  59. return x shr n
  60. ## Returns floor(log_10(2^e))
  61. ## ```c
  62. ## static inline int32_t FloorLog10Pow2(int32_t e)
  63. ## {
  64. ## SF_ASSERT(e >= -1500);
  65. ## SF_ASSERT(e <= 1500);
  66. ## return FloorDivPow2(e * 1262611, 22);
  67. ## }
  68. ## ```
  69. ## Returns floor(log_10(3/4 2^e))
  70. ## ```c
  71. ## static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e)
  72. ## {
  73. ## SF_ASSERT(e >= -1500);
  74. ## SF_ASSERT(e <= 1500);
  75. ## return FloorDivPow2(e * 1262611 - 524031, 22);
  76. ## }
  77. ## ```
  78. ## Returns floor(log_2(10^e))
  79. proc floorLog2Pow10(e: int32): int32 {.inline.} =
  80. sf_Assert(e >= -1233)
  81. sf_Assert(e <= 1233)
  82. return floorDivPow2(e * 1741647, 19)
  83. const
  84. kMin: int32 = -31
  85. kMax: int32 = 45
  86. g: array[kMax - kMin + 1, uint64] = [0x81CEB32C4B43FCF5'u64, 0xA2425FF75E14FC32'u64,
  87. 0xCAD2F7F5359A3B3F'u64, 0xFD87B5F28300CA0E'u64, 0x9E74D1B791E07E49'u64,
  88. 0xC612062576589DDB'u64, 0xF79687AED3EEC552'u64, 0x9ABE14CD44753B53'u64,
  89. 0xC16D9A0095928A28'u64, 0xF1C90080BAF72CB2'u64, 0x971DA05074DA7BEF'u64,
  90. 0xBCE5086492111AEB'u64, 0xEC1E4A7DB69561A6'u64, 0x9392EE8E921D5D08'u64,
  91. 0xB877AA3236A4B44A'u64, 0xE69594BEC44DE15C'u64, 0x901D7CF73AB0ACDA'u64,
  92. 0xB424DC35095CD810'u64, 0xE12E13424BB40E14'u64, 0x8CBCCC096F5088CC'u64,
  93. 0xAFEBFF0BCB24AAFF'u64, 0xDBE6FECEBDEDD5BF'u64, 0x89705F4136B4A598'u64,
  94. 0xABCC77118461CEFD'u64, 0xD6BF94D5E57A42BD'u64, 0x8637BD05AF6C69B6'u64,
  95. 0xA7C5AC471B478424'u64, 0xD1B71758E219652C'u64, 0x83126E978D4FDF3C'u64,
  96. 0xA3D70A3D70A3D70B'u64, 0xCCCCCCCCCCCCCCCD'u64, 0x8000000000000000'u64,
  97. 0xA000000000000000'u64, 0xC800000000000000'u64, 0xFA00000000000000'u64,
  98. 0x9C40000000000000'u64, 0xC350000000000000'u64, 0xF424000000000000'u64,
  99. 0x9896800000000000'u64, 0xBEBC200000000000'u64, 0xEE6B280000000000'u64,
  100. 0x9502F90000000000'u64, 0xBA43B74000000000'u64, 0xE8D4A51000000000'u64,
  101. 0x9184E72A00000000'u64, 0xB5E620F480000000'u64, 0xE35FA931A0000000'u64,
  102. 0x8E1BC9BF04000000'u64, 0xB1A2BC2EC5000000'u64, 0xDE0B6B3A76400000'u64,
  103. 0x8AC7230489E80000'u64, 0xAD78EBC5AC620000'u64, 0xD8D726B7177A8000'u64,
  104. 0x878678326EAC9000'u64, 0xA968163F0A57B400'u64, 0xD3C21BCECCEDA100'u64,
  105. 0x84595161401484A0'u64, 0xA56FA5B99019A5C8'u64, 0xCECB8F27F4200F3A'u64,
  106. 0x813F3978F8940985'u64, 0xA18F07D736B90BE6'u64, 0xC9F2C9CD04674EDF'u64,
  107. 0xFC6F7C4045812297'u64, 0x9DC5ADA82B70B59E'u64, 0xC5371912364CE306'u64,
  108. 0xF684DF56C3E01BC7'u64, 0x9A130B963A6C115D'u64, 0xC097CE7BC90715B4'u64,
  109. 0xF0BDC21ABB48DB21'u64, 0x96769950B50D88F5'u64, 0xBC143FA4E250EB32'u64,
  110. 0xEB194F8E1AE525FE'u64, 0x92EFD1B8D0CF37BF'u64, 0xB7ABC627050305AE'u64,
  111. 0xE596B7B0C643C71A'u64, 0x8F7E32CE7BEA5C70'u64, 0xB35DBF821AE4F38C'u64]
  112. proc computePow10Single(k: int32): uint64 {.inline.} =
  113. ## There are unique beta and r such that 10^k = beta 2^r and
  114. ## 2^63 <= beta < 2^64, namely r = floor(log_2 10^k) - 63 and
  115. ## beta = 2^-r 10^k.
  116. ## Let g = ceil(beta), so (g-1) 2^r < 10^k <= g 2^r, with the latter
  117. ## value being a pretty good overestimate for 10^k.
  118. ## NB: Since for all the required exponents k, we have g < 2^64,
  119. ## all constants can be stored in 128-bit integers.
  120. sf_Assert(k >= kMin)
  121. sf_Assert(k <= kMax)
  122. return g[k - kMin]
  123. proc lo32(x: uint64): uint32 {.inline.} =
  124. return cast[uint32](x)
  125. proc hi32(x: uint64): uint32 {.inline.} =
  126. return cast[uint32](x shr 32)
  127. when defined(sizeof_Int128):
  128. proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} =
  129. let p: uint128 = uint128(g) * cp
  130. let y1: uint32 = lo32(cast[uint64](p shr 64))
  131. let y0: uint32 = hi32(cast[uint64](p))
  132. return y1 or uint32(y0 > 1)
  133. elif defined(vcc) and defined(cpu64):
  134. proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "<intrin.h>".}
  135. proc roundToOdd(g: uint64; cpHi: uint32): uint32 {.inline.} =
  136. var p1: uint64 = 0
  137. var p0: uint64 = umul128(g, cpHi, addr(p1))
  138. let y1: uint32 = lo32(p1)
  139. let y0: uint32 = hi32(p0)
  140. return y1 or uint32(y0 > 1)
  141. else:
  142. proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} =
  143. let b01: uint64 = uint64(lo32(g)) * cp
  144. let b11: uint64 = uint64(hi32(g)) * cp
  145. let hi: uint64 = b11 + hi32(b01)
  146. let y1: uint32 = hi32(hi)
  147. let y0: uint32 = lo32(hi)
  148. return y1 or uint32(y0 > 1)
  149. ## Returns whether value is divisible by 2^e2
  150. proc multipleOfPow2(value: uint32; e2: int32): bool {.inline.} =
  151. sf_Assert(e2 >= 0)
  152. sf_Assert(e2 <= 31)
  153. return (value and ((uint32(1) shl e2) - 1)) == 0
  154. type
  155. FloatingDecimal32 {.bycopy.} = object
  156. digits: uint32 ## num_digits <= 9
  157. exponent: int32
  158. proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal32 {.
  159. inline.} =
  160. var c: uint32
  161. var q: int32
  162. if ieeeExponent != 0:
  163. c = hiddenBit or ieeeSignificand
  164. q = cast[int32](ieeeExponent) - exponentBias
  165. if 0 <= -q and -q < significandSize and multipleOfPow2(c, -q):
  166. return FloatingDecimal32(digits: c shr -q, exponent: 0'i32)
  167. else:
  168. c = ieeeSignificand
  169. q = 1 - exponentBias
  170. let isEven: bool = (c mod 2 == 0)
  171. let lowerBoundaryIsCloser: bool = (ieeeSignificand == 0 and ieeeExponent > 1)
  172. ## const int32_t qb = q - 2;
  173. let cbl: uint32 = 4 * c - 2 + uint32(lowerBoundaryIsCloser)
  174. let cb: uint32 = 4 * c
  175. let cbr: uint32 = 4 * c + 2
  176. ## (q * 1262611 ) >> 22 == floor(log_10( 2^q))
  177. ## (q * 1262611 - 524031) >> 22 == floor(log_10(3/4 2^q))
  178. sf_Assert(q >= -1500)
  179. sf_Assert(q <= 1500)
  180. let k: int32 = floorDivPow2(q * 1262611 - (if lowerBoundaryIsCloser: 524031 else: 0), 22)
  181. let h: int32 = q + floorLog2Pow10(-k) + 1
  182. sf_Assert(h >= 1)
  183. sf_Assert(h <= 4)
  184. let pow10: uint64 = computePow10Single(-k)
  185. let vbl: uint32 = roundToOdd(pow10, cbl shl h)
  186. let vb: uint32 = roundToOdd(pow10, cb shl h)
  187. let vbr: uint32 = roundToOdd(pow10, cbr shl h)
  188. let lower: uint32 = vbl + uint32(not isEven)
  189. let upper: uint32 = vbr - uint32(not isEven)
  190. ## See Figure 4 in [1].
  191. ## And the modifications in Figure 6.
  192. let s: uint32 = vb div 4
  193. ## NB: 4 * s == vb & ~3 == vb & -4
  194. if s >= 10:
  195. let sp: uint32 = s div 10
  196. ## = vb / 40
  197. let upInside: bool = lower <= 40 * sp
  198. let wpInside: bool = 40 * sp + 40 <= upper
  199. ## if (up_inside || wp_inside) // NB: At most one of u' and w' is in R_v.
  200. if upInside != wpInside:
  201. return FloatingDecimal32(digits: sp + uint32(wpInside), exponent: k + 1)
  202. let uInside: bool = lower <= 4 * s
  203. let wInside: bool = 4 * s + 4 <= upper
  204. if uInside != wInside:
  205. return FloatingDecimal32(digits: s + uint32(wInside), exponent: k)
  206. let mid: uint32 = 4 * s + 2
  207. ## = 2(s + t)
  208. let roundUp: bool = vb > mid or (vb == mid and (s and 1) != 0)
  209. return FloatingDecimal32(digits: s + uint32(roundUp), exponent: k)
  210. ## ==================================================================================================
  211. ## ToChars
  212. ## ==================================================================================================
  213. proc printDecimalDigitsBackwards[T: Ordinal](buf: var openArray[char]; pos: T; output: uint32): int {.inline.} =
  214. var output = output
  215. var pos = pos
  216. var tz = 0
  217. ## number of trailing zeros removed.
  218. var nd = 0
  219. ## number of decimal digits processed.
  220. ## At most 9 digits remaining
  221. if output >= 10000:
  222. let q: uint32 = output div 10000
  223. let r: uint32 = output mod 10000
  224. output = q
  225. dec(pos, 4)
  226. if r != 0:
  227. let rH: uint32 = r div 100
  228. let rL: uint32 = r mod 100
  229. utoa2Digits(buf, pos, rH)
  230. utoa2Digits(buf, pos + 2, rL)
  231. tz = trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0)
  232. else:
  233. tz = 4
  234. nd = 4
  235. if output >= 100:
  236. let q: uint32 = output div 100
  237. let r: uint32 = output mod 100
  238. output = q
  239. dec(pos, 2)
  240. utoa2Digits(buf, pos, r)
  241. if tz == nd:
  242. inc(tz, trailingZeros2Digits(r))
  243. inc(nd, 2)
  244. if output >= 100:
  245. let q2: uint32 = output div 100
  246. let r2: uint32 = output mod 100
  247. output = q2
  248. dec(pos, 2)
  249. utoa2Digits(buf, pos, r2)
  250. if tz == nd:
  251. inc(tz, trailingZeros2Digits(r2))
  252. inc(nd, 2)
  253. sf_Assert(output >= 1)
  254. sf_Assert(output <= 99)
  255. if output >= 10:
  256. let q: uint32 = output
  257. dec(pos, 2)
  258. utoa2Digits(buf, pos, q)
  259. if tz == nd:
  260. inc(tz, trailingZeros2Digits(q))
  261. else:
  262. let q: uint32 = output
  263. sf_Assert(q >= 1)
  264. sf_Assert(q <= 9)
  265. dec(pos)
  266. buf[pos] = chr(uint32('0') + q)
  267. return tz
  268. proc decimalLength(v: uint32): int {.inline.} =
  269. sf_Assert(v >= 1)
  270. sf_Assert(v <= 999999999'u)
  271. if v >= 100000000'u:
  272. return 9
  273. if v >= 10000000'u:
  274. return 8
  275. if v >= 1000000'u:
  276. return 7
  277. if v >= 100000'u:
  278. return 6
  279. if v >= 10000'u:
  280. return 5
  281. if v >= 1000'u:
  282. return 4
  283. if v >= 100'u:
  284. return 3
  285. if v >= 10'u:
  286. return 2
  287. return 1
  288. proc formatDigits[T: Ordinal](buffer: var openArray[char]; pos: T; digits: uint32; decimalExponent: int;
  289. forceTrailingDotZero: bool = false): int {.inline.} =
  290. const
  291. minFixedDecimalPoint: int32 = -4
  292. maxFixedDecimalPoint: int32 = 9
  293. var pos = pos
  294. assert(minFixedDecimalPoint <= -1, "internal error")
  295. assert(maxFixedDecimalPoint >= 1, "internal error")
  296. sf_Assert(digits >= 1)
  297. sf_Assert(digits <= 999999999'u)
  298. sf_Assert(decimalExponent >= -99)
  299. sf_Assert(decimalExponent <= 99)
  300. var numDigits = decimalLength(digits)
  301. let decimalPoint = numDigits + decimalExponent
  302. let useFixed: bool = minFixedDecimalPoint <= decimalPoint and
  303. decimalPoint <= maxFixedDecimalPoint
  304. ## Prepare the buffer.
  305. ## Avoid calling memset/memcpy with variable arguments below...
  306. for i in 0..<32: buffer[pos+i] = '0'
  307. assert(minFixedDecimalPoint >= -30, "internal error")
  308. assert(maxFixedDecimalPoint <= 32, "internal error")
  309. var decimalDigitsPosition: int
  310. if useFixed:
  311. if decimalPoint <= 0:
  312. ## 0.[000]digits
  313. decimalDigitsPosition = 2 - decimalPoint
  314. else:
  315. ## dig.its
  316. ## digits[000]
  317. decimalDigitsPosition = 0
  318. else:
  319. ## dE+123 or d.igitsE+123
  320. decimalDigitsPosition = 1
  321. var digitsEnd = pos + decimalDigitsPosition + numDigits
  322. let tz = printDecimalDigitsBackwards(buffer, digitsEnd, digits)
  323. dec(digitsEnd, tz)
  324. dec(numDigits, tz)
  325. ## decimal_exponent += tz; // => decimal_point unchanged.
  326. if useFixed:
  327. if decimalPoint <= 0:
  328. ## 0.[000]digits
  329. buffer[pos+1] = '.'
  330. pos = digitsEnd
  331. elif decimalPoint < numDigits:
  332. ## dig.its
  333. for i in countdown(7, 0):
  334. buffer[i + decimalPoint + 1] = buffer[i + decimalPoint]
  335. buffer[pos+decimalPoint] = '.'
  336. pos = digitsEnd + 1
  337. else:
  338. ## digits[000]
  339. inc(pos, decimalPoint)
  340. if forceTrailingDotZero:
  341. buffer[pos] = '.'
  342. buffer[pos+1] = '0'
  343. inc(pos, 2)
  344. else:
  345. buffer[pos] = buffer[pos+1]
  346. if numDigits == 1:
  347. ## dE+123
  348. inc(pos)
  349. else:
  350. ## d.igitsE+123
  351. buffer[pos+1] = '.'
  352. pos = digitsEnd
  353. let scientificExponent = decimalPoint - 1
  354. ## SF_ASSERT(scientific_exponent != 0);
  355. buffer[pos] = 'e'
  356. buffer[pos+1] = if scientificExponent < 0: '-' else: '+'
  357. inc(pos, 2)
  358. let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent)
  359. if k < 10:
  360. buffer[pos] = chr(uint32('0') + k)
  361. inc pos
  362. else:
  363. utoa2Digits(buffer, pos, k)
  364. inc(pos, 2)
  365. return pos
  366. proc float32ToChars*(buffer: var openArray[char]; v: float32; forceTrailingDotZero = false): int {.
  367. inline.} =
  368. let significand: uint32 = physicalSignificand(constructSingle(v))
  369. let exponent: uint32 = physicalExponent(constructSingle(v))
  370. var pos = 0
  371. if exponent != maxIeeeExponent:
  372. ## Finite
  373. buffer[pos] = '-'
  374. inc(pos, signBit(constructSingle(v)))
  375. if exponent != 0 or significand != 0:
  376. ## != 0
  377. let dec: auto = toDecimal32(significand, exponent)
  378. return formatDigits(buffer, pos, dec.digits, dec.exponent.int, forceTrailingDotZero)
  379. else:
  380. buffer[pos] = '0'
  381. buffer[pos+1] = '.'
  382. buffer[pos+2] = '0'
  383. buffer[pos+3] = ' '
  384. inc(pos, if forceTrailingDotZero: 3 else: 1)
  385. return pos
  386. if significand == 0:
  387. buffer[pos] = '-'
  388. inc(pos, signBit(constructSingle(v)))
  389. buffer[pos] = 'i'
  390. buffer[pos+1] = 'n'
  391. buffer[pos+2] = 'f'
  392. buffer[pos+3] = ' '
  393. return pos + 3
  394. else:
  395. buffer[pos] = 'n'
  396. buffer[pos+1] = 'a'
  397. buffer[pos+2] = 'n'
  398. buffer[pos+3] = ' '
  399. return pos + 3