mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-06 16:56:57 +08:00
Merge branch 'main' of github.com:allenbarnett5/fast_float_ftn into fortran
This commit is contained in:
commit
7646f819a8
117
README.md
117
README.md
@ -55,6 +55,34 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can parse delimited numbers:
|
||||||
|
```C++
|
||||||
|
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
// check error
|
||||||
|
}
|
||||||
|
// we have result == 234532.3426362.
|
||||||
|
if(answer.ptr[0] != ',') {
|
||||||
|
// unexpected delimiter
|
||||||
|
}
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
// check error
|
||||||
|
}
|
||||||
|
// we have result == 7869234.9823.
|
||||||
|
if(answer.ptr[0] != ',') {
|
||||||
|
// unexpected delimiter
|
||||||
|
}
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
// check error
|
||||||
|
}
|
||||||
|
// we have result == 324562.645.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
||||||
the type `fast_float::chars_format`. It is a bitset value: we check whether
|
the type `fast_float::chars_format`. It is a bitset value: we check whether
|
||||||
@ -115,7 +143,7 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using commas as decimal separator
|
## Advanced options: using commas as decimal separator, JSON and Fortran
|
||||||
|
|
||||||
|
|
||||||
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
||||||
@ -140,33 +168,72 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You can parse delimited numbers:
|
You can also parse Fortran-like inputs:
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
#include "fast_float/fast_float.h"
|
||||||
double result;
|
#include <iostream>
|
||||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
|
||||||
if(answer.ec != std::errc()) {
|
int main() {
|
||||||
// check error
|
const std::string input = "1d+4";
|
||||||
}
|
double result;
|
||||||
// we have result == 234532.3426362.
|
fast_float::parse_options options{ fast_float::chars_format::fortran };
|
||||||
if(answer.ptr[0] != ',') {
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
// unexpected delimiter
|
if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||||
}
|
std::cout << "parsed the number " << result << std::endl;
|
||||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
return EXIT_SUCCESS;
|
||||||
if(answer.ec != std::errc()) {
|
}
|
||||||
// check error
|
|
||||||
}
|
|
||||||
// we have result == 7869234.9823.
|
|
||||||
if(answer.ptr[0] != ',') {
|
|
||||||
// unexpected delimiter
|
|
||||||
}
|
|
||||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
|
||||||
if(answer.ec != std::errc()) {
|
|
||||||
// check error
|
|
||||||
}
|
|
||||||
// we have result == 324562.645.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6)):
|
||||||
|
|
||||||
|
|
||||||
|
```C++
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::string input = "+.1"; // not valid
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
By default the JSON format does not allow `inf`:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::string input = "inf"; // not valid in JSON
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
You can allow it with a non-standard `json_or_infnan` variant:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::json_or_infnan };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
``````
|
||||||
|
|
||||||
## Relation With Other Work
|
## Relation With Other Work
|
||||||
|
|
||||||
|
|||||||
@ -347,9 +347,17 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
int64_t exp_number = 0; // explicit exponential part
|
int64_t exp_number = 0; // explicit exponential part
|
||||||
if ((fmt & chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) {
|
if ( ((fmt & chars_format::scientific) &&
|
||||||
|
(p != pend) &&
|
||||||
|
((UC('e') == *p) || (UC('E') == *p)))
|
||||||
|
||
|
||||||
|
((fmt & FASTFLOAT_FORTRANFMT) &&
|
||||||
|
(p != pend) &&
|
||||||
|
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) {
|
||||||
UC const * location_of_e = p;
|
UC const * location_of_e = p;
|
||||||
++p;
|
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
|
||||||
|
++p;
|
||||||
|
}
|
||||||
bool neg_exp = false;
|
bool neg_exp = false;
|
||||||
if ((p != pend) && (UC('-') == *p)) {
|
if ((p != pend) && (UC('-') == *p)) {
|
||||||
neg_exp = true;
|
neg_exp = true;
|
||||||
|
|||||||
@ -13,14 +13,18 @@
|
|||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
#define FASTFLOAT_JSONFMT (1 << 5)
|
#define FASTFLOAT_JSONFMT (1 << 5)
|
||||||
|
#define FASTFLOAT_FORTRANFMT (1 << 6)
|
||||||
|
|
||||||
enum chars_format {
|
enum chars_format {
|
||||||
scientific = 1 << 0,
|
scientific = 1 << 0,
|
||||||
fixed = 1 << 2,
|
fixed = 1 << 2,
|
||||||
hex = 1 << 3,
|
hex = 1 << 3,
|
||||||
no_infnan = 1 << 4,
|
no_infnan = 1 << 4,
|
||||||
|
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
|
||||||
json = FASTFLOAT_JSONFMT | fixed | scientific | no_infnan,
|
json = FASTFLOAT_JSONFMT | fixed | scientific | no_infnan,
|
||||||
|
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
|
||||||
json_or_infnan = FASTFLOAT_JSONFMT | fixed | scientific,
|
json_or_infnan = FASTFLOAT_JSONFMT | fixed | scientific,
|
||||||
|
fortran = FASTFLOAT_FORTRANFMT | fixed | scientific,
|
||||||
general = fixed | scientific
|
general = fixed | scientific
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -73,8 +73,7 @@ fast_float_add_cpp_test(powersoffive_hardround)
|
|||||||
fast_float_add_cpp_test(string_test)
|
fast_float_add_cpp_test(string_test)
|
||||||
|
|
||||||
fast_float_add_cpp_test(json_fmt)
|
fast_float_add_cpp_test(json_fmt)
|
||||||
|
fast_float_add_cpp_test(fortran)
|
||||||
|
|
||||||
|
|
||||||
option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF)
|
option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF)
|
||||||
|
|
||||||
|
|||||||
69
tests/fortran.cpp
Normal file
69
tests/fortran.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
* Exercise the Fortran conversion option.
|
||||||
|
*/
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define FASTFLOAT_ALLOWS_LEADING_PLUS
|
||||||
|
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
int main_readme() {
|
||||||
|
const std::string input = "1d+4";
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::fortran };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n" << result <<"\n"; return EXIT_FAILURE; }
|
||||||
|
std::cout << "parsed the number " << result << std::endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main ()
|
||||||
|
{
|
||||||
|
const std::vector<double> expected{ 10000, 1000, 100, 10, 1, .1, .01, .001, .0001 };
|
||||||
|
const std::vector<std::string> fmt1{ "1+4", "1+3", "1+2", "1+1", "1+0", "1-1", "1-2",
|
||||||
|
"1-3", "1-4" };
|
||||||
|
const std::vector<std::string> fmt2{ "1d+4", "1d+3", "1d+2", "1d+1", "1d+0", "1d-1",
|
||||||
|
"1d-2", "1d-3", "1d-4" };
|
||||||
|
const std::vector<std::string> fmt3{ "+1+4", "+1+3", "+1+2", "+1+1", "+1+0", "+1-1",
|
||||||
|
"+1-2", "+1-3", "+1-4" };
|
||||||
|
const fast_float::parse_options options{ fast_float::chars_format::fortran };
|
||||||
|
|
||||||
|
for ( auto const& f : fmt1 ) {
|
||||||
|
auto d{ std::distance( &fmt1[0], &f ) };
|
||||||
|
double result;
|
||||||
|
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result,
|
||||||
|
options ) };
|
||||||
|
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) {
|
||||||
|
std::cerr << "parsing failure on " << f << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( auto const& f : fmt2 ) {
|
||||||
|
auto d{ std::distance( &fmt2[0], &f ) };
|
||||||
|
double result;
|
||||||
|
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result,
|
||||||
|
options ) };
|
||||||
|
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) {
|
||||||
|
std::cerr << "parsing failure on " << f << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ( auto const& f : fmt3 ) {
|
||||||
|
auto d{ std::distance( &fmt3[0], &f ) };
|
||||||
|
double result;
|
||||||
|
auto answer{ fast_float::from_chars_advanced( f.data(), f.data()+f.size(), result,
|
||||||
|
options ) };
|
||||||
|
if ( answer.ec != std::errc() || result != expected[std::size_t(d)] ) {
|
||||||
|
std::cerr << "parsing failure on " << f << std::endl;
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@ -8,6 +8,34 @@
|
|||||||
|
|
||||||
#include "fast_float/fast_float.h"
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
int main_readme() {
|
||||||
|
const std::string input = "+.1"; // not valid
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main_readme2() {
|
||||||
|
const std::string input = "inf"; // not valid in JSON
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main_readme3() {
|
||||||
|
const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||||
|
double result;
|
||||||
|
fast_float::parse_options options{ fast_float::chars_format::json_or_infnan };
|
||||||
|
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||||
|
if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
const std::vector<double> expected{ -0.2, 0.02, 0.002, 1., 0., std::numeric_limits<double>::infinity() };
|
const std::vector<double> expected{ -0.2, 0.02, 0.002, 1., 0., std::numeric_limits<double>::infinity() };
|
||||||
@ -36,5 +64,8 @@ int main()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||||
|
if(main_readme2() != EXIT_SUCCESS) { return EXIT_FAILURE; }
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user