La biblioteca Fast_Float proporciona implementaciones de solo encabezado FAST para las funciones C ++ de_Chars para tipos float y double , así como tipos enteros. Estas funciones convierten las cadenas ASCII que representan valores decimales (p. Ej., 1.3e10 ) en tipos binarios. Proporcionamos un redondeo exacto (incluida la ronda a uniforme). En nuestra experiencia, estas funciones fast_float muchas veces más rápido que las funciones comparables de patas numéricas de las bibliotecas estándar C ++ existentes.
Específicamente, fast_float proporciona las siguientes dos funciones para analizar números de punto flotante con una sintaxis similar a C ++ 17 (la biblioteca en sí solo requiere 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, ...);También puede analizar los tipos de enteros:
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, ...); El tipo de retorno ( from_chars_result ) se define como la estructura:
struct from_chars_result {
char const *ptr;
std::errc ec;
}; Analiza la secuencia de caracteres [first, last) para un número. Analiza los números de punto flotante que esperan un formato independiente de locales equivalente a la función C ++ 17 de_chars. El valor de punto flotante resultante son los valores de punto flotante más cercanos (usando float o double ), utilizando la convención "Ronda a uniforme" para valores que de otro modo caerían en dos valores. Es decir, proporcionamos un análisis exacto de acuerdo con el estándar IEEE.
Dado un análisis exitoso, el puntero ( ptr ) en el valor devuelto se establece para apuntar justo después del número analizado, y el value referenciado se establece en el valor analizado. En caso de error, la ec devuelta contiene un error representativo, de lo contrario se almacena el valor predeterminado ( std::errc() ).
La implementación no se lanza y no asigna memoria (por ejemplo, con new o malloc ).
Analizará los valores de infinito y nan.
Ejemplo:
# 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;
}Puede analizar números delimitados:
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. Al igual que el estándar C ++ 17, las funciones fast_float::from_chars toman un último argumento opcional del tipo fast_float::chars_format . Es un valor de Bitset: verificamos si fmt & fast_float::chars_format::fixed and fmt & fast_float::chars_format::scientific están configurados para determinar si permitimos el punto fijo y la notación científica respectivamente. El valor predeterminado es fast_float::chars_format::general que permite fixed y scientific .
La biblioteca busca seguir la especificación C ++ 17 (ver 28.2.3. (6.1)).
from_chars no omite los caracteres principales del espacio blanco (a menos que se establezca fast_float::chars_format::chars_format ).+ signo (a menos que se establezca fast_float::chars_format::skip_white_space ).float y double ). Buscamos el valor más cercano. Nos rodeamos a una mantissa uniforme cuando estamos entre dos números de punto flotante binarios.Además, tenemos las siguientes restricciones:
float y double , pero no long double . También admitimos tipos de puntos flotantes de ancho fijo, como std::float32_t y std::float64_t .1e9999 ), lo representamos usando el valor infinito infinito o negativo y la ec devuelta se establece en std::errc::result_out_of_range .Apoyamos Visual Studio, MacOS, Linux, FreeBSD. Apoyamos a Big y Little Endian. Apoyamos sistemas de 32 bits y 64 bits.
Suponemos que el modo de redondeo está configurado en más cercano ( std::fegetround() == FE_TONEAREST ).
También puede analizar los tipos de enteros utilizando diferentes bases (por ejemplo, 2, 10, 16). El siguiente código imprimirá el número 22250738585072012 tres veces:
# 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;
} Al analizar los valores de punto flotante, los números a veces pueden ser demasiado pequeños (por ejemplo, 1e-1000 ) o demasiado grandes (por ejemplo, 1e1000 ). El lenguaje C estableció el precedente de que estos pequeños valores están fuera de rango. En tales casos, es costumbre analizar valores pequeños a cero y grandes valores al infinito. Ese es el comportamiento del lenguaje C (por ejemplo, stdtod ). Ese es el comportamiento seguido de la biblioteca Fast_Float.
Específicamente, seguimos la interpretación de Jonathan Wakely del estándar:
En cualquier caso, el valor resultante es uno de los dos valores de punto flotante más cercano al valor de la cadena que coincide con el patrón.
También es el enfoque adoptado por la biblioteca Microsoft C ++.
Por lo tanto, tenemos los siguientes ejemplos:
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() Los usuarios que deseen que el valor se quede sin modificar dado std::errc::result_out_of_range puede hacerlo agregando dos líneas de código:
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; } En C ++ 20, puede usar fast_float::from_chars para analizar cadenas en el tiempo de compilación, como en el siguiente ejemplo:
// 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 " );
} La biblioteca también admite tipos de puntos flotantes de ancho fijo, como std::float32_t y std::float64_t . Por ejemplo, puedes escribir:
std:: float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);También admitimos entradas UTF-16 y UTF-32, así como ASCII/UTF-8, como en el siguiente ejemplo:
# 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;
} El estándar C ++ estipula que from_chars debe ser independiente de la localidad. En particular, el separador decimal debe ser el período ( . ). Sin embargo, algunos usuarios aún quieren usar la biblioteca fast_float de una manera dependiente de la configuración regional. Usando una función separada llamada from_chars_advanced , permitimos que los usuarios pasen una instancia parse_options que contiene un separador decimal personalizado (por ejemplo, la coma). Puede usarlo de la siguiente manera.
# 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;
} Por defecto, el formato JSON no permite 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;
} Puede permitirlo con una variante json_or_infnan no estándar:
# 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;
}La biblioteca Fast_Float es parte de:
from_chars en GCC se basa en fast_float,El algoritmo FastFloat es parte de las bibliotecas estándar LLVM. Hay una parte de implementación derivada de Adacore.
La biblioteca Fast_Float proporciona un rendimiento similar al de la biblioteca Fast_Double_Parser, pero utilizando un algoritmo actualizado reelaborado desde cero, y mientras ofrece una API más en línea con las expectativas de los programadores C ++. La biblioteca Fast_Double_Parser es parte del marco de aprendizaje automático de Microsoft LightGBM.
rcppfastfloat .fast-float-rust .FastDoubleParser . Se utiliza para sistemas importantes como Jackson.csFastFloat . Puede analizar números de punto flotante aleatorios a una velocidad de 1 GB/s en algunos sistemas. Encontramos que a menudo es dos veces más rápido que el mejor competidor disponible, y muchas veces más rápido que muchas implementaciones de biblioteca estándar.
$ ./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/sConsulte https://github.com/lemire/simple_fastfloat_benchmark para nuestro código de evaluación comparativa.
Esta biblioteca es solo de encabezado por diseño. El archivo CMake proporciona el objetivo fast_float , que es simplemente un puntero al directorio include .
Si deja caer el repositorio fast_float en su proyecto CMake, debería poder usarlo de esta manera:
add_subdirectory (fast_float)
target_link_libraries (myprogram PUBLIC fast_float)O es posible que desee recuperar la dependencia automáticamente si tiene una versión suficientemente reciente de CMake (3.11 o mejor al menos):
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) Debe cambiar la línea GIT_TAG para recuperar la versión que desea usar.
También puede usar CPM, como así:
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v6.1.6) El script script/amalgamate.py se puede usar para generar una versión de encabezado única de la biblioteca si así lo desea. Simplemente ejecute el script desde el directorio raíz de este repositorio. Puede personalizar el tipo de licencia y el archivo de salida si se desea como se describe en la línea de comando.
Puede descargar directamente archivos generados automáticamente de cabeza de un solo header:
https://github.com/fastfloat/fast_float/releases/download/v7.0.0/fast_float.h
fast_float-devel ). Aunque este trabajo está inspirado en muchas personas diferentes, este trabajo se benefició especialmente de los intercambios con Michael Eisel, quien motivó la investigación original con sus ideas clave y con Nigel Tao, quien proporcionó comentarios invaluables. Rémy Ouompheng implementó primero una ruta rápida que usamos en el caso de dígitos largos.
La biblioteca incluye código adaptado de Google Wuffs (escrito por Nigel Tao) que se publicó originalmente bajo la licencia Apache 2.0.