boostorg / charconv Goto Github PK
View Code? Open in Web Editor NEWC++11 compatible charconv
Home Page: https://www.boost.org/doc/libs/master/libs/charconv/doc/html/charconv.html
License: Boost Software License 1.0
C++11 compatible charconv
Home Page: https://www.boost.org/doc/libs/master/libs/charconv/doc/html/charconv.html
License: Boost Software License 1.0
Locales are used in some corner cases along with switching them back and
forth
https://github.com/cppalliance/charconv/blob/41a83261c0f65aee575e89ccb71d94b9ddcf799b/include/boost/charconv/detail/from_chars_float_impl.hpp#L64
Looks like there's no need in that line for locales switching. Just get the
decimal point separator from the current locale and if it is not '.' then
do the replacement of the first '.' to the locale's separator.
Examples from the benchmark PR (test_boost_to_chars with chars_format::general):
Roundtrip failure with: -1042963531082612
to_chars val: -1042963531082612.5
to_chars ptr: 19
from_chars val: -1.042963531082612e+164
from_chars ptr: 24
Roundtrip failure with: -2984127144195026
to_chars val: -2984127144195026.5
to_chars ptr: 19
from_chars val: -2.984127144195027e-233
from_chars ptr: 24
Roundtrip failure with: 1816265025891540
to_chars val: 1816265025891539.5
to_chars ptr: 18
from_chars val: 1.81626502589154e-98
from_chars ptr: 24
Roundtrip failure with: 217945471321669.8
to_chars val: 217945471321669.75
to_chars ptr: 18
from_chars val: 0
from_chars ptr: 24
Single precision does not have any issues.
This fails:
string_view s = "0e0";
auto err = detail::charconv::from_chars( s.begin(), s.end(), d );
BOOST_ASSERT( err.ptr == s.end() );
Same for e.g. 0e00000000000
and 0e1
.
Since std::bfloat16_t
uses float
as an interchange type the result can exceed the length of brainfloat. The value of std::numeric_limits<std::bfloat16_t>::max_digits10
is 4 so output should be capped at that.
I'm encountering build errors because of undefined symbols when using CMake. It looks like Boost::charconv
is failing to state the dependency.
find_package
. It doesn't happen when consuming the Boost CMake modules generated by b2.target_link_libraries(... quadmath)
such that -lquadmath
is added to the link command line fixes the issue.Full repro:
cd $BOOST_ROOT
mkdir -p __build && cd __build
cmake -DBOOST_INCLUDE_LIBRARIES=charconv -DCMAKE_INSTALL_PREFIX=/tmp/repro/ ..
make -j8 install
cd ~/repro-consumer
mkdir __build && cd __build
cmake -DCMAKE_PREFIX_PATH=/tmp/repro/ ..
make
File ~/repro-consumer/main.cpp
:
#include <boost/charconv/from_chars.hpp>
int main() {
auto str = "42.0";
float val = 0;
boost::charconv::from_chars(str, str + 2, val);
}
File ~/repro-consumer/CMakeLists.txt
:
cmake_minimum_required(VERSION 3.12.0)
project(boost_mysql_example LANGUAGES CXX)
find_package(Boost REQUIRED COMPONENTS charconv)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE Boost::charconv)
If file https://github.com/cppalliance/charconv/blob/0e9dbf273d215ca717ae98f36e9c15d4f88934c2/include/boost/charconv/detail/compute_float64.hpp#L1[compute_float64.hpp] is only included from the cpp files in src
directory, I do not think it should be exposed in include
directory.
The benchmark file provides a long string of values with \0
in between them. The parser currently processes this as a valid character.
Hello Matt,
here is a somewhat clearer test case.
test.cpp.txt
FP128
No buffer overflow, but the values in to_chars_result do not match the default.
all others
buffer overflows + wrong to_chars_result
thx
Gero
Section https://master.charconv.cpp.al/#build_from_chars is uncomfortable to read for a couple of resons.
The description of from_chars_result
is wrong. ptr
is just a pointer. It doesn't point to the first invalid character, or to last
: there is no last
in the scope of from_chars_result
. ptr
points to whatever the user wants it to point to. Now that you have defined this class, users can create its objects for whatever purpose they want. It is only funcitons from_chars
that choose to use it in a specific way.
Please use computer font for referring to variable names. Sentene like "points to the first character not matching the pattern, or has the value of last if all characters are successfully parsed" is confusing. Words "first" and "last" seem like a pair of similar things, but in your sentence only one of them represents an object (which is not in scope).
Same for ec
: the user can assign any value they want. "Valid values for are" belongs to the description of from_chars
funcitons.
The description of comparisons brings no value, and is imprecise. Why not just declare:
friend constexpr bool operator==(const from_chars_result& lhs, const from_chars_result& rhs) noexcept = default;
even though it is a c++11 library?
If you insist on having the two declarations, do change their description to:
operator==
- returns lhs.ptr == phs.ptr && lhs.ec == rhs.ec
,operator!
- returns !(lhs == rhs)
.I think that the initial section would be better off with some verbal description rather than class/function synopses. It should start from explaining that the convention of this library is to report failures via class from_chars_result
with these two fields that are *intended` to represent the following situations.
https://lemire.me/blog/2023/11/28/parsing-8-bit-integers-quickly/
License on blog code: "Unless otherwise stated, I make no copyright claim on this code: you may consider it to be in the public domain. Don't bother forking this code: just steal it."
Give better/additional examples, and rationale for the use of hex floats.
Hi Matt,
if the buffer is too small and value = ±inf/±(s)nan there will be buffer overflows again. Here:
using buffer_type = std::array<char, 2>;
value = -L::quiet_NaN();
std::bfloat16_t
(std , scientific) {} (2, Value too large for defined data type)
(boost, scientific) BOF! (9, Success)
(std , fixed ) {} (2, Value too large for defined data type)
(boost, fixed ) BOF! (9, Success)
(std , hex ) {} (2, Value too large for defined data type)
(boost, hex ) {} (2, Numerical result out of range)
(std , general ) {} (2, Value too large for defined data type)
(boost, general ) BOF! (9, Success)
regards
Gero
Hi Matt,
there are still warnings
-Wsign-conversion
-Wconversion
-Wold-style-cast
thx
Gero
Hello Matt,
I see you are implementing the bit structures twice:
https://github.com/cppalliance/charconv/blob/develop/include/boost/charconv/detail/bit_layouts.hpp
https://github.com/boostorg/math/blob/develop/include/boost/math/ccmath/signbit.hpp
I think it is better to do this only once, for example in boost::math::bits:
bits.hpp.txt
In detail I first provide various bit structures, no matter if they are needed or not. In bits the "normal" bit structures are available.
In https://github.com/boostorg/math/blob/develop/include/boost/math/ccmath/signbit.hpp you still implement BOOST_MATH_BIT_CAST. This is not necessary anymore, because Peter has adapted boost::core::bit_cast
https://github.com/boostorg/core/blob/develop/include/boost/core/bit.hpp
boostorg/core#147 (not quite correct yet)
thx & regards
Gero
There is a bug at https://github.com/cppalliance/charconv/blob/06d834728ae8970473f6ef18c079f0e7fae7b071/src/from_chars.cpp#L317
std::strtold
is called with a pointer to a temporary std::string and a pointer relative to that is then returned by the function which after the function is invalid/dangling.
It also looks like this is duplicating an existing function that doesn't has this issue and is even better by using a local buffer before falling back to allocating: https://github.com/cppalliance/charconv/blob/06d834728ae8970473f6ef18c079f0e7fae7b071/include/boost/charconv/detail/from_chars_float_impl.hpp#L38 &
https://github.com/cppalliance/charconv/blob/06d834728ae8970473f6ef18c079f0e7fae7b071/include/boost/charconv/detail/from_chars_float_impl.hpp#L101
This should probably be tested too.
Minor comments on documentation:
cc: @anarthal
Hi, I think the way Boost.Charconv deals with FP to_chars
is wrong when the precision is given.
Let's say we do
char buffer[60];
double d = 1e-15;
auto res = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), d,
boost::charconv::chars_format::scientific, 50);
*res.ptr = '\0';
The supposed output is the 51 decimal digits of 1e-15
with rounding, in scientific format, which is: 1.00000000000000007770539987666107923830718560119502e-15
.
But it seems Boost.Charconv produces 0.000000000000001
, which is neither in scientific form nor of 51 digits.
On the other hand, if we do chars_format::general
instead, then we get the same output 0.000000000000001
while it's supposed to be 1.0000000000000000777053998766610792383071856011950e-15
(i.e., one less digit due to the inconsistency in how digits are counted between scientific vs general. I'm not sure if the trailing zero in this case is needed/must be removed/doesn't matter... MS STL implementation seems to remove it, but I'm not sure what's the exact requirement.)
Finally, if we do chars_format::fixed
instead, then we again get the same output 0.000000000000001
while it's supposed to be 0.00000000000000100000000000000007770539987666107924
(i.e., 50 digits after the decimal dot).
I looked into the implementation and it seems Boost.Charconv invokes dragonbox::to_decimal
if the input is in [1e-16,1e16)
. I am not so sure what was the intention of doing these comparisons, but as far as I understand dragonbox::to_decimal
in general will not be enough when the precision is given, especially when it's absurdly large like the case above. (It would be enough when precision is small and the input is normal I think, though.)
The route invoking floff
also looks wrong. For instance, let d = 1e-17
instead of 1e-15
and repeat the above three experiments. For the case of chars_format::scientific
it works correctly. For the case of chars_format::general
, it should produce one less digit again due to different ways of interpreting precision
for these two formats, but Boost.Charconv produces identical outputs. For the case of chars_format::fixed
, the output must be in the fixed-point form (0.00000000000000001000000000000000071542424054621925
) with 50 digits after the decimal dot, but the Boost.Charconv's output is again identical to the case of chars_format::scientific
.
Note that floff
does not provide an option for fixed-point output, so you can't really treat it as a complete blackbox. I think therefore you have four options at this moment:
floff
as a blackbox but manipulate the precision and the resulting string.floff
internally works and tweak it into an implementation of fixed-point formatting.I somehow had an impression that you already did the third option... I don't know why. Thinking about it again it sounds like the least manageable option. In my opinion maybe the most efficient option is 1, whose downside is a huge table.
For a comparison table:
from_chars
to show if it's worth the MSVC team pursuing Lemire's implementation. (microsoft/STL#1610)https://github.com/google/double-conversion
from_chars_erange
to READMEGCC complains with pedantic warnings enabled
include/boost/charconv/detail/integer_conversion.hpp|29 col 20| warning: ISO C++ does not support ‘__int128’ for ‘pack’ [-Wpedantic]
Instead of int ec
the structs should have std::errc ec
.
Several people on slack have asked for a string_view
interface in addition to pointer pair. Core is already a dependency and has the convertible string_view in it.
test value: -35896.53987658756543653653365436f128
The C and C++ standards are clear that for a floating-point type that supports infinities (like IEEE types) all values are in the representable range. So there can never be out of range results (for all implementations supporting IEEE floats, i.e. most of them).
e.g. 1e-9999 should be 0 not ERANGE
. It is within range but unrepresentable.
from_chars()
returns std::errc::result_out_of_range
, but the correct value. to_chars()
returns the correct value and ec.
Class limits
has its own public header, and appears to be part of the library's interface. But the docs never say what it is for and when and how users are expected to use it.
It looks like in the safe usages users would use its constants to determine the sizes of their buffers. I think the docs should show such real life example.
The documentation is a good start.
Main issues:
Other issues:
OVERVIEW:
There are three questions to be answered with API docs: What, Why, How - in
that order. The Why question is the most important and usually the most
neglected.
The "What" question ("What makes up the Boost.Charconv library"). For
example: "Boost.Charconv converts character buffers to numbers, and numbers
to character buffers. It is a small library of two overloaded functions to
do the heavy lifting, plus several supporting enums, structures, templates,
and constants, with a particular focus on performance and consistency
across the supported development environments."
The overview really needs to answer the "Why" question: "Why should I [a
C++ developer new to Boost] be interested in this library?". There should
be several paragraphs explaining how it compares with the standard library
and why use this one instead (or as well as perhaps?), and what scenarios
the library is most suited for. A developer who knows nothing about the
library should know at the end of the overview whether this is for them or
not.
If a claim is made, such as "non-allocating", this should be explained with
at least one sentence (no use of malloc perhaps?), and there should be a
note of whether there are exceptions. If there are one or two exceptions
then they can be mentioned by name in the overview. If there are more, add
a sentence that there are exceptions to a general rule, and instead link to
a section listing and explaining those differences.
Is the charconv standard library the opposite: locale-dependent,
allocating, or throwing - if so point that out.
The overview should be upbeat in tone, and should clearly mention
performance.
Dependencies
It is good practice to mention any significant dependencies - on Boost or
other libraries - as part of the overview or part of the initial setup
process. It is good practice too to mention that there are no significant
dependencies.
The rest of the doc should answer the final question, the "How" question:
Usage Examples
Add a sentence stating what the examples do (before each and every code
block), such as "The following examples show a straightforward use of the
two core functions of the library, from_chars and to_chars, converting a
character string to a number (integer or floating point) and converting a
number to a character string. Note that the character string buffer can be
of any length."
Supported Compilers
Good - clear. What about supported Operating Systems?
What about unsupported compilers - does that mean the lib will not work, or
that the lib has not been tested on other compilers? If the latter, perhaps
suggest that it is up to the developer to verify if the lib works correctly
on an unsupported compiler? Would running the benchmarks on an unsupported
compiler do the trick?
Why use Boost.Charconv over ?
Move this up to the Overview with more details - change "several times
faster" to some specific examples. For example using the
Boost.Charconv:from_chars_result function with floating point numbers can
work from 3.23 to 5.77 times faster than the standard library. OK to link
to your benchmarks for more details, but don't require it to answer the Why
question. And are there any areas where this library performs better
generally - floating point numbers, long integers, anything else - then
mention this in the Overview.
Building the Library (perhaps rename to "Getting Started" as it involves
more than just building)
Introduce the section, start with a sentence such as: "The following
section explains how to download and build this library locally on your
development machine."
B2
What if the developer wants to use C++13, does that mean cxxstd=13 should
be set, or should it always be set to 11. Explain any options.
vcpkg
Change "Any required Boost packages that do not already exist will be
installed automatically." to
"Any required Boost packages not currently installed in your development
environment will be installed automatically."
Add the TIP code into the tip itself, if possible, so its indentation is
correct.
from_chars
Add a general title before this something like "API Reference" with an
introductory sentence (though see the notes on Organization below):
"This section describes all the functions, structures, enums, constants,
and macros available in the library." This is generally a good order to
describe an API reference.
Add some text to describe what the source code does or is. Is this the
source code of the function in the library, or perhaps a summarized version
of its definition? Perhaps add a subheading "Definition" or "Syntax".
from_chars_result
The bulleted list of error codes would be better presented as a table with
sub-heading "Return Values" and two columns entitled: "Value" and
"Description" (or similar). This format should be re-used by
to_chars_result.
from_chars [ don't reuse a heading string at the top level] Parameters (or
from_chars Parameters) might be a better title here, or re-organize as
suggested below.
Again, the parameters would be better represented by a table rather than
bullets, with sub-heading Parameters and columns "Name" and "Description".
This format should be re-used by to_chars.
"One known exception is GCC 5 which does not support constexpr comparison
of const char*." - do you have a workaround or suggestion on how to handle
this?
Consider more descriptive headings for:
from_chars for integral types
from_chars for floating point types
Examples
Integral, Floating Point, Hexadecimal
to_chars
chars_format
Add an intro sentence, noting that this is an enum defining the supported
number formats.
Limits
Add an intro sentence describing what is in this section, and that Limits
are templates (or reorganize as suggested below).
Same notes as above - explain what the Examples are showing, plus nuances
Reference
Not sure what is meant by "Reference" as the above sections appear to be
API reference material. Seems to be a lot of duplication of information
from the above section? Perhaps consider combining the two sections into a
single reference?
References can benefit from being divided up into sections: Functions,
Structures, Enums, Macros, Constants, etc. This makes it clear (from
looking at the table of contents) what the complete contents of the lib are.
Benchmarks
Provide a sentence/paragraph describing the purpose of this section.
Something like:
"This section describes a range of performance benchmarks that have been
run comparing this library with the standard library, and how to run your
own benchmarks if required."
Might need some explanation for libdouble-conversion and {fmt} - such as
"Download the zip file and install the code to your development project" -
or whatever steps are necessary.
Like the use of tables. Is this enough information "to_chars floating point
with the shortest representation" - what is meant by "the shortest
representation"?
Perhaps make it clearer the order of the numbers in Relative Performance -
standard charconv first, then Boost.Charconv second? Just to be completely
clear.
Acknowledgements
Perhaps move "Special thanks to Stephan T. Lavavej for providing the basis
for the benchmarks." to an Acknowledgements section, so you can add more
names as appropriate (testers, design feedback, etc.). A bulleted list
works here.
Sources
Perhaps add an introductory sentence here - not sure that you copied any
algorithms from these but more inspiration and ideas?
Maybe: "The authors acknowledge the inspiration and guidelines from the
following published works." - or similar
Missing Information?:
What about defined constants, such as BOOST_CHARCONV_RUN_BENCHMARKS,
BOOST_CHARCONV_CONSTEXPR - should there be a table of constants defined by
the library - with an explanation of the use of each?
ORGANIZATION and NAVIGATION
Ideally, I would organize the doc into pages (each [page] indicates a new
html page). And ideally the pages are well linked together, for example,
each time from_chars_result is mentioned, it is a link to the page for that
structure. Same for all entries of the functions, structs, enums, etc.
Overview [page]
Intro - answer the What and Why questions
Supported compilers/OS
Getting Started [page]
Downloading and building
Dependencies
Basic usage examples [page]
from_chars examples
to_chars examples
API Reference [page]
toc (table of contents: Functions, Structures, Enumerations, Templates,
Constants, in a table of links)
Functions [page]
toc (table of contents: from_chars, to_chars)
from_chars [page]
overview
definition/syntax (include the library path where the function is
defined)
parameters
return value
remarks/notes [add lower level headings for integers, floating point,
hexadecimal etc. as needed]
examples
to_chars [page]
overview
definition
parameters
return value
remarks/notes
examples
Structures [page]
toc
from_chars_result [page]
overview
definition
fields
remarks/notes
to_chars_result [page]
overview
definition
fields
remarks/notes
Enumerations [page]
toc
chars_format [page]
definition
members
remarks/notes
Templates [page]
toc
Limits [page]
overview
definition
remarks/notes
Constants [page]
table of defined constants with descriptions
Benchmarks
toc
How to run...
Linux
Windows
MacOS
Sources
Acknowledgements
copyright & license
https://drone.cpp.al/boostorg/json/1482/14/2
I've also noticed that GCC 4.8 is not tested in CI. Is it not supported?
Hi Matt,
first the code:
template <typename Type>
void from_and_show(const boost::string_view& view)
{
Type
value{};
{
const boost::charconv::from_chars_result
result = boost::charconv::from_chars(view.begin(), view.end(), value);
std::cout << "boost:";
if (result.ec == std::errc{})
{
std::cout << value;
}
else
{
std::cout << std::make_error_code(result.ec).message();
}
}
std::cout << '\t';
// std::from_chars not support __float128
BOOST_IF_CONSTEXPR (!std::is_same<Type, boost::float128_type>::value)
{
const std::from_chars_result
result = std::from_chars(view.begin(), view.end(), value);
std::cout << "std:";
if (result.ec == std::errc{})
{
std::cout << value;
}
else
{
std::cout << std::make_error_code(result.ec).message();
}
}
std::cout << '\n';
}
template <typename Type>
void to_and_show(const Type value)
{
std::array<char, 32>
string;
{
string.fill(0);
const boost::charconv::to_chars_result
result = boost::charconv::to_chars(string.begin(), string.end(), value);
std::cout << "boost:";
if (result.ec == std::errc{})
{
std::cout << string.data();
}
else
{
std::cout << std::make_error_code(result.ec).message();
}
}
std::cout << '\t';
// std::to_chars not support __float128
BOOST_IF_CONSTEXPR (!std::is_same<Type, boost::float128_type>::value)
{
string.fill(0);
const std::to_chars_result
result = std::to_chars(string.begin(), string.end(), value);
std::cout << "std:";
if (result.ec == std::errc{})
{
std::cout << string.data();
}
else
{
std::cout << std::make_error_code(result.ec).message();
}
}
std::cout << '\n';
}
int main()
{
const boost::string_view
nan_pos{"+nan"},
nan_neg{"-nan"},
nan{"nan"};
std::cout << "from nan_pos:\n";
from_and_show<boost::float128_type>(nan_pos);
from_and_show<boost::float80_t>(nan_pos);
from_and_show<boost::float64_t>(nan_pos);
from_and_show<boost::float32_t>(nan_pos);
std::cout << "\nfrom nan_neg:\n";
from_and_show<boost::float128_type>(nan_neg);
from_and_show<boost::float80_t>(nan_neg);
from_and_show<boost::float64_t>(nan_neg);
from_and_show<boost::float32_t>(nan_neg);
std::cout << "\nfrom nan:\n";
from_and_show<boost::float128_type>(nan);
from_and_show<boost::float80_t>(nan);
from_and_show<boost::float64_t>(nan);
from_and_show<boost::float32_t>(nan);
std::cout << "\nto qnan:\n";
to_and_show(std::numeric_limits<boost::float128_type>::quiet_NaN());
to_and_show(std::numeric_limits<boost::float80_t>::quiet_NaN());
to_and_show(std::numeric_limits<boost::float64_t>::quiet_NaN());
to_and_show(std::numeric_limits<boost::float32_t>::quiet_NaN());
std::cout << "\nto -qnan:\n";
to_and_show(-std::numeric_limits<boost::float128_type>::quiet_NaN());
to_and_show(-std::numeric_limits<boost::float80_t>::quiet_NaN());
to_and_show(-std::numeric_limits<boost::float64_t>::quiet_NaN());
to_and_show(-std::numeric_limits<boost::float32_t>::quiet_NaN());
std::cout << "\nto snan:\n";
to_and_show(std::numeric_limits<boost::float128_type>::signaling_NaN());
to_and_show(std::numeric_limits<boost::float80_t>::signaling_NaN());
to_and_show(std::numeric_limits<boost::float64_t>::signaling_NaN());
to_and_show(std::numeric_limits<boost::float32_t>::signaling_NaN());
std::cout << "\nto -snan:\n";
to_and_show(-std::numeric_limits<boost::float128_type>::signaling_NaN());
to_and_show(-std::numeric_limits<boost::float80_t>::signaling_NaN());
to_and_show(-std::numeric_limits<boost::float64_t>::signaling_NaN());
to_and_show(-std::numeric_limits<boost::float32_t>::signaling_NaN());
return EXIT_SUCCESS;
}
and the results (gcc):
from nan_pos:
boost:Invalid argument
boost:Invalid argument std:Invalid argument
boost:Invalid argument std:Invalid argument
boost:Invalid argument std:Invalid argument
from nan_neg:
boost:Invalid argument
boost:Invalid argument std:-nan
boost:-nan std:-nan
boost:-nan std:-nan
from nan:
boost:Invalid argument
boost:Invalid argument std:nan
boost:nan std:nan
boost:nan std:nan
to qnan:
boost:nan
boost:nan std:nan
boost:nan std:nan
boost:nan std:nan
to -qnan:
boost:-nan(ind)
boost:-nan(ind) std:-nan
boost:-nan(ind) std:-nan
boost:-nan(ind) std:-nan
to snan:
boost:nan(snan)
boost:nan(snan) std:nan
boost:nan(snan) std:nan
boost:nan(snan) std:nan
to -snan:
boost:-nan(snan)
boost:-nan(snan) std:-nan
boost:-nan(snan) std:-nan
boost:-nan(snan) std:-nan
in general:
from_chars("+nan") is also not supported by the standard (unfortunately).
string values for nan:
These deviate from the standard ("nan"/"-nan"). This is documented https://develop.charconv.cpp.al/#to_chars_to_chars_for_floating_point_types, but boost::charconv is incompatible with it.
from_chars "nan"/"-nan":
boost::float128_type and boost::float80_t -> do not work
boost::float64_t and boost::float32_t -> work
to_chars:
-numeric_limits::quiet_NaN and ±numeric_limits::signaling_NaN -> do not work (see string values)
(+)numeric_limits::quiet_NaN -> works
thx
Gero
The initial docs examples use auto
as i n:
auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v);
I recommend spelling out the return type:
boost::charconv::from_chars_result r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v);
It will make it clear for the person that first reads the introduction that the return type is also part of this library, as opposed to using another Boost library or a component from STD.
It seems that std::to_chars
returns errc::value_too_large
rather than errc::result_out_of_range
. Is this simply a mistake or do you have a reason for using errc::result_out_of_range
instead? In the code I edited I used errc::result_out_of_range
for consistency.
to_chars
with only buffer & value, with the format param as well, and with the precision param as well, all should be separate overloads. The reason why the first and the second should be separate overloads is because the first one should behave differently from the second one with fmt == chars_format::general
; it needs to select whatever representation that is shortest, which does not always need to be equal to chars_format::general
's output. The reason why the second and the third should be separate overloads is because by the spec, precision
being negative should be treated as if precision == 6
, because the spec of std::printf
says that negative precision is ignored, which means it should fall back to the default, which is 6. I guess this is quite stupid, but well, it seems that's how the spec is written anyway.
EDIT: Ah, forgot to mention. When fixed format is chosen for the shortest representation, the output of Dragonbox may not be the correctly rounded one, because there can be trailing zeros in the integer part. (See fmtlib/fmt#3649 for a related discussion in libfmt.)
We are in the process of making B2 build changes to all of the B2 build files
to support "modular" consumption of the Boost Libraries by users. See this list
post for some details: https://lists.boost.org/Archives/boost/2024/01/255704.php
The process requires making a variety of changes to make each Boost library
independent of the super-project structure. But the changes do not remove the
super-project structure or the comprehensive Boost release. The changes make
solely make it possible, optionally, for users, like package manages, to easily
consume libraries individually.
Generally the changes include:
Some examples of such changes:
We are asking how you would like us to handle the changes. We would prefer if
you allow the owners of the Boost.org GitHub project to make changes to B2
build files, as needed, to accomplish the changes. But understand
that you may want to manage the proposed changes yourself.
We previously sent emails to all known maintainers to fill out a form with their
preference. We are contacting you in this issue as we have not gotten a response
to that email. You can see the ongoing responses for that form and the responses
to these issues here https://github.com/users/grafikrobot/projects/1/views/6
We are now asking if you can reply directly to this issue to indicate your
preference of handling the changes. Please supply a response to this question
and close the issue (so that we can verify you are a maintainer).
How would you like the build changes to be processed?
Also please indicate any special instructions you want us to consider. Or other
information you want us to be aware of.
Thanks you, René
Hi Matt,
Questions
supported character types
I see that you are implementing from_chars_result_t as a template.
Do you plan to provide from_chars/to_chars for all character types (char32/16/8_t, wchar_t, char)?
multiprecision
Are from_chars/to_chars also provided for boost::multiprecision types?
If not, are from_chars/to_chars implemented in boost::multiprecision itself?
Comments
Conversion
If std::charconv is available I can well imagine that between the types chars_format/from_chars_result/to_chars_result of std::charconv and boost::charconv also times must be converted. You could write functions for this. But I think it makes more sense to provide the boost types with conversions (ctor, cast-operator):
from_chars_result: from_chars_result.hpp.txt in the template specialization
to_chars_result: to_chars_result.hpp.txt
chars_format: chars_format.hpp.txt Unfortunately it is not possible to implement cast operators for enum_a -> enum_b too directly. Therefore I go the way over a struct.
Usage
If std::charconv is available you can use this.
Comparison operators
In case you wonder why I adapted the comparison operators; this has the following background:
gcc very often generates conditional gaps when evaluating the arguments of logical links (&& ||). The result is that the generated code is full of conditional jumps and it increases the code size significantly. This is not cache friendly. In addition, possible cmov's are overlooked/not generated.
With the "int-trick", the binary linking of the arguments, this does not happen. Of course all arguments are evaluated. If necessary, only exactly one conditional jump is generated.
This strategy - to generate branchfree code if possible - I find better.
Warnings
At the moment there are still a lot of warnings (>350) generated concerning conversions Unsigned <-> Signed.
thx
Gero
we should only offer one library, the Boost library
A difference of std::from_chars
to std::strto*
(for both integer and float parsers) is
leading whitespace is not ignored.
I don't see that in the "invalid input" tests. I guess it makes sense to test that too.
And the documentation should cover what the input strings are expected to be. I've found the various format descriptions for floats in an extra section, but none for int. It only states stuff in terms of "matching the pattern", but for ints I wasn't able to find which pattern is used.
Hi Matt,
in quite a few files there are direct checks for msvc:
#if BOOST_MSVC ...
This generates warnings with other compilers. Please rewrite this to
#if defined(BOOST_MSVC) && (BOOST_MSVC ...)
thx
Gero
Hello Matt,
to_chars for std::bfloat16_t is not available
You define BOOST_CHARCONV_HAS_BRAINFLOAT16 in config.hpp, but check for BOOST_CHARCONV_HAS_BFLOAT16 in to_chars.hpp (and for BOOST_CHARCONV_HAS_BRAINFLOAT16 in from_chars.hpp).
Why these error-prone redundancies?
to_chars for FP-Types not work
./Test -nan
gets always -nan(ind)
./Test 3.3
gets
__float128:
std 3.300000 3.300000
boost 3.3000 3.3000
long double:
std 3.300000 3.300000
boost 3.3000 3.3000
double:
std 3.300000 3.300000
boost 3.300000e+00 3.300000e+00
float:
std 3.300000 3.300000
boost 3.300000e+00 3.300000e+00
_Float128:
std 3.300000 3.300000
boost 3.3000 3.3000
_Float64:
std 3.300000 3.300000
boost 3.300000e+00 3.300000e+00
_Float32:
std 3.300000 3.300000
boost 3.300000e+00 3.300000e+00
_Float16:
std 3.300781 3.300781
boost 3.300781e+00 3.300781e+00
std::bfloat16_t:
std 3.296875 3.296875
boost 3.296875e+00 3.296875e+00
Also, to_chars_result.ptr does not always match the actual written values (buffer overflow, hence the double output in print), e.g. compiled with fmt=general:
__float128:
std 3.3 3.3
boost 3.3 3.3
long double:
std 3.3 3.3
boost 3.3
3.3
double:
std 3.3 3.3
boost 3.3e+00 3.3e+000
float:
std 3.3 3.3
boost 3.3e+00 3.3e+000
_Float128:
std 3.3 3.3
boost 3.3 3.3
_Float64:
std 3.3 3.3
boost 3.3e+00 3.3e+000
_Float32:
std 3.3 3.3
boost 3.3e+00 3.3e+000
_Float16:
std 3.30078 3.30078
boost 3.300781e+00 3.300781e+00
std::bfloat16_t:
std 3.29688 3.29688
boost 3.296875e+00 3.296875e+00
code
namespace test
{
using buffer_type = std::array<char, 1024>;
inline constexpr boost::charconv::chars_format to_format(const std::chars_format format) noexcept
{
switch (format)
{
case std::chars_format::scientific: return boost::charconv::chars_format::scientific;
case std::chars_format::fixed: return boost::charconv::chars_format::fixed;
case std::chars_format::hex: return boost::charconv::chars_format::hex;
default: return boost::charconv::chars_format::general;
}
}
template <typename Type>
Type get_as(const int argc, const char*const*const args) noexcept
{
Type result{};
if (argc > 1) [[likely]]
{
const std::string_view view(args[1]);
std::from_chars(view.data(), view.data()+view.size(), result);
}
return result;
}
template <typename Type>
inline std::string nameof()
{
return boost::core::demangle(typeid(Type).name());
}
template <typename Result>
void print(const Result& res, const buffer_type& buf)
{
if constexpr (std::is_same_v<Result, std::to_chars_result>) std::cout << "std\t";
else std::cout << "boost\t";
if (res.ec == std::errc{}) [[likely]] std::cout << std::string_view(buf.data(), res.ptr) << '\t' << buf.data() << std::endl;
else std::cout << "error\n";
}
template <std::floating_point Type>
void test_to_chars(const Type value, const std::chars_format format, const int precision)
{
buffer_type buffer{};
std::cout << nameof<Type>() << ":\n";
const auto rs = std::to_chars(buffer.data(), buffer.data()+buffer.size(), value, format, precision);
print(rs, buffer);
buffer.fill(char{});
const auto rb = boost::charconv::to_chars(buffer.data(), buffer.data()+buffer.size(), value, to_format(format), precision);
print(rb, buffer);
std::cout << std::endl;
}
} // test
int main(const int argc, const char*const*const args)
{
using namespace test;
constexpr std::chars_format fmt{std::chars_format::fixed};
constexpr int pre{6};
test_to_chars(get_as<boost::float128_t>(argc, args), fmt, pre);
test_to_chars(get_as<boost::float80_t>(argc, args), fmt, pre);
test_to_chars(get_as<boost::float64_t>(argc, args), fmt, pre);
test_to_chars(get_as<boost::float32_t>(argc, args), fmt, pre);
test_to_chars(get_as<std::float128_t>(argc, args), fmt, pre);
test_to_chars(get_as<std::float64_t>(argc, args), fmt, pre);
test_to_chars(get_as<std::float32_t>(argc, args), fmt, pre);
test_to_chars(get_as<std::float16_t>(argc, args), fmt, pre);
test_to_chars(get_as<std::bfloat16_t>(argc, args), fmt, pre);
return EXIT_SUCCESS;
}
thx
Gero
In the instance that from_chars
needs to use strtold
in an non-"C" locale with a proper "C" locale string it's likely that the value will be returned incorrectly. Also probe strtoflt128
since it's not documented, but I assume it matches strto*.
The test function: https://github.com/boostorg/json/blob/develop/test/double.cpp#L398
Test data is randomly generated. The number to parse is generated like this:
unsigned long long x1 = rng();
unsigned long long x2 = rng();
int x3 = std::uniform_int_distribution<>( -308, +308 )( rng );
char buffer[ 128 ];
sprintf( buffer, "%llu.%llue%d", x1, x2, x3 );
The tests check if the result of the parse is within 2 ULPs from strtod
's result.
The output template is
source_string
: differencedifference
ulp
strtod:strtod_output_formatted_as_hexfloat
strtod_output_formatted_as_scientific
boost.json:json_output_formatted_as_hexfloat
json_output_formatted_as_scientific
My personal obersravtion: almost all failed tests have exponent equal to zero, some have exponent equal to a small negative number (one digit), none have positive exponent.
Abriged test output (800+ tests fail in total):
13037152512515243620.9123444678836069176e0: difference 14783958307161805 ulp
strtod: 0x1.69da8a45ce200p+63 1.303715251251524e+19
boost.json: 0x1.217ba1d171b33p+60 1.303715251251524e+18
17697431460539906388.13521888826860801885e0: difference 15239063673179838 ulp
strtod: 0x1.eb33d04bf49b7p+63 1.769743146053991e+19
boost.json: 0x1.88f6403cc3af9p+60 1.769743146053991e+18
17947220026488055965.10643827068131766968e0: difference 15263457087823212 ulp
strtod: 0x1.f222ab0d2131dp+63 1.794722002648806e+19
boost.json: 0x1.8e822270e75b1p+60 1.794722002648806e+18
11270160277506678000.2887804927902173715e-2: difference 4861924248478836362 ulp
strtod: 0x1.906582b70668ap+56 1.127016027750668e+17
boost.json: 0x0.0000000000000p+0 0
16014537415383412664.8126122414435723949e0: difference -4324643243183061970 ulp
strtod: 0x1.bc7e21dc7c42ep+63 1.601453741538341e+19
boost.json: inf inf
12357924155005561639.12442313442625658765e0: difference 14717627412873749 ulp
strtod: 0x1.5700551678a6dp+63 1.235792415500556e+19
boost.json: 0x1.1266aa7860858p+60 1.235792415500556e+18
10842998803422154030.17417436331911474420e0: difference 14837738703791900 ulp
strtod: 0x1.2cf423ed13ed2p+63 1.084299880342215e+19
boost.json: 0x1.e1869fe1b97b6p+59 1.084299880342215e+18
3296695948989379140.2072548304930659598e0: difference -4335031181509788263 ulp
strtod: 0x1.6e01bbfddfd99p+61 3.296695948989379e+18
boost.json: inf inf
14720212849613461300.6252427338161741728e0: difference 14948319668206553 ulp
strtod: 0x1.98916730feb3bp+63 1.472021284961346e+19
boost.json: 0x1.46dab8f3fef62p+60 1.472021284961346e+18
13415429427203554359.6827680959828376040e0: difference 14820899412111836 ulp
strtod: 0x1.745a5bf8db948p+63 1.341542942720356e+19
boost.json: 0x1.29e1e32d7c76cp+60 1.341542942720355e+18
16425388162793696488.1428648637494353073e0: difference 15114840694884310 ulp
strtod: 0x1.c7e567046a72ep+63 1.64253881627937e+19
boost.json: 0x1.6cb7859d21f58p+60 1.64253881627937e+18
15551626348852379480.895578500933292035e0: difference 15029512392741603 ulp
strtod: 0x1.afa4f38955e70p+63 1.555162634885238e+19
boost.json: 0x1.5950c2d444b8dp+60 1.555162634885238e+18
10178294731847607931.4139032688024108114e0: difference 15032476224761005 ulp
strtod: 0x1.1a8122436e035p+63 1.017829473184761e+19
boost.json: 0x1.c401d06be3388p+59 1.017829473184761e+18
16884687391751567190.4207240559831443912e0: difference 15159694135212227 ulp
strtod: 0x1.d4a4eb72a1bcfp+63 1.688468739175157e+19
boost.json: 0x1.76ea55f54e30cp+60 1.688468739175157e+18
15508783427740974078.9784169047074029632e0: difference 15025328513726817 ulp
strtod: 0x1.ae7488b789ee7p+63 1.550878342774097e+19
boost.json: 0x1.585d3a2c6e586p+60 1.550878342774097e+18
4161674219653134848.2773916188372125478e0: difference -4333341770824898115 ulp
strtod: 0x1.ce09e539ea9bdp+61 4.161674219653135e+18
boost.json: inf inf
17046718138251591642.10126573241994849589e0: difference 15175517450300120 ulp
strtod: 0x1.d92437a8a1638p+63 1.704671813825159e+19
boost.json: 0x1.7a835fba1ab60p+60 1.704671813825159e+18
17833052686508292662.16607130432395199321e0: difference 15252307933528313 ulp
strtod: 0x1.eef775dea04dfp+63 1.783305268650829e+19
boost.json: 0x1.8bf92b18803e6p+60 1.783305268650829e+18
12902599730923774497.3638243857162020647e0: difference -4326162744005552028 ulp
strtod: 0x1.661e7c3051864p+63 1.290259973092377e+19
boost.json: inf inf
17562616406302510556.14203299143330054223e0: difference 15225898140539468 ulp
strtod: 0x1.e775e5244037ap+63 1.756261640630251e+19
boost.json: 0x1.85f7ea836692ep+60 1.756261640630251e+18
11124534337803053923.8221091433172228722e0: difference 14755257590203745 ulp
strtod: 0x1.34c4921470c5dp+63 1.112453433780305e+19
boost.json: 0x1.ee075020b46fcp+59 1.112453433780305e+18
18376112329591122782.8739648139940204595e0: difference 15305341101798121 ulp
strtod: 0x1.fe0a219b1be8dp+63 1.837611232959112e+19
boost.json: 0x1.98081ae27cba4p+60 1.837611232959112e+18
17303681503759734373.13476111557383782824e0: difference 15200611528963025 ulp
strtod: 0x1.e0460d4c34313p+63 1.730368150375973e+19
boost.json: 0x1.80380aa35cf42p+60 1.730368150375973e+18
4171777641287505421.15745296356884056941e0: difference -4333322037579518485 ulp
strtod: 0x1.cf290d4b36debp+61 4.171777641287505e+18
boost.json: inf inf
15007638836982346553.17622405128745822208e0: difference 14976388612285545 ulp
strtod: 0x1.a08bb0044760ep+63 1.500763883698235e+19
boost.json: 0x1.4d3c8cd0391a5p+60 1.500763883698235e+18
15679947399601163882.9175847862436986831e0: difference 15042043745353789 ulp
strtod: 0x1.b334ba2b04132p+63 1.567994739960116e+19
boost.json: 0x1.5c2a2e88d00f5p+60 1.567994739960116e+18
1375625481728964437.3257470654421881132e0: difference -4340600103374524873 ulp
strtod: 0x1.3173416566e37p+60 1.375625481728964e+18
boost.json: inf inf
17522151151592932105.3192661631912476227e0: difference 15221946455509235 ulp
strtod: 0x1.e6565f45a6cc0p+63 1.752215115159293e+19
boost.json: 0x1.8511e5d1523cdp+60 1.752215115159293e+18
10904266218413608595.11601234078016542213e0: difference 14819789265806122 ulp
strtod: 0x1.2ea778b469abap+63 1.090426621841361e+19
boost.json: 0x1.e43f27870f790p+59 1.090426621841361e+18
11536133438290722858.3167973823142988657e0: difference 14637374413194566 ulp
strtod: 0x1.4031287b6da60p+63 1.153613343829072e+19
boost.json: 0x1.00275395f151ap+60 1.153613343829072e+18
15812369546801428021.676415907514195163e0: difference 15054975595666315 ulp
strtod: 0x1.b6e1a4a68c9b7p+63 1.581236954680143e+19
boost.json: 0x1.5f1aea1ed6e2cp+60 1.581236954680143e+18
10785418902967488452.4135477779533264455e0: difference 14854607815253228 ulp
strtod: 0x1.2b5b02b282522p+63 1.078541890296749e+19
boost.json: 0x1.def80450d0836p+59 1.078541890296749e+18
17725096521973075874.10939473679303342849e0: difference 15241765339335421 ulp
strtod: 0x1.ebf862c0136f2p+63 1.772509652197308e+19
boost.json: 0x1.8993823342bf5p+60 1.772509652197308e+18
18213197810053703865.16826757387480770172e0: difference 15289431480749545 ulp
strtod: 0x1.f9848dd23858dp+63 1.82131978100537e+19
boost.json: 0x1.946a0b0e937a4p+60 1.82131978100537e+18
11950260987498944012.6386608747976736054e0: difference 14677816556671932 ulp
strtod: 0x1.4bafb61b1a2abp+63 1.195026098749894e+19
boost.json: 0x1.09595e7c14eefp+60 1.195026098749894e+18
18435124945933098595.1350387634451383774e0: difference 15311104052612767 ulp
strtod: 0x1.ffad70ee0091cp+63 1.84351249459331e+19
boost.json: 0x1.99578d8b33a7dp+60 1.84351249459331e+18
15732155702845141543.12170084634184239293e0: difference 15047142212467459 ulp
strtod: 0x1.b4a7b084b670ep+63 1.573215570284514e+19
boost.json: 0x1.5d52f39d5ec0bp+60 1.573215570284514e+18
14735145692382608385.13721209258082999052e0: difference 14949777953633228 ulp
strtod: 0x1.98fb81dfc82f8p+63 1.473514569238261e+19
boost.json: 0x1.472f9b196cf2cp+60 1.473514569238261e+18
16843845449241599091.3645101603587738312e0: difference 15155705664263988 ulp
strtod: 0x1.d382b8631b404p+63 1.68438454492416e+19
boost.json: 0x1.76022d1c15cd0p+60 1.68438454492416e+18
13728498331271665810.9173772052889383142e0: difference -4325759473204600909 ulp
strtod: 0x1.7d0ad8fdbdfb3p+63 1.372849833127167e+19
boost.json: inf inf
7727263384834318767.15818157836697797250e0: difference -4329420285508535384 ulp
strtod: 0x1.acf3046e4bba8p+62 7.727263384834318e+18
boost.json: inf inf
10706017860980947897.10919070792848914143e0: difference 14877869839272721 ulp
strtod: 0x1.2926d50b15837p+63 1.070601786098095e+19
boost.json: 0x1.db7154de88d26p+59 1.070601786098095e+18
10654154846154977666.13474161817864302486e0: difference 14893064081897518 ulp
strtod: 0x1.27b652c4d625ep+63 1.065415484615498e+19
boost.json: 0x1.d923b7a156a30p+59 1.065415484615498e+18
10679211027982662991.3553476966102005076e0: difference 14885723403627689 ulp
strtod: 0x1.28685bb2b8ae8p+63 1.067921102798266e+19
boost.json: 0x1.da4092b78de3fp+59 1.067921102798266e+18
16709350569134734862.10502183930325098237e0: difference 15142571398628552 ulp
strtod: 0x1.cfc713a0197e8p+63 1.670935056913474e+19
boost.json: 0x1.7305a94ce1320p+60 1.670935056913474e+18
16462365524966446931.10878889807662127244e0: difference 15118451765408993 ulp
strtod: 0x1.c8ec24727c863p+63 1.646236552496645e+19
boost.json: 0x1.6d89b6c1fd382p+60 1.646236552496645e+18
16259249861213631764.10036525128987826459e0: difference 15098616251370632 ulp
strtod: 0x1.c348eb3507ea7p+63 1.625924986121363e+19
boost.json: 0x1.69072290d321fp+60 1.625924986121363e+18
13238618194926065003.11600368111639048145e-4: difference 4833154247819142138 ulp
strtod: 0x1.2d02e65ee23fap+50 1323861819492606
boost.json: 0x0.0000000000000p+0 0
16425993616894296297.11242468553826483552e0: difference 15114899821261322 ulp
strtod: 0x1.c7e9b454f3a30p+63 1.64259936168943e+19
boost.json: 0x1.6cbaf6aa5c826p+60 1.64259936168943e+18
15273281031176574834.10612599083659480591e0: difference 15002330232812325 ulp
strtod: 0x1.a7eb305a63fbbp+63 1.527328103117657e+19
boost.json: 0x1.53228d151cc96p+60 1.527328103117658e+18
18206574149764647052.17549221604765645903e0: difference 15288784638924442 ulp
strtod: 0x1.f9557d740b101p+63 1.820657414976465e+19
boost.json: 0x1.9444645cd5a67p+60 1.820657414976465e+18
15506626813000530246.8152389634815566227e0: difference 15025117906818571 ulp
strtod: 0x1.ae6535db8ae37p+63 1.550662681300053e+19
boost.json: 0x1.5850f7e2d582cp+60 1.550662681300053e+18
Examples:
Value: 0.4851437654714521865648158005512869
Boost: 4.851437654714521865648158005512869e-01
STL: 0.4851437654714521865648158005512869
Value: 0.007103704461770353364817587316082381
Boost: 7.103704461770353364817587316082381e-03
STL: 0.007103704461770353364817587316082381
For small negative exponents we should be using fixed formatting not scientific.
I'm experimenting with using this library in Boost.JSON. I get these errors from from_chars
:
Invalid argument [generic:22 at ../../boost/json/basic_parser_impl.hpp:2691 in function 'parse_number'] after 3 characters of 0E0
Invalid argument [generic:22 at ../../boost/json/basic_parser_impl.hpp:2691 in function 'parse_number'] after 4 characters of 0E01
Invalid argument [generic:22 at ../../boost/json/basic_parser_impl.hpp:2691 in function 'parse_number'] after 5 characters of 0.0e0
Invalid argument [generic:22 at ../../boost/json/basic_parser_impl.hpp:2691 in function 'parse_number'] after 4 characters of -0E0
Invalid argument [generic:22 at ../../boost/json/basic_parser_impl.hpp:2691 in function 'parse_number'] after 5 characters of -0E01
Invalid argument [generic:22 at ../../boost/json/basic_parser_impl.hpp:2691 in function 'parse_number'] after 6 characters of -0.0e0
Hi Matt,
from_chars for boost::int128_type does not work if the string values are smaller/larger than min/max:
template <typename Type>
void test_from_chars(const std::string_view& view)
{
Type
v1{0}, v2{0};
std::cout << view << ":\n";
std::from_chars(view.data(), view.data()+view.size(), v1);
std::cout << "std\t" << v1 << '\n';
boost::charconv::from_chars(view.data(), view.data()+view.size(), v2);
std::cout << "boost\t" << v2 << '\n';
std::cout << "equal\t" << (v1==v2) << "\n\n";
}
test_from_chars<boost::int128_type>( "170141183460469231731687303715884105728"); // max+1
test_from_chars<boost::int128_type>("-170141183460469231731687303715884105729"); // min-1
gets
170141183460469231731687303715884105728:
std 0
boost -170141183460469231731687303715884105728
equal false
-170141183460469231731687303715884105729:
std 0
boost 170141183460469231731687303715884105727
equal false
thx
Gero
Test value: -35896.53987658756543653653365436f128
FP80
hex not work
(std , hex ) -8.c388a355a1f783ap+12 (22, Success)
(boost, hex ) -1.0000000000000c388ap+81950 (28, Success)
FP16
scientific different precision
(std , scientific) -3.59e+04 (9, Success)
(boost, scientific) -3.5904e+04 (11, Success)
BF16
scientific different precision
(std , scientific) -3.59e+04 (9, Success)
(boost, scientific) -3.5904e+04 (11, Success)
The following behavior is described in from_chars for floating point types:
On
std::errc::result_out_of_range
we return±0
for small values (e.g. 1.0e-99999) or±HUGE_VAL
for large values (e.g. 1.0e+99999) to match the handling ofstd::strtod
. This is a divergence from the standard which states we should return thevalue
argument unmodified.
This divergence from std::from_chars
is unacceptable in a library that claims to be a faster replacement for the STD component. std::from_chars
gives a practical guarantee: either we populated the new value as per your request, or we didn't touch your value. One can easily imagine how this strong (commit-or-rollback) error guarantee is useful. And it will silently disappear when someone replaces std::from_chars
with boost::from_chars
for efficiency.
Test value: -3.589653987658756543653653365436e+04
(std , hex ) -1.1871146ab43ef0735e163704ac67p+15 (35, Success)
(boost, hex ) -1.1871146ab43ef0735e163704ac67p+32783 (38, Success)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.