Merge branch 'main' of github.com:allenbarnett5/fast_float_ftn into fortran

This commit is contained in:
Daniel Lemire 2023-09-15 09:43:27 -04:00
commit 7646f819a8
6 changed files with 207 additions and 29 deletions

115
README.md
View File

@ -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"
#include <iostream>
int main() {
const std::string input = "1d+4";
double result; double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); fast_float::parse_options options{ fast_float::chars_format::fortran };
if(answer.ec != std::errc()) { auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
// check error if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
} std::cout << "parsed the number " << result << std::endl;
// we have result == 234532.3426362. return EXIT_SUCCESS;
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.
``` ```
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

View File

@ -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;
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
++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;

View File

@ -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
}; };

View File

@ -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
View 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;
}

View File

@ -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;
} }