MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
floats.hpp
Go to the documentation of this file.
1#ifndef MTCORE_FLOATS_H
2#define MTCORE_FLOATS_H
3
5#include <array>
6#include <cmath>
7#include <limits>
8
16 namespace dragonbox {
17 template<WriterImpl WI>
19 Slice<const char> opts = {nullptr, 0});
20
21 namespace impl {
22 template<typename From, typename To = u64>
23 To bit_cast(From f) {
24 static_assert(sizeof(From) == sizeof(To));
25 return std::bit_cast<To>(f);
26 }
27
28 struct DoubleExtraction {
29 // make sure we're working with what we think we are
30 static_assert(std::numeric_limits<f64>::is_iec559 && std::numeric_limits<f64>::digits == 53 &&
31 std::numeric_limits<f64>::max_exponent == 1024);
32
33 static constexpr auto sigSize = std::numeric_limits<f64>::digits;
34 static constexpr auto expBias = std::numeric_limits<f64>::max_exponent - 1 + (sigSize - 1);
35 static constexpr auto maxIeeeExp = static_cast<u64>(2 * std::numeric_limits<f64>::max_exponent - 1);
36 static constexpr auto hiddenBit = static_cast<u64>(1) << (sigSize - 1);
37 static constexpr auto sigMask = hiddenBit - 1;
38 static constexpr auto expMask = maxIeeeExp << (sigSize - 1);
39 static constexpr auto signMask = ~((~static_cast<u64>(0)) >> 1);
40
41 u64 bits;
42
43 static auto from_bits(u64 bits) -> DoubleExtraction;
44
45 static auto from_float(f64 f) -> DoubleExtraction;
46
47 [[nodiscard]] auto phys_significand() const -> u64;
48
49 [[nodiscard]] auto phys_exp() const -> u64;
50
51 [[nodiscard]] auto is_finite() const -> bool;
52
53 [[nodiscard]] auto is_inf() const -> bool;
54
55 [[nodiscard]] auto is_nan() const -> bool;
56
57 [[nodiscard]] auto is_zero() const -> bool;
58
59 [[nodiscard]] auto sign_bit() const -> bool;
60 };
61
62 auto int_floor_div_pow_2(i32 x, i32 p) -> i32;
63
64 auto int_floor_log2_pow10(i32 x) -> i32;
65
66 auto int_floor_log10_pow2(i32 x) -> i32;
67
68 auto int_floor_log7p5_pow2(i32 x) -> i32;
69
70 struct U128 {
71 u64 high;
72 u64 low;
73
74#ifdef __SIZEOF_INT128__
75 [[nodiscard]] static U128 mul(u64 l, u64 remainder) {
76 __extension__ using u128 = unsigned __int128;
77 const auto f = u128{l} * u128{remainder};
78 const auto high = static_cast<u64>((f >> 64) & 0xFFFFFFFFFFFFFFFF);
79 const auto low = static_cast<u64>((f >> 0) & 0xFFFFFFFFFFFFFFFF);
80 return {high, low};
81 }
82#elif defined(_MSC_VER) && defined(_M_X64)
83 [[nodiscard]] static U128 mul(u64 l, u64 remainder) {
84 u64 high = 0;
85 u64 low = _umul128(l, remainder, &high);
86 return {high, low};
87 }
88#else
89 [[nodiscard]] static auto low32(u64 x) -> u32 { return static_cast<u32>((x >> 0) & 0xFFFFFFFF); }
90 [[nodiscard]] static auto high32(u64 x) -> u32 { return static_cast<u32>((x >> 32) & 0xFFFFFFFF); }
91
92 [[nodiscard]] static U128 mul(u64 l, u64 remainder) {
93 const auto mult_ll = static_cast<u64>(low32(l)) * static_cast<u64>(low32(remainder));
94 const auto mult_lh = static_cast<u64>(low32(l)) * static_cast<u64>(high32(remainder));
95 const auto mult_hl = static_cast<u64>(high32(l)) * static_cast<u64>(low32(remainder));
96 const auto mult_hh = static_cast<u64>(high32(l)) * static_cast<u64>(high32(remainder));
97
98 const auto mid1 = mult_hl + high32(mult_ll);
99 const auto mid2 = mult_lh + low32(mid1);
100
101 const auto high = mult_hh + high32(mid1) + high32(mid2);
102 const auto low = low32(mult_ll) | (static_cast<u64>(low32(mid2)) << 32);
103 return {high, low};
104 }
105#endif
106
107 [[nodiscard]] auto mul_shift(u64 x) const -> u64;
108
109 [[nodiscard]] auto mul_parity(u64 twoF, u32 betaM1) const -> bool;
110 };
111 } // namespace impl
112
113 auto compute_pow10(i32 p) -> impl::U128;
114
115 auto multiple_of_pow2(u64 val, i32 exp);
116
117 auto multiple_of_pow5(u64 val, i32 exp) -> bool;
118
119 struct FloatDec64 {
122
124
125 static auto compute_delta(impl::U128 p10, i32 betaM1) -> u32;
126
127 static auto is_integral_endpoint(u64 twoF, i32 exp, i32 minusK) -> bool;
128
129 static auto is_integral_midpoint(u64 twoF, i32 exp, i32 minusK) -> bool;
130
131 static auto from_parts(u64 ieeeSig, u64 ieeeExp) -> FloatDec64;
132 };
133
135
136 auto trailing_zeros_2digs(u32 digits) -> i8;
137
139
141
142 auto round_compute_pow10_double(size_t d) -> double;
143
145 size_t precision = 0;
146 bool capitalE = false;
147 bool forceScientific = false;
148 bool plusE = false;
151 i32 maxDecPlaces = std::numeric_limits<i32>::max();
152 };
153
155
157
158 template<WriterImpl WI>
160 auto opts = DragonboxFormatOptions{};
161
162 if (slices::starts_with('+', fmtStr)) {
163 opts.signIndicator = DragonboxFormatOptions::PLUS;
164 fmtStr = fmtStr.sub(1);
165 }
166 else if (slices::starts_with(slice_from("()"), fmtStr)) {
167 opts.signIndicator = DragonboxFormatOptions::NEG_PAREN;
168 fmtStr = fmtStr.sub(2);
169 }
170
171 if (slices::starts_with('F', fmtStr)) {
172 opts.capitalE = true;
173 fmtStr = fmtStr.sub(1);
174 }
175 else if (slices::starts_with('f', fmtStr)) {
176 opts.capitalE = false;
177 fmtStr = fmtStr.sub(1);
178 }
179
180 if (slices::starts_with('E', fmtStr)) {
181 opts.capitalE = true;
182 opts.forceScientific = true;
183 fmtStr = fmtStr.sub(1);
184 }
185 else if (slices::starts_with('e', fmtStr)) {
186 opts.forceScientific = true;
187 fmtStr = fmtStr.sub(1);
188 }
189
190 if (slices::starts_with('+', fmtStr)) {
191 opts.plusE = true;
192 fmtStr = fmtStr.sub(1);
193 }
194
195 if (slices::contains('.', fmtStr)) {
196 const auto pivot = slices::first_index('.', fmtStr).value();
197 const auto decSpecifier = fmtStr.sub(pivot + 1);
198 ensure(!decSpecifier.empty(), "Invalid format string");
199
200 const auto sepIndex = slices::first_index('-', decSpecifier).value_or(decSpecifier.size());
201 const auto minDigits = decSpecifier.sub(0, sepIndex);
202 ensure(!minDigits.empty(), "Invalid format string");
203
204 opts.minDecPlaces = ascii::base10_to_int<i32>(minDigits).value();
205 ensure(opts.minDecPlaces >= 0);
206
207 opts.maxDecPlaces = opts.minDecPlaces;
208 if (const auto maxDigits = decSpecifier.sub(sepIndex + 1); !maxDigits.empty()) {
209 opts.maxDecPlaces = ascii::base10_to_int<i32>(maxDigits).value();
210 ensure(opts.maxDecPlaces >= opts.minDecPlaces);
211 ensure(opts.maxDecPlaces <= 16);
212 }
213
214 fmtStr = fmtStr.sub(0, pivot);
215
216 if (opts.maxDecPlaces <= 16) {
217 auto b = round_compute_pow10_double(opts.maxDecPlaces);
218 f *= b;
219 f = std::round(f);
220 f /= b;
221 }
222 }
223
224 if (ascii::is_pos_int_str(fmtStr)) {
225 opts.precision = ascii::base10_to_int(fmtStr).value();
226 fmtStr = fmtStr.sub(fmtStr.size());
227 }
228
229 // check that there isn't excess stuff we didn't understand
230 ensure(fmtStr.empty(), "Invalid format string");
231 ensure(opts.precision <= 16);
232 auto b = std::array<char, 100>{};
233 auto buff = mut_slice_from(b);
234 auto printRes = to_chars(buff, f, opts);
235 if (printRes.is_error()) {
236 using Err = typename Writer<WI>::ErrType;
237 return error(Err::OUT_OF_ROOM);
238 }
239
240 if (opts.minDecPlaces == 0 && static_cast<size_t>(opts.maxDecPlaces) >= printRes.value().size()) {
241 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
242 }
243 else {
244 auto raw = printRes.value().to_const();
245 if (slices::contains(slice_from("NaN"), raw) || slices::contains(slice_from("Infinity"), raw)) {
246 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
247 }
248
249 auto decPointIndex = slices::first_index('.', raw);
250 if (decPointIndex.empty()) {
251 if (opts.minDecPlaces == 0) {
252 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
253 }
254
255 size_t written = 0;
256
257 auto sciNoteLowerE = slices::first_index('e', raw);
258 auto sciNoteUpperE = slices::first_index('E', raw);
259
260 auto end = raw.size();
261
262 if (sciNoteLowerE.has_value()) {
263 end = sciNoteLowerE.value();
264 }
265 else if (sciNoteUpperE.has_value()) {
266 end = sciNoteUpperE.value();
267 }
268
269 auto toPrint = raw.sub(0, end);
270
271 if (auto r = writer.write_all(toPrint); r.is_error()) {
272 return r.error();
273 }
274 written += toPrint.size();
275
276 if (auto r = writer.write('.'); r.is_error()) {
277 return r.error();
278 }
279 ++written;
280
281 if (auto r = writer.write_n_times('0', static_cast<size_t>(opts.minDecPlaces)); r.is_error()) {
282 return r.error();
283 }
284 written += opts.minDecPlaces;
285 auto rest = raw.sub(end);
286 if (!rest.empty()) {
287 if (auto r = writer.write_all(rest); r.is_error()) {
288 return r.error();
289 }
290 written += rest.size();
291 }
292
293 return success(written);
294 }
295 else {
296 auto end = raw.size();
297 auto sciNoteLowerE = slices::first_index('e', raw);
298 auto sciNoteUpperE = slices::first_index('E', raw);
299
300 if (sciNoteLowerE.has_value()) {
301 end = sciNoteLowerE.value();
302 }
303 else if (sciNoteUpperE.has_value()) {
304 end = sciNoteUpperE.value();
305 }
306
307 auto decs = raw.sub(0, end).sub(decPointIndex.value() + 1);
308 auto numDigits = decs.size();
309 if (static_cast<i32>(numDigits) >= opts.minDecPlaces && static_cast<i32>(numDigits) <= opts.maxDecPlaces) {
310 return writer.write_all(printRes.value()).with_success_val(printRes.value().size());
311 }
312
313 auto start = raw.sub(0, decPointIndex.value() + 1);
314 size_t written = 0;
315
316 if (auto r = writer.write_all(start); r.is_error()) {
317 return r.error();
318 }
319 written += start.size();
320
321 if (static_cast<i32>(numDigits) < opts.minDecPlaces) {
322 if (auto r = writer.write_all(decs); r.is_error()) {
323 return r.error();
324 }
325 written += decs.size();
326
327 auto toWrite = opts.minDecPlaces - decs.size();
328 if (auto r = writer.write_n_times('0', toWrite); r.is_error()) {
329 return r.error();
330 }
331 written += toWrite;
332 }
333 else {
334 auto decsToWrite = decs.sub(0, opts.maxDecPlaces);
335 if (auto r = writer.write_all(decsToWrite); r.is_error()) {
336 return r.error();
337 }
338 written += decsToWrite.size();
339 }
340 auto finish = raw.sub(end);
341 if (!finish.empty()) {
342 if (auto r = writer.write_all(finish); r.is_error()) {
343 return r.error();
344 }
345 written += finish.size();
346 }
347
348 return success(written);
349 }
350 }
351 }
352 } // namespace dragonbox
353} // namespace mtcore::floats
354
355#endif // MTCORE_FLOATS_H
Result< Out, AsciiNumericParseError > base10_to_int(const Slice< const char > &parse)
Tries to parse an ASCII numeric string into an integer Will return an error if the parsed string does...
Definition ascii.hpp:244
bool is_pos_int_str(Slice< const char > str)
Checks if a string is composed of only positive integer characters Will return false if string is emp...
Definition ascii.hpp:123
constexpr Slice< char32_t > mut_slice_from(char32_t *cstr, size_t len)
Creates a mutable slice from a utf32 string and length.
bool starts_with(const Slice< T > &needle, const Slice< T > &haystack)
Checks whether a slice (the haystack) starts with elements in another slice in the same order (the ne...
constexpr Slice< const char32_t > slice_from(char32_t *cstr)
Creates a slice from a utf32 string in the form of a c string.
mtcore::Optional< size_t > first_index(const Slice< T > &needle, const Slice< T > &haystack)
Gets the first index that a needle appears in the haystack, or nullopt if the needle does not appear ...
bool contains(const std::remove_const_t< T > &needle, const Slice< T > &haystack)
Checks whether a slice (the haystack) contains an element (the needle) Uses the equality operator of ...
SliceWriteError
Errors when writing to a slice.
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
uint64_t u64
Alias for 64-bit unsigned ints.
int32_t i32
Alias for 32-bit ints.
double f64
Alias for 64-bit floats.
int8_t i8
Alias for 8-bit ints.
uint32_t u32
Alias for 32-bit unsigned ints.
Implementation of the dragonbox algorithm to convert floats to strings Uses slices and writers rather...
Definition floats.hpp:16
auto print_dec_digs_reversed(Slice< char > buff, u64 out) -> i32
auto trailing_zeros_2digs(u32 digits) -> i8
void unsigned_to_ascii_2digs(Slice< char > buff, u32 digits)
auto format_digits(Slice< char > buff, u64 digits, i32 decExp, DragonboxFormatOptions={}) -> Slice< char >
auto unsigned_to_ascii_8digits_skip_trailing_zeros(Slice< char > buff, u32 digits) -> i32
auto compute_pow10(i32 p) -> impl::U128
auto multiple_of_pow5(u64 val, i32 exp) -> bool
auto to_chars(Slice< char > buff, double val, DragonboxFormatOptions={}) -> Result< Slice< char >, SliceWriteError >
auto round_compute_pow10_double(size_t d) -> double
auto multiple_of_pow2(u64 val, i32 exp)
Result< size_t, typename Writer< WI >::ErrType > format_float(Writer< WI > &writer, f64 f, Slice< const char > opts={nullptr, 0})
Definition floats.hpp:159
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
A Slice which is just a pointer + length Accessing elements through the array operator will do bounds...
constexpr Slice sub(size_t start) const noexcept
Gets a sub Slice from start.
constexpr size_t size() const noexcept
Gets the size of a Slice.
constexpr bool empty() const noexcept
Checks if a Slice is empty.
A writer that writes data to some sort of stream or buffer Note: the data elements written should be ...
Definition io/writer.hpp:51
typename Impl::ErrType ErrType
Definition io/writer.hpp:53
enum mtcore::io::floats::dragonbox::DragonboxFormatOptions::@335321247214046164356022211110025356113355062163 signIndicator
static auto from_parts(u64 ieeeSig, u64 ieeeExp) -> FloatDec64
static auto from_asymmetric_interval(const i32 exp) -> FloatDec64
static auto compute_delta(impl::U128 p10, i32 betaM1) -> u32
static auto is_integral_midpoint(u64 twoF, i32 exp, i32 minusK) -> bool
static auto is_integral_endpoint(u64 twoF, i32 exp, i32 minusK) -> bool