FAST_FLOAT库为float和double类型以及整数类型的C ++功能提供了快速的仅标头实现。这些函数将代表小数值(例如1.3e10 )的ASCII字符串转换为二进制类型。我们提供精确的舍入(包括圆形到偶数)。根据我们的经验,这些fast_float功能比现有C ++标准库中的可比数字功能快多次。
具体而言, fast_float提供以下两个功能,以用C ++ 17类语法解析浮点数(库本身仅需要C ++ 11):
from_chars_result from_chars ( char const *first, char const *last, float &value, ...);
from_chars_result from_chars ( char const *first, char const *last, double &value, ...);您还可以解析整数类型:
from_chars_result from_chars ( char const *first, char const *last, int &value, ...);
from_chars_result from_chars ( char const *first, char const *last, unsigned &value, ...);返回类型( from_chars_result )定义为结构:
struct from_chars_result {
char const *ptr;
std::errc ec;
};它将字符序列[first, last)解析为一个数字。它解析了浮点数,期望与C ++ 17相当的语言独立格式从_CHARS函数。所得的浮点值是最接近的浮点值(使用float或double ),使用“圆形到达”的惯例,以否则将在两个值之间落在两个值之间。也就是说,我们根据IEEE标准提供精确解析。
鉴于成功的解析,返回值中的指针( ptr )设置为在解析数字后立即点点,并将引用的value设置为解析值。如果发生错误,返回的ec包含一个代表性错误,否则存储默认值( std::errc() )值。
实现不会抛出,也不会分配内存(例如,使用new或malloc )。
它将解析无穷大和NAN值。
例子:
# include " fast_float/fast_float.h "
# include < iostream >
int main () {
std::string input = " 3.1416 xyz " ;
double result;
auto answer = fast_float::from_chars (input. data (), input. data () + input. size (), result);
if (answer. ec != std::errc ()) { std::cerr << " parsing failure n " ; return EXIT_FAILURE; }
std::cout << " parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}您可以解析划界数字:
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.像C ++ 17标准一样, fast_float::from_chars函数对fast_float::chars_format类型进行可选的最后一个参数。这是一个小点值:我们检查fmt & fast_float::chars_format::fixed and fmt & fast_float::chars_format::scientific是否分别确定我们是否允许固定点和科学符号。默认值为fast_float::chars_format::general允许fixed和scientific 。
图书馆试图遵循C ++ 17(请参阅28.2.3。(6.1))规范。
from_chars函数不会跳过领先的白空间字符(除非fast_float::chars_format::chars_format设置为设置)。+标志(除非设置fast_float::chars_format::skip_white_space )。float和double类型)。我们寻求最近的价值。当我们介于两个二进制浮点数之间时,我们到达了一个均匀的Mantissa。此外,我们有以下限制:
float和double ,但不long double 。我们还支持固定宽度的浮点类型,例如std::float32_t和std::float64_t 。1e9999 ),我们使用无穷大或无限值表示它,并且返回的ec设置为std::errc::result_out_of_range 。我们支持Visual Studio,MacOS,Linux,FreeBSD。我们支持大和小的恩迪安。我们支持32位和64位系统。
我们假设舍入模式设置为最近( std::fegetround() == FE_TONEAREST )。
您还可以使用不同的基础(例如2、10、16)解析整数类型。以下代码将三次打印数字22250738585072012:
# include " fast_float/fast_float.h "
# include < iostream >
int main () {
uint64_t i;
std::string str = " 22250738585072012 " ;
auto answer = fast_float::from_chars (str. data (), str. data () + str. size (), i);
if (answer. ec != std::errc ()) {
std::cerr << " parsing failure n " ;
return EXIT_FAILURE;
}
std::cout << " parsed the number " << i << std::endl;
std::string binstr = " 1001111000011001110110111001001010110100111000110001100 " ;
answer = fast_float::from_chars (binstr. data (), binstr. data () + binstr. size (), i, 2 );
if (answer. ec != std::errc ()) {
std::cerr << " parsing failure n " ;
return EXIT_FAILURE;
}
std::cout << " parsed the number " << i << std::endl;
std::string hexstr = " 4f0cedc95a718c " ;
answer = fast_float::from_chars (hexstr. data (), hexstr. data () + hexstr. size (), i, 16 );
if (answer. ec != std::errc ()) {
std::cerr << " parsing failure n " ;
return EXIT_FAILURE;
}
std::cout << " parsed the number " << i << std::endl;
return EXIT_SUCCESS;
}解析浮点值时,数字有时可能太小(例如, 1e-1000 )或太大(例如, 1e1000 )。 C语言确定了这些小值超出范围的先例。在这种情况下,通常将小值分析为零,而无限大。这就是C语言的行为(例如, stdtod )。那就是fast_float库的行为。
具体而言,我们遵循乔纳森·沃克利(Jonathan Wakely)对标准的解释:
在任何情况下,最终值是最多两个浮点值之一,最接近与模式匹配的字符串值。
这也是Microsoft C ++库采用的方法。
因此,我们有以下示例:
double result = - 1 ;
std::string str = " 3e-1000 " ;
auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
// r.ec == std::errc::result_out_of_range
// r.ptr == str.data() + 7
// result == 0 double result = - 1 ;
std::string str = " 3e1000 " ;
auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result);
// r.ec == std::errc::result_out_of_range
// r.ptr == str.data() + 6
// result == std::numeric_limits<double>::infinity()希望将值未修改的用户给定的std::errc::result_out_of_range可以通过添加两行代码来做到这一点:
double old_result = result; // make copy
auto r = fast_float::from_chars(start, end, result);
if (r.ec == std::errc::result_out_of_range) { result = old_result; }在C ++ 20中,您可以在compile-time时使用fast_float::from_chars将字符串用于分析字符串,如以下示例:
// consteval forces compile-time evaluation of the function in C++20.
consteval double parse (std::string_view input) {
double result;
auto answer = fast_float::from_chars (input. data (), input. data () + input. size (), result);
if (answer. ec != std::errc ()) { return - 1.0 ; }
return result;
}
// This function should compile to a function which
// merely returns 3.1415.
constexpr double constexptest () {
return parse ( " 3.1415 input " );
}该库还支持固定宽度的浮点类型,例如std::float32_t和std::float64_t 。例如,您可以写:
std:: float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);我们还支持UTF-16和UTF-32输入,以及ASCII/UTF-8,如以下示例:
# include " fast_float/fast_float.h "
# include < iostream >
int main () {
std::u16string input = u" 3.1416 xyz " ;
double result;
auto answer = fast_float::from_chars (input. data (), input. data () + input. size (), result);
if (answer. ec != std::errc ()) { std::cerr << " parsing failure n " ; return EXIT_FAILURE; }
std::cout << " parsed the number " << result << std::endl;
return EXIT_SUCCESS;
} C ++标准规定, from_chars必须与区域无关。特别是,十进制分离器必须是( . )。但是,一些用户仍然希望以环境依赖性方式使用fast_float库。使用称为from_chars_advanced的单独函数,我们允许用户通过包含自定义十进制分离器(例如逗号)的parse_options实例。您可以按照以下方式使用它。
# include " fast_float/fast_float.h "
# include < iostream >
int main () {
std::string input = " 3,1416 xyz " ;
double result;
fast_float::parse_options options{fast_float::chars_format::general, ' , ' };
auto answer = fast_float::from_chars_advanced (input. data (), input. data () + input. size (), result, options);
if ((answer. ec != std::errc ()) || ((result != 3.1416 ))) { std::cerr << " parsing failure n " ; return EXIT_FAILURE; }
std::cout << " parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}# include " fast_float/fast_float.h "
# include < iostream >
int main () {
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 " ; return EXIT_FAILURE; }
std::cout << " parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}# include " fast_float/fast_float.h "
# include < iostream >
int main () {
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;
}默认情况下,JSON格式不允许inf :
# include " fast_float/fast_float.h "
# include < iostream >
int main () {
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;
}您可以使用非标准json_or_infnan变体允许它:
# include " fast_float/fast_float.h "
# include < iostream >
int main () {
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;
}fast_float库是:
from_chars功能依赖于fast_float,FastFloat算法是LLVM标准库的一部分。 Adacore有一个派生的实现部分。
FAST_FLOAT库提供了类似于Fast_Double_Parser库的性能,但使用从头开始重新设计的更新算法,同时提供了与C ++程序员的期望更一致的API。 Fast_Double_Parser库是Microsoft LightGBM机器学习框架的一部分。
rcppfastfloat的R绑定。fast-float-rust 。FastDoubleParser 。它用于杰克逊等重要系统。csFastFloat 。 它可以在某些系统上以1 GB/s的速度解析随机浮点数。我们发现它通常是最佳竞争对手的两倍,并且比许多标准图书馆实现更快。
$ ./build/benchmarks/benchmark
# parsing random integers in the range [0,1)
volume = 2.09808 MB
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s有关我们的基准代码,请参见https://github.com/lemire/simple_fastfloat_benchmark。
该库是按设计限制的。 CMAKE文件提供了fast_float目标,它仅是指向include目录的指针。
如果将fast_float存储库放在CMAKE项目中,则应该能够以这种方式使用它:
add_subdirectory (fast_float)
target_link_libraries (myprogram PUBLIC fast_float)或者,如果您有足够的CMAKE版本(至少3.11或更高),则可能需要自动检索依赖项:
FetchContent_Declare(
fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
GIT_TAG tags/v6.1.6
GIT_SHALLOW TRUE )
FetchContent_MakeAvailable(fast_float)
target_link_libraries (myprogram PUBLIC fast_float)您应该更改GIT_TAG行,以便恢复要使用的版本。
您也可以使用CPM,这样:
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v6.1.6)如果需要,可以使用脚本script/amalgamate.py来生成库的单个标头版本。只需从此存储库的根目录中运行脚本即可。如果需要,您可以根据命令行帮助中所述自定义许可类型和输出文件。
您可以直接下载自动生成的单头文件:
https://github.com/fastfloat/fast_float/releases/download/v7.0.0.0/fast_float.h
fast_float-devel )。 尽管这项工作的灵感来自许多不同的人,但这项工作尤其受益于与迈克尔·艾塞尔(Michael Eisel)的交流,迈克尔·艾塞尔(Michael Eisel)以他的主要见解激发了原始研究,以及提供了宝贵反馈的奈杰尔·陶(Nigel Tao)。 RémyOudompheng首先实施了我们在长位数的情况下使用的快速路径。
该库包括根据Google Wuffs(由Nigel Tao撰写)改编的代码,该代码最初是在Apache 2.0许可下发布的。