La bibliothèque FAST_FLOAT fournit des implémentations en en-tête rapide uniquement pour les fonctions C ++ From_Chars pour les types float et double ainsi que des types entiers. Ces fonctions convertissent les chaînes ASCII représentant des valeurs décimales (par exemple, 1.3e10 ) en types binaires. Nous fournissons un arrondissement exact (y compris le rond à même). D'après notre expérience, ces fonctions fast_float sont plusieurs fois plus rapides que les fonctions comparables de pari de nombres à partir de bibliothèques standard C ++ existantes.
Plus précisément, fast_float fournit les deux fonctions suivantes pour analyser les nombres à virgule flottante avec une syntaxe de type C ++ 17 (la bibliothèque elle-même ne nécessite que 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, ...);Vous pouvez également analyser les types entiers:
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, ...); Le type de retour ( from_chars_result ) est défini comme la structure:
struct from_chars_result {
char const *ptr;
std::errc ec;
}; Il analyse la séquence de caractères [first, last) pour un nombre. Il analyse les nombres à virgule flottante s'attendant à un format indépendant des paramètres régionaux équivalent à la fonction C ++ 17 de_chars. La valeur à virgule flottante résultante est les valeurs à virgule flottante les plus proches (en utilisant float ou double ), en utilisant la convention "Round to même" pour des valeurs qui autrement tomberaient à droite entre deux valeurs. Autrement dit, nous fournissons l'analyse exacte selon la norme IEEE.
Compte tenu d'une analyse réussie, le pointeur ( ptr ) dans la valeur renvoyée est défini juste après le numéro analysé, et la value référencée est définie sur la valeur analysée. En cas d'erreur, l' ec renvoyé contient une erreur représentative, sinon la valeur par défaut ( std::errc() ) est stockée.
L'implémentation ne lance pas et n'attribue pas la mémoire (par exemple, avec new ou malloc ).
Il analysera les valeurs Infinity et NAN.
Exemple:
# 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;
}Vous pouvez analyser les numéros délimités:
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. Comme la norme C ++ 17, les fonctions fast_float::from_chars prennent un dernier argument facultatif du type fast_float::chars_format . C'est une valeur de bits: nous vérifions si fmt & fast_float::chars_format::fixed et fmt & fast_float::chars_format::scientific est défini pour déterminer si nous permettons respectivement le point fixe et la notation scientifique. La valeur par défaut est fast_float::chars_format::general qui permet à la fois fixed et scientific .
La bibliothèque cherche à suivre la spécification C ++ 17 (voir 28.2.3. (6.1)).
from_chars ne saute pas les caractères d'espace blanc de premier plan (sauf si fast_float::chars_format::chars_format est défini).+ est interdit (sauf si fast_float::chars_format::skip_white_space est défini).float et double ). Nous recherchons la valeur la plus proche. Nous tournons à une mantissa uniforme lorsque nous sommes entre deux nombres binaires à virgule flottante.De plus, nous avons les restrictions suivantes:
float et double , mais pas long double . Nous prenons également en charge les types de points flottants à largeur fixe tels que std::float32_t et std::float64_t .1e9999 ), nous la représentons en utilisant l'infini ou la valeur infini négative et l' ec renvoyé est défini sur std::errc::result_out_of_range .Nous prenons en charge Visual Studio, MacOS, Linux, FreeBSD. Nous soutenons Big et Little Endian. Nous prenons en charge les systèmes 32 bits et 64 bits.
Nous supposons que le mode d'arrondi est défini sur le plus proche ( std::fegetround() == FE_TONEAREST ).
Vous pouvez également analyser les types entiers en utilisant différentes bases (par exemple, 2, 10, 16). Le code suivant imprimera le numéro 22250738585072012 trois fois:
# 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;
} Lors de l'analyse des valeurs à virgule flottante, les nombres peuvent parfois être trop petits (par exemple, 1e-1000 ) ou trop grands (par exemple, 1e1000 ). La langue C a établi le précédent que ces petites valeurs sont hors de portée. Dans de tels cas, il est habituel d'analyser de petites valeurs à zéro et de grandes valeurs à l'infini. C'est le comportement de la langue C (par exemple, stdtod ). C'est le comportement suivi de la bibliothèque fast_float.
Plus précisément, nous suivons l'interprétation de Jonathan Wakely de la norme:
Dans tous les cas, la valeur résultante est l'une des deux valeurs à point flottantes au plus la plus proche de la valeur de la chaîne correspondant au modèle.
C'est également l'approche adoptée par la bibliothèque Microsoft C ++.
Par conséquent, nous avons les exemples suivants:
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() Les utilisateurs qui souhaitent que la valeur ne soit pas modifiée étant donné std::errc::result_out_of_range peut le faire en ajoutant deux lignes de code:
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; } Dans C ++ 20, vous pouvez utiliser fast_float::from_chars pour analyser les chaînes au moment de la compilation, comme dans l'exemple suivant:
// 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 bibliothèque prend également en charge les types de points flottants à largeur fixe tels que std::float32_t et std::float64_t . Par exemple, vous pouvez écrire:
std:: float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);Nous prenons également en charge les entrées UTF-16 et UTF-32, ainsi que ASCII / UTF-8, comme dans l'exemple suivant:
# 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;
} La norme C ++ stipule que from_chars doit être indépendante des paramètres régionaux. En particulier, le séparateur décimal doit être la période ( . ). Cependant, certains utilisateurs souhaitent toujours utiliser la bibliothèque fast_float avec une manière dépendante des paramètres régionaux. À l'aide d'une fonction distincte appelée from_chars_advanced , nous permettons aux utilisateurs de passer une instance parse_options qui contient un séparateur décimal personnalisé (par exemple, la virgule). Vous pouvez l'utiliser comme suit.
# 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;
} Par défaut, le format JSON n'autorise pas 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;
} Vous pouvez l'autoriser avec une variante json_or_infnan non standard:
# 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 bibliothèque fast_float fait partie de:
from_chars dans GCC repose sur Fast_float,L'algorithme FastFloat fait partie des bibliothèques standard LLVM. Il y a une partie de mise en œuvre dérivée d'Adacore.
La bibliothèque FAST_FLOAT fournit une performance similaire à celle de la bibliothèque Fast_Double_Parser, mais en utilisant un algorithme mis à jour retravaillé à partir de zéro, et tout en offrant une API plus conforme aux attentes des programmeurs C ++. La bibliothèque FAST_DOUBLE_PARSER fait partie du cadre d'apprentissage de la machine Microsoft LightGBM.
rcppfastfloat .fast-float-rust .FastDoubleParser . Il a utilisé pour des systèmes importants tels que Jackson.csFastFloat . Il peut analyser les nombres à virgule flottante aléatoires à une vitesse de 1 Go / s sur certains systèmes. Nous constatons qu'il est souvent deux fois plus rapide que le meilleur concurrent disponible, et souvent plus vite que de nombreuses implémentations de bibliothèque standard.
$ ./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/sVoir https://github.com/lemire/simple_fastfloat_benchmark pour notre code de référence.
Cette bibliothèque est uniquement en tête par conception. Le fichier cmake fournit la cible fast_float qui n'est qu'un pointeur vers le répertoire include .
Si vous déposez le référentiel fast_float dans votre projet CMake, vous devriez pouvoir l'utiliser de cette manière:
add_subdirectory (fast_float)
target_link_libraries (myprogram PUBLIC fast_float)Ou vous pouvez peut-être récupérer la dépendance automatiquement si vous avez une version suffisamment récente de CMake (3.11 ou mieux au moins):
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) Vous devez modifier la ligne GIT_TAG afin de récupérer la version que vous souhaitez utiliser.
Vous pouvez également utiliser CPM, comme ainsi:
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v6.1.6) Le script/amalgamate.py peut être utilisé pour générer une version d'en-tête unique de la bibliothèque si vous le souhaitez. Exécutez simplement le script à partir du répertoire racine de ce référentiel. Vous pouvez personnaliser le type de licence et le fichier de sortie si vous le souhaitez comme décrit dans l'aide de la ligne de commande.
Vous pouvez télécharger directement des fichiers à tête unique générés automatiquement:
https://github.com/fastfloat/fast_float/releases/download/v7.0.0/fast_float.h
fast_float-devel ). Bien que ce travail soit inspiré par de nombreuses personnes différentes, ce travail a bénéficié en particulier des échanges avec Michael Eisel, qui a motivé la recherche originale avec ses idées clés, et avec Nigel Tao qui a fourni des commentaires inestimables. Rémy Oudompheng a d'abord mis en œuvre un chemin rapide que nous utilisons dans le cas de longs chiffres.
La bibliothèque comprend du code adapté de Google Wuffs (écrit par Nigel Tao) qui a été initialement publié sous la licence Apache 2.0.