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
115
README.md
115
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
|
||||
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
|
||||
@ -140,33 +168,72 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
You can parse delimited numbers:
|
||||
You can also parse Fortran-like inputs:
|
||||
|
||||
```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;
|
||||
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.
|
||||
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"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
|
||||
@ -347,9 +347,17 @@ parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, par
|
||||
return answer;
|
||||
}
|
||||
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;
|
||||
if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) {
|
||||
++p;
|
||||
}
|
||||
bool neg_exp = false;
|
||||
if ((p != pend) && (UC('-') == *p)) {
|
||||
neg_exp = true;
|
||||
|
||||
@ -13,14 +13,18 @@
|
||||
namespace fast_float {
|
||||
|
||||
#define FASTFLOAT_JSONFMT (1 << 5)
|
||||
#define FASTFLOAT_FORTRANFMT (1 << 6)
|
||||
|
||||
enum chars_format {
|
||||
scientific = 1 << 0,
|
||||
fixed = 1 << 2,
|
||||
hex = 1 << 3,
|
||||
no_infnan = 1 << 4,
|
||||
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
|
||||
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,
|
||||
fortran = FASTFLOAT_FORTRANFMT | 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(json_fmt)
|
||||
|
||||
|
||||
fast_float_add_cpp_test(fortran)
|
||||
|
||||
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"
|
||||
|
||||
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()
|
||||
{
|
||||
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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user