A biblioteca fast_float fornece implementações apenas de cabeçalho rápido para as funções C ++ From_Chars para float e tipos double , além de tipos inteiros. Essas funções convertem seqüências de caracteres ASCII representando valores decimais (por exemplo, 1.3e10 ) em tipos binários. Fornecemos arredondamento exato (incluindo redonda até). Em nossa experiência, essas funções fast_float muitas vezes mais rápidas que as funções comparáveis de pular números das bibliotecas padrão C ++ existentes.
Especificamente, fast_float fornece as duas funções a seguir para analisar números de ponto flutuante com uma sintaxe do tipo C ++ 17 (a própria biblioteca requer apenas 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, ...);Você também pode analisar os tipos de número inteiro:
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, ...); O tipo de retorno ( from_chars_result ) é definido como a estrutura:
struct from_chars_result {
char const *ptr;
std::errc ec;
}; Ele analisa a sequência do personagem [first, last) para um número. Ele analisa os números de ponto flutuante que esperam um formato independente de localidade equivalente à função C ++ 17 From_Chars. O valor do ponto flutuante resultante é os valores de ponto flutuante mais próximos (usando float ou double ), usando a convenção "redonda para uniformizar" para valores que, de outra forma, cairiam bem entre dois valores. Ou seja, fornecemos análise exata de acordo com o padrão IEEE.
Dada uma análise bem -sucedida, o ponteiro ( ptr ) no valor retornado é definido como aponte logo após o número analisado e o value referenciado é definido como o valor analisado. Em caso de erro, o ec retornado contém um erro representativo, caso contrário, o valor padrão ( std::errc() ) é armazenado.
A implementação não joga e não aloca memória (por exemplo, com new ou malloc ).
Ele analisará os valores do infinito e da nan.
Exemplo:
# 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;
}Você pode analisar 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. Como o padrão C ++ 17, as funções fast_float::from_chars recebem um último argumento opcional do tipo fast_float::chars_format . É um valor de bitset: verificamos se fmt & fast_float::chars_format::fixed e fmt & fast_float::chars_format::scientific estão definidos para determinar se permitimos que o ponto fixo e a notação científica respectivamente. O padrão é fast_float::chars_format::general , que permite fixed e scientific .
A biblioteca procura seguir a especificação C ++ 17 (consulte 28.2.3. (6.1)).
from_chars não pula os principais caracteres do espaço branco (a menos que fast_float::chars_format::chars_format esteja definido).+ é proibido (a menos que fast_float::chars_format::skip_white_space esteja definido).float e tipos double ). Buscamos o valor mais próximo. Nós arredondamos para uma Mantissa uniforme quando estamos entre dois números binários de ponto flutuante.Além disso, temos as seguintes restrições:
float e double , mas não long double . Também suportamos tipos de ponto flutuante de largura fixa, como std::float32_t e std::float64_t .1e9999 ), nós o representamos usando o valor infinito ou negativo do infinito e o ec retornado é definido como std::errc::result_out_of_range .Apoiamos o Visual Studio, MacOS, Linux, FreeBSD. Apoiamos grandes e pequenos endianos. Apoiamos sistemas de 32 e 64 bits.
Assumimos que o modo de arredondamento está definido como mais próximo ( std::fegetround() == FE_TONEAREST ).
Você também pode analisar tipos inteiros usando bases diferentes (por exemplo, 2, 10, 16). O código a seguir imprimirá o número 22250738585072012 três vezes:
# 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;
} Ao analisar os valores do ponto flutuante, os números às vezes podem ser muito pequenos (por exemplo, 1e-1000 ) ou muito grandes (por exemplo, 1e1000 ). O idioma C estabeleceu o precedente de que esses pequenos valores estão fora de alcance. Nesses casos, é habitual analisar pequenos valores a zero e grandes valores ao infinito. Esse é o comportamento da linguagem C (por exemplo, stdtod ). Esse é o comportamento seguido pela biblioteca fast_float.
Especificamente, seguimos a interpretação de Jonathan Wakely do padrão:
De qualquer forma, o valor resultante é um dos no máximo dois valores de ponto flutuante mais próximos do valor da string que corresponde ao padrão.
É também a abordagem adotada pela biblioteca Microsoft C ++.
Portanto, temos os seguintes exemplos:
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() Os usuários que desejam que o valor seja deixado não modificado, dado std::errc::result_out_of_range pode fazê -lo adicionando duas linhas 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; } Em C ++ 20, você pode usar fast_float::from_chars para analisar strings em tempo de compilação, como no exemplo a seguir:
// 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 " );
} A biblioteca também suporta tipos de ponto flutuante de largura fixa, como std::float32_t e std::float64_t . Por exemplo, você pode escrever:
std:: float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);Também apoiamos as entradas UTF-16 e UTF-32, bem como ASCII/UTF-8, como no exemplo a seguir:
# 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;
} O padrão C ++ estipula que from_chars deve ser independente do local. Em particular, o separador decimal deve ser o período ( . ). No entanto, alguns usuários ainda desejam usar a biblioteca fast_float com de maneira dependente de localidade. Usando uma função separada chamada from_chars_advanced , permitimos que os usuários passem uma instância parse_options que contém um separador decimal personalizado (por exemplo, a vírgula). Você pode usá -lo da seguinte maneira.
# 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 padrão, o formato JSON não 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;
} Você pode permitir isso com uma variante json_or_infnan fora do padrão:
# 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;
}A biblioteca fast_float faz parte de:
from_chars no GCC depende do FAST_FLOAT,O algoritmo Fastfloat faz parte das bibliotecas padrão LLVM. Há uma parte de implementação derivada de Adacore.
A biblioteca fast_float fornece um desempenho semelhante ao da biblioteca fast_double_parser, mas usando um algoritmo atualizado reformulado do início e oferece uma API mais alinhada com as expectativas dos programadores C ++. A biblioteca fast_double_parser faz parte da estrutura de aprendizado de máquina Microsoft LightGBM.
rcppfastfloat .fast-float-rust .FastDoubleParser . Utilizou sistemas importantes, como Jackson.csFastFloat . Ele pode analisar números aleatórios de ponto flutuante a uma velocidade de 1 GB/s em alguns sistemas. Descobrimos que geralmente é duas vezes mais rápido que o melhor concorrente disponível e muitas vezes mais rápido que muitas implementações da biblioteca padrão.
$ ./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 nosso código de benchmarking.
Esta biblioteca é somente para cabeçalho por design. O arquivo cMake fornece o destino fast_float , que é apenas um ponteiro para o diretório include .
Se você soltar o repositório fast_float em seu projeto CMake, poderá usá -lo dessa maneira:
add_subdirectory (fast_float)
target_link_libraries (myprogram PUBLIC fast_float)Ou você pode recuperar a dependência automaticamente se tiver uma versão suficientemente recente do cmake (3.11 ou melhor, pelo 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) Você deve alterar a linha GIT_TAG para recuperar a versão que deseja usar.
Você também pode usar o CPM, assim:
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v6.1.6) O script script/amalgamate.py pode ser usado para gerar uma única versão do cabeçalho da biblioteca, se desejar. Basta executar o script do diretório raiz deste repositório. Você pode personalizar o tipo de licença e o arquivo de saída, se desejado, conforme descrito na ajuda da linha de comando.
Você pode baixar diretamente arquivos de cabeça única gerados automaticamente:
https://github.com/fastfloat/fast_float/releases/download/v7.0.0/fast_float.h
fast_float-devel ). Embora este trabalho seja inspirado por muitas pessoas diferentes, esse trabalho se beneficiou especialmente de trocas com Michael Eisel, que motivou a pesquisa original com suas principais idéias e com Nigel Tao, que forneceu feedback inestimável. Rémy Oudompheng implementou pela primeira vez um caminho rápido que usamos no caso de dígitos longos.
A biblioteca inclui o código adaptado do Google Wuffs (escrito por Nigel Tao), que foi originalmente publicado sob a licença Apache 2.0.