├── src ├── fpconv.h ├── powers.h ├── fpconv.c └── fpconv_ctfe.d ├── README.md └── license /src/fpconv.h: -------------------------------------------------------------------------------- 1 | #ifndef FPCONV_H 2 | #define FPCONV_H 3 | 4 | /* Fast and accurate double to string conversion based on Florian Loitsch's 5 | * Grisu-algorithm[1]. 6 | * 7 | * Input: 8 | * fp -> the double to convert, dest -> destination buffer. 9 | * The generated string will never be longer than 24 characters. 10 | * Make sure to pass a pointer to at least 24 bytes of memory. 11 | * The emitted string will not be null terminated. 12 | * 13 | * Output: 14 | * The number of written characters. 15 | * 16 | * Exemplary usage: 17 | * 18 | * void print(double d) 19 | * { 20 | * char buf[24 + 1] // plus null terminator 21 | * int str_len = fpconv_dtoa(d, buf); 22 | * 23 | * buf[str_len] = '\0'; 24 | * printf("%s", buf); 25 | * } 26 | * 27 | */ 28 | 29 | int fpconv_dtoa(double fp, char dest[24]); 30 | 31 | #endif 32 | 33 | /* [1] http://florian.loitsch.com/publications/dtoa-pldi2010.pdf */ 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Minimalistic C / D implementation of Fabian Loitsch's Grisu-algorithm [[pdf]](http://florian.loitsch.com/publications/dtoa-pldi2010.pdf). 2 | Grisu converts floating point numbers to an optimal decimal string representation without loss of precision. 3 | 4 | ### C Api 5 | ```c 6 | int fpconv_dtoa(double fp, char dest[24]); 7 | ``` 8 | * Writes string representation of ```fp``` to ```dest``` and returns the number of written characters 9 | * The emitted string will never exceed 24 characters 10 | * Does not null terminate the string 11 | * Assumes ```fp``` is an IEEE 64-bit floating point number 12 | 13 | ### Example usage 14 | ```c 15 | void print(double d) 16 | { 17 | char buf[24 + 1]; /* reserve space for null terminator */ 18 | int str_len = fpconv_dtoa(d, buf); 19 | 20 | buf[str_len] = '\0'; 21 | printf("%s", buf); 22 | } 23 | ``` 24 | 25 | ### Why not just use `snprintf`? 26 | Convert doubles faster to shortest strings without precision loss. 27 | 28 | Average processing time on a mix of "long" and "short" doubles in nanoseconds: 29 | ``` 30 | short long 31 | snprintf %g : 515 700 32 | %0.18g : 989 1171 33 | %f : 737 3047 34 | fpconv_dtoa : 165 193 35 | 36 | snprintf overhead : 71 37 | ``` 38 | Measured with `gcc-4.8 -O3` and `glibc 2.17`. 39 | 40 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/powers.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define npowers 87 4 | #define steppowers 8 5 | #define firstpower -348 /* 10 ^ -348 */ 6 | 7 | #define expmax -32 8 | #define expmin -60 9 | 10 | 11 | typedef struct Fp { 12 | uint64_t frac; 13 | int exp; 14 | } Fp; 15 | 16 | static Fp powers_ten[] = { 17 | { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, 18 | { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, 19 | { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, 20 | { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, 21 | { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, 22 | { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, 23 | { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, 24 | { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, 25 | { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, 26 | { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, 27 | { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, 28 | { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, 29 | { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, 30 | { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, 31 | { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, 32 | { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, 33 | { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, 34 | { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, 35 | { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, 36 | { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, 37 | { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, 38 | { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, 39 | { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, 40 | { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, 41 | { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, 42 | { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, 43 | { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, 44 | { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, 45 | { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, 46 | { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, 47 | { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, 48 | { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, 49 | { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, 50 | { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, 51 | { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, 52 | { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, 53 | { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, 54 | { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, 55 | { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, 56 | { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, 57 | { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, 58 | { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, 59 | { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, 60 | { 12648080533535911531U, 1066 } 61 | }; 62 | 63 | static Fp find_cachedpow10(int exp, int* k) 64 | { 65 | const double one_log_ten = 0.30102999566398114; 66 | 67 | int approx = -(exp + npowers) * one_log_ten; 68 | int idx = (approx - firstpower) / steppowers; 69 | 70 | while(1) { 71 | int current = exp + powers_ten[idx].exp + 64; 72 | 73 | if(current < expmin) { 74 | idx++; 75 | continue; 76 | } 77 | 78 | if(current > expmax) { 79 | idx--; 80 | continue; 81 | } 82 | 83 | *k = (firstpower + idx * steppowers); 84 | 85 | return powers_ten[idx]; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/fpconv.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "fpconv.h" 5 | #include "powers.h" 6 | 7 | #define fracmask 0x000FFFFFFFFFFFFFU 8 | #define expmask 0x7FF0000000000000U 9 | #define hiddenbit 0x0010000000000000U 10 | #define signmask 0x8000000000000000U 11 | #define expbias (1023 + 52) 12 | 13 | #define absv(n) ((n) < 0 ? -(n) : (n)) 14 | #define minv(a, b) ((a) < (b) ? (a) : (b)) 15 | 16 | static uint64_t tens[] = { 17 | 10000000000000000000U, 1000000000000000000U, 100000000000000000U, 18 | 10000000000000000U, 1000000000000000U, 100000000000000U, 19 | 10000000000000U, 1000000000000U, 100000000000U, 20 | 10000000000U, 1000000000U, 100000000U, 21 | 10000000U, 1000000U, 100000U, 22 | 10000U, 1000U, 100U, 23 | 10U, 1U 24 | }; 25 | 26 | static inline uint64_t get_dbits(double d) 27 | { 28 | union { 29 | double dbl; 30 | uint64_t i; 31 | } dbl_bits = { d }; 32 | 33 | return dbl_bits.i; 34 | } 35 | 36 | static Fp build_fp(double d) 37 | { 38 | uint64_t bits = get_dbits(d); 39 | 40 | Fp fp; 41 | fp.frac = bits & fracmask; 42 | fp.exp = (bits & expmask) >> 52; 43 | 44 | if(fp.exp) { 45 | fp.frac += hiddenbit; 46 | fp.exp -= expbias; 47 | 48 | } else { 49 | fp.exp = -expbias + 1; 50 | } 51 | 52 | return fp; 53 | } 54 | 55 | static void normalize(Fp* fp) 56 | { 57 | while ((fp->frac & hiddenbit) == 0) { 58 | fp->frac <<= 1; 59 | fp->exp--; 60 | } 61 | 62 | int shift = 64 - 52 - 1; 63 | fp->frac <<= shift; 64 | fp->exp -= shift; 65 | } 66 | 67 | static void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) 68 | { 69 | upper->frac = (fp->frac << 1) + 1; 70 | upper->exp = fp->exp - 1; 71 | 72 | while ((upper->frac & (hiddenbit << 1)) == 0) { 73 | upper->frac <<= 1; 74 | upper->exp--; 75 | } 76 | 77 | int u_shift = 64 - 52 - 2; 78 | 79 | upper->frac <<= u_shift; 80 | upper->exp = upper->exp - u_shift; 81 | 82 | 83 | int l_shift = fp->frac == hiddenbit ? 2 : 1; 84 | 85 | lower->frac = (fp->frac << l_shift) - 1; 86 | lower->exp = fp->exp - l_shift; 87 | 88 | 89 | lower->frac <<= lower->exp - upper->exp; 90 | lower->exp = upper->exp; 91 | } 92 | 93 | static Fp multiply(Fp* a, Fp* b) 94 | { 95 | const uint64_t lomask = 0x00000000FFFFFFFF; 96 | 97 | uint64_t ah_bl = (a->frac >> 32) * (b->frac & lomask); 98 | uint64_t al_bh = (a->frac & lomask) * (b->frac >> 32); 99 | uint64_t al_bl = (a->frac & lomask) * (b->frac & lomask); 100 | uint64_t ah_bh = (a->frac >> 32) * (b->frac >> 32); 101 | 102 | uint64_t tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); 103 | /* round up */ 104 | tmp += 1U << 31; 105 | 106 | Fp fp = { 107 | ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32), 108 | a->exp + b->exp + 64 109 | }; 110 | 111 | return fp; 112 | } 113 | 114 | static void round_digit(char* digits, int ndigits, uint64_t delta, uint64_t rem, uint64_t kappa, uint64_t frac) 115 | { 116 | while (rem < frac && delta - rem >= kappa && 117 | (rem + kappa < frac || frac - rem > rem + kappa - frac)) { 118 | 119 | digits[ndigits - 1]--; 120 | rem += kappa; 121 | } 122 | } 123 | 124 | static int generate_digits(Fp* fp, Fp* upper, Fp* lower, char* digits, int* K) 125 | { 126 | uint64_t wfrac = upper->frac - fp->frac; 127 | uint64_t delta = upper->frac - lower->frac; 128 | 129 | Fp one; 130 | one.frac = 1ULL << -upper->exp; 131 | one.exp = upper->exp; 132 | 133 | uint64_t part1 = upper->frac >> -one.exp; 134 | uint64_t part2 = upper->frac & (one.frac - 1); 135 | 136 | int idx = 0, kappa = 10; 137 | uint64_t* divp; 138 | /* 1000000000 */ 139 | for(divp = tens + 10; kappa > 0; divp++) { 140 | 141 | uint64_t div = *divp; 142 | unsigned digit = part1 / div; 143 | 144 | if (digit || idx) { 145 | digits[idx++] = digit + '0'; 146 | } 147 | 148 | part1 -= digit * div; 149 | kappa--; 150 | 151 | uint64_t tmp = (part1 <<-one.exp) + part2; 152 | if (tmp <= delta) { 153 | *K += kappa; 154 | round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); 155 | 156 | return idx; 157 | } 158 | } 159 | 160 | /* 10 */ 161 | uint64_t* unit = tens + 18; 162 | 163 | while(true) { 164 | part2 *= 10; 165 | delta *= 10; 166 | kappa--; 167 | 168 | unsigned digit = part2 >> -one.exp; 169 | if (digit || idx) { 170 | digits[idx++] = digit + '0'; 171 | } 172 | 173 | part2 &= one.frac - 1; 174 | if (part2 < delta) { 175 | *K += kappa; 176 | round_digit(digits, idx, delta, part2, one.frac, wfrac * *unit); 177 | 178 | return idx; 179 | } 180 | 181 | unit--; 182 | } 183 | } 184 | 185 | static int grisu2(double d, char* digits, int* K) 186 | { 187 | Fp w = build_fp(d); 188 | 189 | Fp lower, upper; 190 | get_normalized_boundaries(&w, &lower, &upper); 191 | 192 | normalize(&w); 193 | 194 | int k; 195 | Fp cp = find_cachedpow10(upper.exp, &k); 196 | 197 | w = multiply(&w, &cp); 198 | upper = multiply(&upper, &cp); 199 | lower = multiply(&lower, &cp); 200 | 201 | lower.frac++; 202 | upper.frac--; 203 | 204 | *K = -k; 205 | 206 | return generate_digits(&w, &upper, &lower, digits, K); 207 | } 208 | 209 | static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) 210 | { 211 | int exp = absv(K + ndigits - 1); 212 | 213 | int max_trailing_zeros = 7; 214 | 215 | if(neg) { 216 | max_trailing_zeros -= 1; 217 | } 218 | 219 | /* write plain integer */ 220 | if(K >= 0 && (exp < (ndigits + max_trailing_zeros))) { 221 | 222 | memcpy(dest, digits, ndigits); 223 | memset(dest + ndigits, '0', K); 224 | 225 | return ndigits + K; 226 | } 227 | 228 | /* write decimal w/o scientific notation */ 229 | if(K < 0 && (K > -7 || exp < 4)) { 230 | int offset = ndigits - absv(K); 231 | /* fp < 1.0 -> write leading zero */ 232 | if(offset <= 0) { 233 | offset = -offset; 234 | dest[0] = '0'; 235 | dest[1] = '.'; 236 | memset(dest + 2, '0', offset); 237 | memcpy(dest + offset + 2, digits, ndigits); 238 | 239 | return ndigits + 2 + offset; 240 | 241 | /* fp > 1.0 */ 242 | } else { 243 | memcpy(dest, digits, offset); 244 | dest[offset] = '.'; 245 | memcpy(dest + offset + 1, digits + offset, ndigits - offset); 246 | 247 | return ndigits + 1; 248 | } 249 | } 250 | 251 | /* write decimal w/ scientific notation */ 252 | ndigits = minv(ndigits, 18 - neg); 253 | 254 | int idx = 0; 255 | dest[idx++] = digits[0]; 256 | 257 | if(ndigits > 1) { 258 | dest[idx++] = '.'; 259 | memcpy(dest + idx, digits + 1, ndigits - 1); 260 | idx += ndigits - 1; 261 | } 262 | 263 | dest[idx++] = 'e'; 264 | 265 | char sign = K + ndigits - 1 < 0 ? '-' : '+'; 266 | dest[idx++] = sign; 267 | 268 | int cent = 0; 269 | 270 | if(exp > 99) { 271 | cent = exp / 100; 272 | dest[idx++] = cent + '0'; 273 | exp -= cent * 100; 274 | } 275 | if(exp > 9) { 276 | int dec = exp / 10; 277 | dest[idx++] = dec + '0'; 278 | exp -= dec * 10; 279 | 280 | } else if(cent) { 281 | dest[idx++] = '0'; 282 | } 283 | 284 | dest[idx++] = exp % 10 + '0'; 285 | 286 | return idx; 287 | } 288 | 289 | static int filter_special(double fp, char* dest) 290 | { 291 | if(fp == 0.0) { 292 | dest[0] = '0'; 293 | return 1; 294 | } 295 | 296 | uint64_t bits = get_dbits(fp); 297 | 298 | bool nan = (bits & expmask) == expmask; 299 | 300 | if(!nan) { 301 | return 0; 302 | } 303 | 304 | if(bits & fracmask) { 305 | dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n'; 306 | 307 | } else { 308 | dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f'; 309 | } 310 | 311 | return 3; 312 | } 313 | 314 | int fpconv_dtoa(double d, char dest[24]) 315 | { 316 | char digits[18]; 317 | 318 | int str_len = 0; 319 | bool neg = false; 320 | 321 | if(get_dbits(d) & signmask) { 322 | dest[0] = '-'; 323 | str_len++; 324 | neg = true; 325 | } 326 | 327 | int spec = filter_special(d, dest + str_len); 328 | 329 | if(spec) { 330 | return str_len + spec; 331 | } 332 | 333 | int K = 0; 334 | int ndigits = grisu2(d, digits, &K); 335 | 336 | str_len += emit_digits(digits, ndigits, dest + str_len, K, neg); 337 | 338 | return str_len; 339 | } 340 | -------------------------------------------------------------------------------- /src/fpconv_ctfe.d: -------------------------------------------------------------------------------- 1 | 2 | /** Port of grisu2 implementation by night-shift 3 | https://github.com/night-shift/fpconv 4 | Converted to (CTFEable) D by Stefan Koch */ 5 | 6 | module fpconv_ctfe; 7 | 8 | enum npowers = 87; 9 | enum steppowers = 8; 10 | enum firstpower = -348; /* 10 ^ -348 */ 11 | 12 | enum expmax = -32; 13 | enum expmin = -60; 14 | 15 | enum expbits = 11; // 64 - 1 /*for signbit*/ - fracbits; 16 | enum fracbits = 52; 17 | 18 | enum fracmask = (1UL << fracbits) - 1; 19 | enum hiddenbit = (1UL << fracbits); 20 | enum expmask = ((1UL << expbits) - 1) << fracbits; 21 | enum signbit = (1UL << (double.sizeof * 8) -1); // 1 << (64 - 1) 22 | enum expbias = (((1 << (expbits - 1)) -1) + fracbits); 23 | 24 | static assert(fracbits + expbits + 1 == (double.sizeof * 8)); 25 | 26 | 27 | struct Fp { 28 | ulong frac; 29 | int exp; 30 | }; 31 | 32 | enum seperate_arrays = 1; 33 | 34 | static if (seperate_arrays) 35 | { 36 | 37 | static immutable int[npowers] powers_ten_exps = 38 | [ 39 | -1220, -1193, -1166, -1140, 40 | -1113, -1087, -1060, -1034, 41 | -1007, -980, -954, -927, 42 | -901, -874, -847, -821, 43 | -794, -768, -741, -715, 44 | -688, -661, -635, -608, 45 | -582, -555, -529, -502, 46 | -475, -449, -422, -396, 47 | -369, -343, -316, -289, 48 | -263, -236, -210, -183, 49 | -157, -130, -103, -77, 50 | -50, -24, 3, 30, 56, 51 | 83, 109, 136, 162, 52 | 189, 216, 242, 269, 53 | 295, 322, 348, 375, 54 | 402, 428, 455, 481, 55 | 508, 534, 561, 588, 56 | 614, 641, 667, 694, 57 | 720, 747, 774, 800, 58 | 827, 853, 880, 907, 59 | 933, 960, 986, 1013, 60 | 1039, 1066 61 | ]; 62 | 63 | static immutable ulong[npowers] powers_ten_fracs = 64 | [ 65 | 18054884314459144840LU, 13451937075301367670LU, 10022474136428063862LU, 66 | 14934650266808366570LU, 11127181549972568877LU, 16580792590934885855LU, 67 | 12353653155963782858LU, 18408377700990114895LU, 13715310171984221708LU, 68 | 10218702384817765436LU, 15227053142812498563LU, 11345038669416679861LU, 69 | 16905424996341287883LU, 12595523146049147757LU, 9384396036005875287LU, 70 | 13983839803942852151LU, 10418772551374772303LU, 15525180923007089351LU, 71 | 11567161174868858868LU, 17236413322193710309LU, 12842128665889583758LU, 72 | 9568131466127621947LU, 14257626930069360058LU, 10622759856335341974LU, 73 | 15829145694278690180LU, 11793632577567316726LU, 17573882009934360870LU, 74 | 13093562431584567480LU, 9755464219737475723LU, 14536774485912137811LU, 75 | 10830740992659433045LU, 16139061738043178685LU, 12024538023802026127LU, 76 | 17917957937422433684LU, 13349918974505688015LU, 9946464728195732843LU, 77 | 14821387422376473014LU, 11042794154864902060LU, 16455045573212060422LU, 78 | 12259964326927110867LU, 18268770466636286478LU, 13611294676837538539LU, 79 | 10141204801825835212LU, 15111572745182864684LU, 11258999068426240000LU, 80 | 16777216000000000000LU, 12500000000000000000LU, 9313225746154785156LU, 81 | 13877787807814456755LU, 10339757656912845936LU, 15407439555097886824LU, 82 | 11479437019748901445LU, 17105694144590052135LU, 12744735289059618216LU, 83 | 9495567745759798747LU, 14149498560666738074LU, 10542197943230523224LU, 84 | 15709099088952724970LU, 11704190886730495818LU, 17440603504673385349LU, 85 | 12994262207056124023LU, 9681479787123295682LU, 14426529090290212157LU, 86 | 10748601772107342003LU, 16016664761464807395LU, 11933345169920330789LU, 87 | 17782069995880619868LU, 13248674568444952270LU, 9871031767461413346LU, 88 | 14708983551653345445LU, 10959046745042015199LU, 16330252207878254650LU, 89 | 12166986024289022870LU, 18130221999122236476LU, 13508068024458167312LU, 90 | 10064294952495520794LU, 14996968138956309548LU, 11173611982879273257LU, 91 | 16649979327439178909LU, 12405201291620119593LU, 9242595204427927429LU, 92 | 13772540099066387757LU, 10261342003245940623LU, 15290591125556738113LU, 93 | 11392378155556871081LU, 16975966327722178521LU, 12648080533535911531LU 94 | ]; 95 | } 96 | else 97 | { 98 | static immutable Fp[npowers] powers_ten = [ 99 | { 18054884314459144840U, -1220 }, { 13451937075301367670U, -1193 }, 100 | { 10022474136428063862U, -1166 }, { 14934650266808366570U, -1140 }, 101 | { 11127181549972568877U, -1113 }, { 16580792590934885855U, -1087 }, 102 | { 12353653155963782858U, -1060 }, { 18408377700990114895U, -1034 }, 103 | { 13715310171984221708U, -1007 }, { 10218702384817765436U, -980 }, 104 | { 15227053142812498563U, -954 }, { 11345038669416679861U, -927 }, 105 | { 16905424996341287883U, -901 }, { 12595523146049147757U, -874 }, 106 | { 9384396036005875287U, -847 }, { 13983839803942852151U, -821 }, 107 | { 10418772551374772303U, -794 }, { 15525180923007089351U, -768 }, 108 | { 11567161174868858868U, -741 }, { 17236413322193710309U, -715 }, 109 | { 12842128665889583758U, -688 }, { 9568131466127621947U, -661 }, 110 | { 14257626930069360058U, -635 }, { 10622759856335341974U, -608 }, 111 | { 15829145694278690180U, -582 }, { 11793632577567316726U, -555 }, 112 | { 17573882009934360870U, -529 }, { 13093562431584567480U, -502 }, 113 | { 9755464219737475723U, -475 }, { 14536774485912137811U, -449 }, 114 | { 10830740992659433045U, -422 }, { 16139061738043178685U, -396 }, 115 | { 12024538023802026127U, -369 }, { 17917957937422433684U, -343 }, 116 | { 13349918974505688015U, -316 }, { 9946464728195732843U, -289 }, 117 | { 14821387422376473014U, -263 }, { 11042794154864902060U, -236 }, 118 | { 16455045573212060422U, -210 }, { 12259964326927110867U, -183 }, 119 | { 18268770466636286478U, -157 }, { 13611294676837538539U, -130 }, 120 | { 10141204801825835212U, -103 }, { 15111572745182864684U, -77 }, 121 | { 11258999068426240000U, -50 }, { 16777216000000000000U, -24 }, 122 | { 12500000000000000000U, 3 }, { 9313225746154785156U, 30 }, 123 | { 13877787807814456755U, 56 }, { 10339757656912845936U, 83 }, 124 | { 15407439555097886824U, 109 }, { 11479437019748901445U, 136 }, 125 | { 17105694144590052135U, 162 }, { 12744735289059618216U, 189 }, 126 | { 9495567745759798747U, 216 }, { 14149498560666738074U, 242 }, 127 | { 10542197943230523224U, 269 }, { 15709099088952724970U, 295 }, 128 | { 11704190886730495818U, 322 }, { 17440603504673385349U, 348 }, 129 | { 12994262207056124023U, 375 }, { 9681479787123295682U, 402 }, 130 | { 14426529090290212157U, 428 }, { 10748601772107342003U, 455 }, 131 | { 16016664761464807395U, 481 }, { 11933345169920330789U, 508 }, 132 | { 17782069995880619868U, 534 }, { 13248674568444952270U, 561 }, 133 | { 9871031767461413346U, 588 }, { 14708983551653345445U, 614 }, 134 | { 10959046745042015199U, 641 }, { 16330252207878254650U, 667 }, 135 | { 12166986024289022870U, 694 }, { 18130221999122236476U, 720 }, 136 | { 13508068024458167312U, 747 }, { 10064294952495520794U, 774 }, 137 | { 14996968138956309548U, 800 }, { 11173611982879273257U, 827 }, 138 | { 16649979327439178909U, 853 }, { 12405201291620119593U, 880 }, 139 | { 9242595204427927429U, 907 }, { 13772540099066387757U, 933 }, 140 | { 10261342003245940623U, 960 }, { 15290591125556738113U, 986 }, 141 | { 11392378155556871081U, 1013 }, { 16975966327722178521U, 1039 }, 142 | { 12648080533535911531U, 1066 } 143 | ]; 144 | } 145 | 146 | 147 | Fp find_cachedpow10(int exp, int* k) 148 | { 149 | enum one_log_ten = 0.30102999566398114; 150 | 151 | int approx = cast(int) (-(exp + npowers) * one_log_ten); 152 | int idx = (approx - firstpower) / steppowers; 153 | 154 | while(1) { 155 | static if (seperate_arrays) 156 | { 157 | int current = exp + powers_ten_exps[idx] + 64; 158 | } 159 | else 160 | { 161 | int current = exp + powers_ten[idx].exp + 64; 162 | } 163 | 164 | if(current < expmin) { 165 | idx++; 166 | continue; 167 | } 168 | 169 | if(current > expmax) { 170 | idx--; 171 | continue; 172 | } 173 | 174 | *k = (firstpower + idx * steppowers); 175 | 176 | static if (seperate_arrays) 177 | { 178 | auto result = Fp(powers_ten_fracs[idx], powers_ten_exps[idx]); 179 | } 180 | else 181 | { 182 | auto result = powers_ten[idx]; 183 | } 184 | 185 | return result; 186 | } 187 | } 188 | 189 | static immutable ulong[20] tens = [ 190 | 10000000000000000000U, 1000000000000000000U, 100000000000000000U, 191 | 10000000000000000U, 1000000000000000U, 100000000000000U, 192 | 10000000000000U, 1000000000000U, 100000000000U, 193 | 10000000000U, 1000000000U, 100000000U, 194 | 10000000U, 1000000U, 100000U, 195 | 10000U, 1000U, 100U, 196 | 10U, 1U 197 | ]; 198 | 199 | ulong get_dbits(double d) 200 | { 201 | const bits = *cast(ulong*)&d; 202 | return bits; 203 | } 204 | 205 | Fp build_fp(double d) 206 | { 207 | ulong bits = get_dbits(d); 208 | 209 | ulong frac = bits & fracmask; 210 | long lexp = (bits & expmask) >> fracbits; 211 | 212 | if(lexp) { 213 | frac += hiddenbit; 214 | lexp -= expbias; 215 | } else { 216 | lexp = -(expbias) + 1; 217 | } 218 | 219 | Fp fp = Fp (frac, cast(int)lexp); 220 | return fp; 221 | } 222 | 223 | double build_double(Fp fp) 224 | { 225 | ulong bits; 226 | if (fp.exp == -(expbias) + 1) 227 | { 228 | bits = fp.frac; 229 | } 230 | else 231 | { 232 | bits = fp.frac - hiddenbit; 233 | bits |= ((ulong(fp.exp + expbias) << fracbits) & expmask); 234 | } 235 | 236 | double r = *(cast(double*) &bits); 237 | return r; 238 | } 239 | 240 | static assert (build_fp(double.max).build_double == double.max); 241 | 242 | void normalize(Fp* fp) 243 | { 244 | while ((fp.frac & hiddenbit) == 0) { 245 | fp.frac <<= 1; 246 | fp.exp--; 247 | } 248 | 249 | int shift = 64 - 52 - 1; 250 | fp.frac <<= shift; 251 | fp.exp -= shift; 252 | } 253 | 254 | void get_normalized_boundaries(Fp* fp, Fp* lower, Fp* upper) 255 | { 256 | upper.frac = (fp.frac << 1) + 1; 257 | upper.exp = fp.exp - 1; 258 | 259 | while ((upper.frac & (hiddenbit << 1)) == 0) { 260 | upper.frac <<= 1; 261 | upper.exp--; 262 | } 263 | 264 | int u_shift = 64 - 52 - 2; 265 | 266 | upper.frac <<= u_shift; 267 | upper.exp = upper.exp - u_shift; 268 | 269 | 270 | int l_shift = fp.frac == 0x0010000000000000U ? 2 : 1; 271 | 272 | lower.frac = (fp.frac << l_shift) - 1; 273 | lower.exp = fp.exp - l_shift; 274 | 275 | 276 | lower.frac <<= lower.exp - upper.exp; 277 | lower.exp = upper.exp; 278 | } 279 | 280 | Fp multiply(const Fp* a, const Fp* b) 281 | { 282 | enum lomask = 0x00000000FFFFFFFF; 283 | 284 | ulong ah_bl = (a.frac >> 32) * (b.frac & lomask); 285 | ulong al_bh = (a.frac & lomask) * (b.frac >> 32); 286 | ulong al_bl = (a.frac & lomask) * (b.frac & lomask); 287 | ulong ah_bh = (a.frac >> 32) * (b.frac >> 32); 288 | 289 | ulong tmp = (ah_bl & lomask) + (al_bh & lomask) + (al_bl >> 32); 290 | 291 | tmp += 1U << 31; 292 | 293 | ulong fract = ah_bh + (ah_bl >> 32) + (al_bh >> 32) + (tmp >> 32); 294 | int exp = a.exp + b.exp + 64; 295 | 296 | auto fp = Fp (fract, exp); 297 | 298 | return fp; 299 | } 300 | 301 | void round_digit(char* digits, int ndigits, ulong delta, ulong rem, ulong kappa, ulong frac) 302 | { 303 | while (rem < frac && delta - rem >= kappa && 304 | (rem + kappa < frac || frac - rem > rem + kappa - frac)) { 305 | 306 | --digits[ndigits - 1]; 307 | rem += kappa; 308 | } 309 | } 310 | 311 | int generate_digits(const Fp* fp, const Fp* upper, const Fp* lower, char* digits, int* K) 312 | { 313 | ulong wfrac = upper.frac - fp.frac; 314 | ulong delta = upper.frac - lower.frac; 315 | 316 | Fp one; 317 | one.frac = 1UL << -upper.exp; 318 | one.exp = upper.exp; 319 | 320 | ulong part1 = upper.frac >> -one.exp; 321 | ulong part2 = upper.frac & (one.frac - 1); 322 | 323 | int idx = 0, kappa = 10; 324 | 325 | for(auto div_idx = 10; kappa > 0; div_idx++) { 326 | 327 | ulong div = tens[div_idx]; 328 | const digit = part1 / div; 329 | 330 | if (digit || idx) { 331 | digits[idx++] = cast(char) (digit + '0'); 332 | } 333 | 334 | part1 -= digit * div; 335 | kappa--; 336 | 337 | ulong tmp = (part1 <<-one.exp) + part2; 338 | if (tmp <= delta) { 339 | *K += kappa; 340 | round_digit(digits, idx, delta, tmp, div << -one.exp, wfrac); 341 | 342 | return idx; 343 | } 344 | } 345 | 346 | 347 | auto unit_idx = 18; 348 | 349 | while(1) { 350 | part2 *= 10; 351 | delta *= 10; 352 | kappa--; 353 | 354 | const digit = part2 >> -one.exp; 355 | if (digit || idx) { 356 | digits[idx++] = cast(char) (digit + '0'); 357 | } 358 | 359 | part2 &= one.frac - 1; 360 | if (part2 < delta) { 361 | *K += kappa; 362 | round_digit(digits, idx, delta, part2, one.frac, wfrac * tens[unit_idx]); 363 | 364 | return idx; 365 | } 366 | 367 | unit_idx--; 368 | } 369 | } 370 | 371 | static int grisu2(double d, char* digits, int* K) 372 | { 373 | Fp w = build_fp(d); 374 | 375 | Fp lower, upper; 376 | get_normalized_boundaries(&w, &lower, &upper); 377 | 378 | normalize(&w); 379 | 380 | int k; 381 | Fp cp = find_cachedpow10(upper.exp, &k); 382 | 383 | w = multiply(&w, &cp); 384 | upper = multiply(&upper, &cp); 385 | lower = multiply(&lower, &cp); 386 | 387 | lower.frac++; 388 | upper.frac--; 389 | 390 | *K = -k; 391 | 392 | return generate_digits(&w, &upper, &lower, digits, K); 393 | } 394 | 395 | static int emit_digits(char* digits, int ndigits, char* dest, int K, bool neg) 396 | { 397 | int exp = ((K + ndigits - 1) < 0 ? -(K + ndigits - 1) : (K + ndigits - 1)); 398 | 399 | 400 | int max_trailing_zeros = 7; 401 | 402 | if(neg) { 403 | max_trailing_zeros -= 1; 404 | } 405 | 406 | if(K >= 0 && (exp < (ndigits + max_trailing_zeros))) { 407 | dest[0 .. ndigits] = digits[0 .. ndigits]; 408 | //memcpy(dest, digits, ndigits); 409 | dest[ndigits .. ndigits + K] = '0'; 410 | //memset(dest + ndigits, '0', K); 411 | 412 | return ndigits + K; 413 | } 414 | 415 | 416 | if(K < 0 && (K > -7 || exp < 4)) { 417 | int offset = ndigits - ((K) < 0 ? -(K) : (K)); 418 | 419 | if(offset <= 0) { 420 | offset = -offset; 421 | dest[0] = '0'; 422 | dest[1] = '.'; 423 | 424 | //memset(dest + 2, '0', offset); 425 | dest[2 .. 2 + offset] = '0'; 426 | //memcpy(dest + offset + 2, digits, ndigits); 427 | dest[2 + offset .. 2 + offset + ndigits] = digits[0 .. ndigits]; 428 | 429 | return ndigits + 2 + offset; 430 | 431 | 432 | } else { 433 | //memcpy(dest, digits, offset); 434 | dest[0 .. offset] = digits[0 .. offset]; 435 | dest[offset] = '.'; 436 | //memcpy(dest + offset + 1, digits + offset, ndigits - offset); 437 | dest[offset + 1 .. 1 + ndigits] = digits[offset .. ndigits]; 438 | 439 | return ndigits + 1; 440 | } 441 | } 442 | 443 | 444 | ndigits = ((ndigits) < (18 - neg) ? (ndigits) : (18 - neg)); 445 | 446 | int idx = 0; 447 | dest[idx++] = digits[0]; 448 | 449 | if(ndigits > 1) { 450 | dest[idx++] = '.'; 451 | //memcpy(dest + idx, digits + 1, ndigits - 1); 452 | dest[idx .. idx + ndigits - 1] = digits[1 .. ndigits]; 453 | idx += ndigits - 1; 454 | } 455 | 456 | dest[idx++] = 'e'; 457 | 458 | char sign = K + ndigits - 1 < 0 ? '-' : '+'; 459 | dest[idx++] = sign; 460 | 461 | auto _cent = 0; 462 | 463 | if(exp > 99) { 464 | _cent = exp / 100; 465 | dest[idx++] = cast(char) (_cent + '0'); 466 | exp -= _cent * 100; 467 | } 468 | if(exp > 9) { 469 | int dec = exp / 10; 470 | dest[idx++] = cast(char) (dec + '0'); 471 | exp -= dec * 10; 472 | 473 | } else if(_cent) { 474 | dest[idx++] = '0'; 475 | } 476 | 477 | dest[idx++] = exp % 10 + '0'; 478 | 479 | return idx; 480 | } 481 | /+ 482 | static int filter_special(double fp, char* dest) 483 | { 484 | ulong bits = get_dbits(fp); 485 | 486 | if((bits & ~signbit) == 0) { 487 | dest[0] = '0'; 488 | return 1; 489 | } 490 | 491 | 492 | bool nan = (bits & expmask) == expmask; 493 | 494 | if(!nan) { 495 | return 0; 496 | } 497 | 498 | if(bits & fracmask) { 499 | dest[0] = 'n'; dest[1] = 'a'; dest[2] = 'n'; 500 | 501 | } else { 502 | dest[0] = 'i'; dest[1] = 'n'; dest[2] = 'f'; 503 | } 504 | 505 | return 3; 506 | } 507 | +/ 508 | int fpconv_dtoa(double d, /*ref char[24]*/ char* dest) 509 | { 510 | char[18] digits; 511 | int str_len = 0; 512 | bool neg = 0; 513 | ulong bits = (*(cast(ulong*)&d)); 514 | 515 | 516 | if(bits & signbit) { 517 | dest[0] = '-'; 518 | str_len = 1; 519 | neg = 1; 520 | } 521 | 522 | if ((bits & ~signbit) == 0) 523 | { 524 | dest[str_len] = '0'; 525 | return ++str_len; 526 | } 527 | 528 | // manually inlined is_special 529 | 530 | bool is_nan = ((bits & expmask) == expmask); 531 | 532 | if (is_nan) 533 | { 534 | // this case is unlikely 535 | // add branch hint when avilable 536 | { 537 | str_len = 3; 538 | if (bits & 0x000FFFFFFFFFFFFFU) 539 | { 540 | dest[0 .. 3] = "nan"; 541 | } 542 | else 543 | { 544 | if (neg) 545 | { 546 | dest[1 .. 4] = "inf"; 547 | str_len = 4; 548 | } 549 | else 550 | { 551 | dest[0 .. 3] = "inf"; 552 | } 553 | } 554 | } 555 | 556 | return str_len; 557 | } 558 | 559 | int K = 0; 560 | auto ndigits = grisu2(d, &digits[0], &K); 561 | 562 | str_len += emit_digits(&digits[0], ndigits, &dest[str_len], K, neg); 563 | return str_len; 564 | } 565 | 566 | 567 | string fpconv_dtoa(double d) 568 | { 569 | char[24] buffer; 570 | const len = fpconv_dtoa(d, &buffer[0]); 571 | auto result = new char[](len); 572 | result[0 .. len] = buffer[0 .. len]; 573 | return cast(string) result; 574 | } 575 | 576 | static assert(() { 577 | return fpconv_dtoa(3.14159); 578 | } () == "3.14159"); 579 | 580 | static assert (fpconv_dtoa(0.3) == "0.3"); 581 | static assert (fpconv_dtoa(-double.infinity) == "-inf"); 582 | static assert (fpconv_dtoa(double.infinity) == "inf"); 583 | static assert (fpconv_dtoa(double.nan) == "nan"); 584 | static assert (fpconv_dtoa(1.3f) == "1.3"); 585 | static assert (fpconv_dtoa(65.221) == "65.221"); 586 | static assert (fpconv_dtoa(1.3) == "1.3"); 587 | static assert (fpconv_dtoa(0.3) == "0.3"); 588 | static assert (fpconv_dtoa(10) == "10"); 589 | static assert (fpconv_dtoa(double.max) == "1.7976931348623157e+308"); 590 | // ensure no index oob error occurs 591 | static assert (fpconv_dtoa(-151115727451828646838272.0)); 592 | 593 | // printf can't handle this one ;) 594 | static assert (fpconv_dtoa(0.3049589) == "0.3049589"); 595 | 596 | /+ 597 | pragma(msg, () { 598 | string[] result; 599 | result.length = npowers * 2; 600 | foreach(idx; 0 .. npowers * 2) 601 | { 602 | const idx2 = idx / 2; 603 | if (idx & 1) 604 | { 605 | static if (seperate_arrays) 606 | { 607 | auto fp = Fp(powers_ten_fracs[idx2], powers_ten_exps[idx2]); 608 | } 609 | else 610 | { 611 | auto fp = powers_ten[idx2]; 612 | } 613 | result[idx] = fpconv_dtoa(build_double(fp)); 614 | } 615 | else 616 | { 617 | result[idx] = fpconv_dtoa(idx2); 618 | } 619 | } 620 | return result; 621 | } ()); 622 | +/ 623 | --------------------------------------------------------------------------------