Skip to content

Commit c9fa9b2

Browse files
committed
add function to generate string from decimal repr.
- Some algorithms do not support directly this operation - The function added comes from the Ryu implementation
1 parent eed60ac commit c9fa9b2

File tree

3 files changed

+158
-16
lines changed

3 files changed

+158
-16
lines changed

benchmarks/CMakeLists.txt

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
1-
add_executable(benchmark benchmark.cpp)
2-
target_link_libraries(benchmark PUBLIC grisu2)
3-
if (NOT CYGWIN)
4-
target_link_libraries(benchmark PUBLIC absl::strings)
5-
endif()
6-
target_link_libraries(benchmark PUBLIC double-conversion)
7-
target_link_libraries(benchmark PUBLIC ryu::ryu)
8-
target_link_libraries(benchmark PUBLIC fmt)
9-
target_link_libraries(benchmark PUBLIC cxxopts)
10-
if(NOT WIN32)
11-
target_link_libraries(benchmark PUBLIC netlib)
12-
target_compile_definitions(benchmark PUBLIC NETLIB_SUPPORTED=1)
13-
endif()
14-
15-
target_link_libraries(benchmark PUBLIC dragonbox::dragonbox_to_chars)
1+
add_executable(benchmark
2+
benchmark.cpp
3+
decimalToString.cpp
4+
)
165

176
include(CheckSourceCompiles)
18-
197
check_source_compiles(CXX "
208
#include <charconv>
219
int main(void) {
@@ -29,3 +17,20 @@ int main(void) {
2917
if (from_chars_double)
3018
target_compile_definitions(benchmark PUBLIC FROM_CHARS_DOUBLE_SUPPORTED=1)
3119
endif()
20+
21+
if (NOT WIN32)
22+
target_link_libraries(benchmark PUBLIC netlib)
23+
target_compile_definitions(benchmark PUBLIC NETLIB_SUPPORTED=1)
24+
endif()
25+
26+
if (NOT CYGWIN)
27+
target_link_libraries(benchmark PUBLIC absl::strings)
28+
endif()
29+
30+
target_link_libraries(benchmark PUBLIC fmt)
31+
target_link_libraries(benchmark PUBLIC cxxopts)
32+
33+
target_link_libraries(benchmark PUBLIC grisu2)
34+
target_link_libraries(benchmark PUBLIC double-conversion)
35+
target_link_libraries(benchmark PUBLIC ryu::ryu)
36+
target_link_libraries(benchmark PUBLIC dragonbox::dragonbox_to_chars)

benchmarks/decimalToString.cpp

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
#include "decimalToString.h"
2+
3+
#include <cassert>
4+
#include <cstring>
5+
6+
#include "ryu/digit_table.h"
7+
8+
// Extracted from the Ryu implementation.
9+
static inline uint32_t decimalLength17(const uint64_t v) {
10+
// Function precondition: v is not an 18, 19, or 20-digit number.
11+
// (17 digits are sufficient for round-tripping.)
12+
assert(v < 100000000000000000L);
13+
14+
// Slightly faster than a loop.
15+
// Average output length is 16.38 digits, so we check high-to-low.
16+
if (v >= 10000000000000000L) { return 17; }
17+
if (v >= 1000000000000000L) { return 16; }
18+
if (v >= 100000000000000L) { return 15; }
19+
if (v >= 10000000000000L) { return 14; }
20+
if (v >= 1000000000000L) { return 13; }
21+
if (v >= 100000000000L) { return 12; }
22+
if (v >= 10000000000L) { return 11; }
23+
if (v >= 1000000000L) { return 10; }
24+
if (v >= 100000000L) { return 9; }
25+
if (v >= 10000000L) { return 8; }
26+
if (v >= 1000000L) { return 7; }
27+
if (v >= 100000L) { return 6; }
28+
if (v >= 10000L) { return 5; }
29+
if (v >= 1000L) { return 4; }
30+
if (v >= 100L) { return 3; }
31+
if (v >= 10L) { return 2; }
32+
return 1;
33+
}
34+
35+
// Adapted from the Ryu implementation.
36+
int to_chars(uint64_t mantissa, int32_t exponent, bool sign, char* const result) {
37+
int index = 0;
38+
if (sign)
39+
result[index++] = '-';
40+
41+
uint64_t output = mantissa;
42+
const uint32_t olength = decimalLength17(mantissa);
43+
44+
// Print the decimal digits.
45+
// for (uint32_t i = 0; i < olength - 1; ++i) {
46+
// const uint32_t c = output % 10; output /= 10;
47+
// result[index + olength - i] = (char) ('0' + c);
48+
// }
49+
// result[index] = '0' + output % 10;
50+
51+
uint32_t i = 0;
52+
// We prefer 32-bit operations, even on 64-bit platforms.
53+
// We have at most 17 digits, and uint32_t can store 9 digits.
54+
// If output doesn't fit into uint32_t, we cut off 8 digits,
55+
// so the rest will fit into uint32_t.
56+
if ((output >> 32) != 0) {
57+
// Expensive 64-bit division.
58+
const uint64_t q = output / 100000000;
59+
uint32_t output2 = ((uint32_t) output) - 100000000 * ((uint32_t) q);
60+
output = q;
61+
62+
const uint32_t c = output2 % 10000;
63+
output2 /= 10000;
64+
const uint32_t d = output2 % 10000;
65+
const uint32_t c0 = (c % 100) << 1;
66+
const uint32_t c1 = (c / 100) << 1;
67+
const uint32_t d0 = (d % 100) << 1;
68+
const uint32_t d1 = (d / 100) << 1;
69+
memcpy(result + index + olength - 1, DIGIT_TABLE + c0, 2);
70+
memcpy(result + index + olength - 3, DIGIT_TABLE + c1, 2);
71+
memcpy(result + index + olength - 5, DIGIT_TABLE + d0, 2);
72+
memcpy(result + index + olength - 7, DIGIT_TABLE + d1, 2);
73+
i += 8;
74+
}
75+
uint32_t output2 = (uint32_t) output;
76+
while (output2 >= 10000) {
77+
const uint32_t c = output2 % 10000;
78+
output2 /= 10000;
79+
const uint32_t c0 = (c % 100) << 1;
80+
const uint32_t c1 = (c / 100) << 1;
81+
memcpy(result + index + olength - i - 1, DIGIT_TABLE + c0, 2);
82+
memcpy(result + index + olength - i - 3, DIGIT_TABLE + c1, 2);
83+
i += 4;
84+
}
85+
if (output2 >= 100) {
86+
const uint32_t c = (output2 % 100) << 1;
87+
output2 /= 100;
88+
memcpy(result + index + olength - i - 1, DIGIT_TABLE + c, 2);
89+
i += 2;
90+
}
91+
if (output2 >= 10) {
92+
const uint32_t c = output2 << 1;
93+
// We can't use memcpy here: the decimal dot goes between these two digits.
94+
result[index + olength - i] = DIGIT_TABLE[c + 1];
95+
result[index] = DIGIT_TABLE[c];
96+
} else {
97+
result[index] = (char) ('0' + output2);
98+
}
99+
100+
// Print decimal point if needed.
101+
if (olength > 1) {
102+
result[index + 1] = '.';
103+
index += olength + 1;
104+
} else {
105+
++index;
106+
}
107+
108+
// Print the exponent.
109+
result[index++] = 'E';
110+
int32_t exp = exponent + (int32_t) olength - 1;
111+
if (exp < 0) {
112+
result[index++] = '-';
113+
exp = -exp;
114+
}
115+
116+
if (exp >= 100) {
117+
const int32_t c = exp % 10;
118+
memcpy(result + index, DIGIT_TABLE + 2 * (exp / 10), 2);
119+
result[index + 2] = (char) ('0' + c);
120+
index += 3;
121+
} else if (exp >= 10) {
122+
memcpy(result + index, DIGIT_TABLE + 2 * exp, 2);
123+
index += 2;
124+
} else {
125+
result[index++] = (char) ('0' + exp);
126+
}
127+
128+
return index;
129+
}

benchmarks/decimalToString.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef DECIMALTOSTRING_H
2+
#define DECIMALTOSTRING_H
3+
4+
#include <cstdint>
5+
6+
int to_chars(uint64_t mantissa, int32_t exponent, bool sign, char* const result);
7+
8+
#endif

0 commit comments

Comments
 (0)