ไลบรารี FAST_FLOAT ให้การใช้งานส่วนหัวอย่างรวดเร็วอย่างเดียวสำหรับฟังก์ชัน C ++ FROM_CHARS สำหรับประเภท float และ double ประเภทรวมถึงประเภทจำนวนเต็ม ฟังก์ชั่นเหล่านี้แปลงสตริง ASCII แสดงถึงค่าทศนิยม (เช่น 1.3e10 ) เป็นประเภทไบนารี เราให้การปัดเศษที่แน่นอน (รวมถึงรอบแม้) จากประสบการณ์ของเราฟังก์ชั่น fast_float เหล่านี้เร็วกว่าฟังก์ชั่นการแยกตัวเลขจำนวนมากจากไลบรารีมาตรฐาน C ++ ที่มีอยู่
โดยเฉพาะ fast_float ให้ฟังก์ชั่นสองฟังก์ชั่นต่อไปนี้เพื่อแยกวิเคราะห์หมายเลขจุดลอยตัวด้วยไวยากรณ์ C ++ 17-like (ไลบรารีเองต้องใช้ 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:
struct from_chars_result {
char const *ptr;
std::errc ec;
}; มันแยกวิเคราะห์ลำดับอักขระ [first, last) สำหรับตัวเลข มันแยกวิเคราะห์ตัวเลขจุดลอยตัวคาดว่าจะมีรูปแบบที่ไม่ขึ้นกับสถานที่เทียบเท่ากับฟังก์ชัน C ++ 17 From_Chars ค่าจุดลอยตัวที่ได้คือค่าจุดลอยตัวที่ใกล้เคียงที่สุด (โดยใช้ float หรือ double ) โดยใช้การประชุม "กลมถึงแม้กระทั่ง" สำหรับค่าที่จะลดลงในระหว่างสองค่า นั่นคือเราให้การแยกวิเคราะห์ที่แน่นอนตามมาตรฐาน IEEE
ด้วยการแยกวิเคราะห์ที่ประสบความสำเร็จตัวชี้ ( ptr ) ในค่าที่ส่งคืนถูกตั้งค่าเป็นจุดขวาหลังจากหมายเลขที่แยกวิเคราะห์และ value ที่อ้างอิงถูกตั้งค่าเป็นค่าที่แยกวิเคราะห์ ในกรณีที่เกิดข้อผิดพลาด ec ที่ส่งคืนจะมีข้อผิดพลาดตัวแทนมิฉะนั้นค่าเริ่มต้น ( std::errc() ) จะถูกเก็บไว้
การใช้งานไม่ได้โยนและไม่จัดสรรหน่วยความจำ (เช่น new หรือ malloc )
มันจะแยกวิเคราะห์อินฟินิตี้และค่าน่าน
ตัวอย่าง:
# 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 ใช้อาร์กิวเมนต์สุดท้ายของ TYPE fast_float::chars_format มันเป็นค่า bitset: เราตรวจสอบว่า fmt & fast_float::chars_format::fixed และ 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_t1e9999 ) เราเป็นตัวแทนโดยใช้ค่าอินฟินิตี้หรืออินฟินิตี้เชิงลบและ ec ที่ส่งคืนถูกตั้งค่าเป็น std::errc::result_out_of_rangeเราสนับสนุน Visual Studio, MacOS, Linux, FreeBSD เราสนับสนุน Endian ขนาดใหญ่และน้อย เราสนับสนุนระบบ 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 คุณสามารถใช้ 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 ใน GCC อาศัย Fast_floatอัลกอริทึม FastFloat เป็นส่วนหนึ่งของไลบรารีมาตรฐาน LLVM มีการใช้งานส่วนหนึ่งของ Adacore
ไลบรารี FAST_FLOAT ให้ประสิทธิภาพคล้ายกับไลบรารี FAST_DOUBLE_PARSER แต่ใช้อัลกอริทึมที่ได้รับการปรับปรุงใหม่จากพื้นดินและในขณะที่เสนอ API ให้สอดคล้องกับความคาดหวังของโปรแกรมเมอร์ C ++ ไลบรารี FAST_DOUBLE_PARSER เป็นส่วนหนึ่งของเฟรมเวิร์ก Microsoft LightGBM Machine-Learning
rcppfastfloatfast-float-rustFastDoubleParser มันใช้สำหรับระบบสำคัญเช่นแจ็คสัน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/fast_float.h
fast_float-devel ) แม้ว่างานนี้จะได้รับแรงบันดาลใจจากผู้คนมากมาย แต่งานนี้ได้รับประโยชน์โดยเฉพาะอย่างยิ่งจากการแลกเปลี่ยนกับ Michael Eisel ซึ่งเป็นแรงจูงใจในการวิจัยดั้งเดิมด้วยข้อมูลเชิงลึกที่สำคัญของเขาและกับไนเจลเทา Rémy Oudompheng ใช้เส้นทางที่รวดเร็วที่เราใช้ในกรณีของตัวเลขยาว
ห้องสมุดรวมถึงรหัสที่ดัดแปลงมาจาก Google Wuffs (เขียนโดย Nigel Tao) ซึ่งเผยแพร่ครั้งแรกภายใต้ใบอนุญาต Apache 2.0