Skip to content

Commit 90f96f3

Browse files
authored
Merge pull request #67 from tobybell/add-simple
Add simplified implementation in `subproject/simple`
2 parents fe0e5fe + 23abf54 commit 90f96f3

File tree

5 files changed

+2234
-0
lines changed

5 files changed

+2234
-0
lines changed

Diff for: CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ if (DRAGONBOX_ENABLE_SUBPROJECT)
106106
add_subdirectory("subproject/benchmark")
107107
add_subdirectory("subproject/meta")
108108
add_subdirectory("subproject/test")
109+
add_subdirectory("subproject/simple")
109110
endif()
110111

111112
# ---- MSVC Specifics ----

Diff for: subproject/simple/CMakeLists.txt

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_library(simple_dragonbox INTERFACE simple_dragonbox.h)
2+
target_compile_features(simple_dragonbox INTERFACE cxx_std_17)
3+
4+
add_executable(simple_dragonbox_test simple_dragonbox_test.cpp)
5+
target_link_libraries(simple_dragonbox_test PRIVATE
6+
simple_dragonbox
7+
dragonbox::common
8+
ryu::ryu
9+
)

Diff for: subproject/simple/README.md

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# Simple Dragonbox
2+
3+
This is a simplified implementation of the algorithm, that closely follows the
4+
one in `include/dragonbox/dragonbox.h`, but aims to be shorter overall, use less
5+
C++ template indirection, and offer less flexibility and performance for the
6+
sake of simplicity.
7+
8+
Simplifications over the implementation in `include/dragonbox/dragonbox.h` are
9+
made based primarily on the following assumptions:
10+
11+
- No need to support `sign` policies (always uses `return_sign`).
12+
- No need to support `trailing_zeros` policies (always uses `remove`).
13+
- No need to support 128-bit compiler intrinsics (always uses portable
14+
implementation).
15+
- No need to support the `fast` digit-generation policy (always uses `compact`).
16+
- `if constexpr` is available (C++17).
17+
- `float` and `double` use [IEEE-754 binary32](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) and [IEEE-754 binary64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) representations,
18+
respectively.
19+
- A modern compiler and standard library are available.
20+
21+
Note the `cache`, `binary_to_decimal_rounding`, and `decimal_to_binary_rounding`
22+
policies are still supported.
23+
24+
# Usage Examples
25+
26+
Simple string generation from `float`/`double` (mirrors the interface and
27+
behavior of `jkj::dragonbox::to_chars`; see the [primary README](/README.md)):
28+
29+
```cpp
30+
#include "simple_dragonbox.h"
31+
32+
constexpr int buffer_length = 1 + // for '\0'
33+
simple_dragonbox::max_output_string_length<double>;
34+
double x = 1.234; // Also works for float
35+
char buffer[buffer_length];
36+
37+
// Null-terminate the buffer and return the pointer to the null character.
38+
// Hence, the length of the string is `end - buffer`.
39+
// `buffer` is now { '1', '.', '2', '3', '4', 'E', '0', '\0', (garbage) }.
40+
char* end = simple_dragonbox::to_chars(x, buffer);
41+
```
42+
43+
Direct use of `simple_dragonbox::to_decimal` (mirrors the interface and
44+
behavior of `jkj::dragonbox::to_decimal`; see the [primary README](/README.md)):
45+
46+
```cpp
47+
#include "simple_dragonbox.h"
48+
double x = 1.234; // Also works for float
49+
50+
// `x` must be a nonzero finite number.
51+
// `v` is a struct with three members:
52+
// significand : decimal significand (1234 in this case);
53+
// it is of type std::uint64_t for double, std::uint32_t for float
54+
// exponent : decimal exponent (-3 in this case); it is of type int
55+
// sign : as the name suggests; it is of type bool
56+
auto v = jkj::dragonbox::to_decimal(x);
57+
```
58+
59+
Like `jkj::dragonbox::to_decimal`, `simple_dragonbox::to_decimal` only works
60+
with finite nonzero inputs. Its behavior when given infinities, NaNs, and ±0 is
61+
undefined. `simple_dragonbox::to_chars` works fine for any inputs.
62+
63+
# Policies
64+
65+
`simple_dragonbox` supports the same style of policy interface as
66+
`jkj::dragonbox`. For an introduction to `jkj::dragonbox`'s policy interface,
67+
see the [primary README](/README.md). For example,
68+
69+
```cpp
70+
simple_dragonbox::to_decimal(3.14,
71+
simple_dragonbox::policy::cache::compact,
72+
simple_dragonbox::policy::binary_to_decimal_rounding::to_odd);
73+
```
74+
75+
`simple_dragonbox` supports a subset of the policies that `jkj::dragonbox` does.
76+
When supported, policies have the same meaning as the matching policy in
77+
`jkj::dragonbox` (see the [primary README](/README.md)). Supported policies
78+
include:
79+
80+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_even`
81+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_odd`
82+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_plus_infinity`
83+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_minus_infinity`
84+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_zero`
85+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_away_from_zero`
86+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_even_static_boundary`
87+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_odd_static_boundary`
88+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_plus_infinity_static_boundary`
89+
- `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_minus_infinity_static_boundary`
90+
- `simple_dragonbox::policy::decimal_to_binary_rounding::toward_plus_infinity`
91+
- `simple_dragonbox::policy::decimal_to_binary_rounding::toward_minus_infinity`
92+
- `simple_dragonbox::policy::decimal_to_binary_rounding::toward_zero`
93+
- `simple_dragonbox::policy::decimal_to_binary_rounding::away_from_zero`
94+
95+
- `simple_dragonbox::policy::binary_to_decimal_rounding::to_even`
96+
- `simple_dragonbox::policy::binary_to_decimal_rounding::to_odd`
97+
- `simple_dragonbox::policy::binary_to_decimal_rounding::away_from_zero`
98+
- `simple_dragonbox::policy::binary_to_decimal_rounding::toward_zero`
99+
- `simple_dragonbox::policy::binary_to_decimal_rounding::do_not_care`
100+
101+
- `simple_dragonbox::policy::cache::full`
102+
- `simple_dragonbox::policy::cache::compact`
103+
104+
# Internal direct API
105+
106+
Internally, `simple_dragonbox` uses a more explicit, direct interface to express
107+
the various policies, via a class called `simple_dragonbox::detail::impl`. The
108+
`impl` class has four template parameters: `Float`, `BinaryRoundMode`,
109+
`DecimalRoundMode`, and `CacheType`, which correspond to the floating point
110+
type and three categories of policies above. These template parameters can be
111+
specified explicitly to instantiate a particular variant of the algorithm. The
112+
class has a single constructor that receives a value of type `Float` (e.g.
113+
`float` or `double`), and has methods `to_decimal()` and `to_chars()` for
114+
performing the desired conversions.
115+
116+
For example,
117+
118+
```cpp
119+
namespace detail = simple_dragonbox::detail;
120+
using impl = detail::impl<double,
121+
detail::binary_round_mode::toward_zero,
122+
detail::decimal_round_mode::toward_zero,
123+
detail::cache_type::compact>;
124+
125+
char buffer[32];
126+
auto x = impl(3.14);
127+
x.to_decimal(); // equivalent to `simple_dragonbox::to_decimal(3.14, policies...)`
128+
x.to_chars(buffer); // equivalent to `simple_dragonbox::to_chars(3.14, buf, policies...)`
129+
```

0 commit comments

Comments
 (0)