MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
mtcore::io::floats::dragonbox Namespace Reference

Implementation of the dragonbox algorithm to convert floats to strings Uses slices and writers rather than raw char pointers Also, allows for more control over formatting Don't use this namespace directly. More...

Classes

struct  DragonboxFormatOptions
 
struct  FloatDec64
 

Functions

template<WriterImpl WI>
Result< size_t, typename Writer< WI >::ErrType > format_float (Writer< WI > &writer, f64 f, Slice< const char > opts={nullptr, 0})
 
auto compute_pow10 (i32 p) -> impl::U128
 
auto multiple_of_pow2 (u64 val, i32 exp)
 
auto multiple_of_pow5 (u64 val, i32 exp) -> bool
 
void unsigned_to_ascii_2digs (Slice< char > buff, u32 digits)
 
auto trailing_zeros_2digs (u32 digits) -> i8
 
auto unsigned_to_ascii_8digits_skip_trailing_zeros (Slice< char > buff, u32 digits) -> i32
 
auto print_dec_digs_reversed (Slice< char > buff, u64 out) -> i32
 
auto round_compute_pow10_double (size_t d) -> double
 
auto format_digits (Slice< char > buff, u64 digits, i32 decExp, DragonboxFormatOptions={}) -> Slice< char >
 
auto to_chars (Slice< char > buff, double val, DragonboxFormatOptions={}) -> Result< Slice< char >, SliceWriteError >
 

Detailed Description

Implementation of the dragonbox algorithm to convert floats to strings Uses slices and writers rather than raw char pointers Also, allows for more control over formatting Don't use this namespace directly.

Instead, use the Formatter<> class

Function Documentation

◆ compute_pow10()

auto mtcore::io::floats::dragonbox::compute_pow10 ( i32 p) -> impl::U128

◆ format_digits()

auto mtcore::io::floats::dragonbox::format_digits ( Slice< char > buff,
u64 digits,
i32 decExp,
DragonboxFormatOptions = {} ) -> Slice< char >

◆ format_float()

template<WriterImpl WI>
Result< size_t, typename Writer< WI >::ErrType > mtcore::io::floats::dragonbox::format_float ( Writer< WI > & writer,
f64 f,
Slice< const char > opts = {nullptr, 0} )

Definition at line 159 of file floats.hpp.

159 {
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 }
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 ...
io::Writer< csv::impl::Writer< WI > > writer(io::Writer< WI > &underlying, Options opts={})
Creates a CSV writer which will encode the data before writing it out.
#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
int32_t i32
Alias for 32-bit ints.
auto to_chars(Slice< char > buff, double val, DragonboxFormatOptions={}) -> Result< Slice< char >, SliceWriteError >
auto round_compute_pow10_double(size_t d) -> double
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.
typename Impl::ErrType ErrType
Definition io/writer.hpp:53
Here is the call graph for this function:
Here is the caller graph for this function:

◆ multiple_of_pow2()

auto mtcore::io::floats::dragonbox::multiple_of_pow2 ( u64 val,
i32 exp )

◆ multiple_of_pow5()

auto mtcore::io::floats::dragonbox::multiple_of_pow5 ( u64 val,
i32 exp ) -> bool

◆ print_dec_digs_reversed()

auto mtcore::io::floats::dragonbox::print_dec_digs_reversed ( Slice< char > buff,
u64 out ) -> i32

◆ round_compute_pow10_double()

auto mtcore::io::floats::dragonbox::round_compute_pow10_double ( size_t d) -> double
Here is the caller graph for this function:

◆ to_chars()

auto mtcore::io::floats::dragonbox::to_chars ( Slice< char > buff,
double val,
DragonboxFormatOptions = {} ) -> Result< Slice< char >, SliceWriteError >
Here is the caller graph for this function:

◆ trailing_zeros_2digs()

auto mtcore::io::floats::dragonbox::trailing_zeros_2digs ( u32 digits) -> i8

◆ unsigned_to_ascii_2digs()

void mtcore::io::floats::dragonbox::unsigned_to_ascii_2digs ( Slice< char > buff,
u32 digits )

◆ unsigned_to_ascii_8digits_skip_trailing_zeros()

auto mtcore::io::floats::dragonbox::unsigned_to_ascii_8digits_skip_trailing_zeros ( Slice< char > buff,
u32 digits ) -> i32