From fde0116d4d0a79f90aa661d9a163b05b0579a2dd Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Mon, 27 Mar 2023 13:53:18 -0400 Subject: [PATCH 01/10] New version. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1da5f48..d1b9e98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.9) -project(fast_float VERSION 3.10.0 LANGUAGES CXX) +project(fast_float VERSION 3.11.0 LANGUAGES CXX) option(FASTFLOAT_TEST "Enable tests" OFF) if(FASTFLOAT_TEST) enable_testing() From bfee511d78afb329f7873821758a5d5c4f8adbe4 Mon Sep 17 00:00:00 2001 From: Adam Lugowski Date: Tue, 28 Mar 2023 22:13:56 -0700 Subject: [PATCH 02/10] Set errc::result_out_of_range on over/underflow Best-effort values are still returned, such as 0 for underflow and infinity for overflow, but now the returned ec is set to std::errc::result_out_of_range instead of std::errc(). --- README.md | 2 +- include/fast_float/parse_number.h | 4 ++ tests/basictest.cpp | 96 ++++++++++++++++--------------- tests/string_test.cpp | 3 +- 4 files changed, 58 insertions(+), 47 deletions(-) diff --git a/README.md b/README.md index 63c2c8c..10cd01b 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/char Furthermore, we have the following restrictions: * We only support `float` and `double` types at this time. * We only support the decimal format: we do not support hexadecimal strings. -* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value. +* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`. We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index d16a25d..6e4f6eb 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -214,6 +214,10 @@ from_chars_result from_chars_advanced(const char *first, const char *last, // then we need to go the long way around again. This is very uncommon. if(am.power2 < 0) { am = digit_comp(pns, am); } to_float(pns.negative, am, value); + // Test for over/underflow. + if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format::infinite_power()) { + answer.ec = std::errc::result_out_of_range; + } return answer; } diff --git a/tests/basictest.cpp b/tests/basictest.cpp index 3ed10f0..ff55920 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -286,11 +286,11 @@ bool check_file(std::string file_name) { // Parse as 32-bit float float parsed_32; auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32); - if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } + if(fast_float_r32.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) {std::cerr << "32-bit fast_float parsing failure for: " + str + "\n"; return false; } // Parse as 64-bit float double parsed_64; auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64); - if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } + if(fast_float_r64.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) { std::cerr << "64-bit fast_float parsing failure: " + str + "\n"; return false; } // Convert the floats to unsigned ints. uint32_t float32_parsed; uint64_t float64_parsed; @@ -592,8 +592,9 @@ TEST_CASE("powers_of_ten") { REQUIRE(n < sizeof(buf)); // if false, fails the test and exits double actual; auto result = fast_float::from_chars(buf, buf + 1000, actual); - CHECK_MESSAGE(result.ec == std::errc(), " I could not parse " << buf); double expected = ((i >= -307) ? testing_power_of_ten[i + 307] : std::pow(10, i)); + auto expected_ec = (expected == 0 || std::isinf(expected)) ? std::errc::result_out_of_range : std::errc(); + CHECK_MESSAGE(result.ec == expected_ec, " I could not parse " << buf); CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual); } } @@ -643,7 +644,7 @@ enum class Diag { runtime, comptime }; template constexpr void check_basic_test_result(std::string_view str, fast_float::from_chars_result result, - T actual, T expected) { + T actual, T expected, std::errc expected_ec) { if constexpr (diag == Diag::runtime) { INFO( "str=" << str << "\n" @@ -689,7 +690,7 @@ constexpr void check_basic_test_result(std::string_view str, return x != x; }; - FASTFLOAT_CHECK_EQ(result.ec, std::errc()); + FASTFLOAT_CHECK_EQ(result.ec, expected_ec); FASTFLOAT_CHECK_EQ(result.ptr, str.data() + str.size()); FASTFLOAT_CHECK_EQ(copysign(1, actual), copysign(1, expected)); FASTFLOAT_CHECK_EQ(isnan(actual), isnan(expected)); @@ -699,17 +700,24 @@ constexpr void check_basic_test_result(std::string_view str, } template -constexpr void basic_test(std::string_view str, T expected) { +constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec = std::errc()) { T actual; auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual); - check_basic_test_result(str, result, actual, expected); + check_basic_test_result(str, result, actual, expected, expected_ec); } template constexpr void basic_test(std::string_view str, T expected, fast_float::parse_options options) { T actual; auto result = fast_float::from_chars_advanced(str.data(), str.data() + str.size(), actual, options); - check_basic_test_result(str, result, actual, expected); + check_basic_test_result(str, result, actual, expected, std::errc()); +} + +template +constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec, fast_float::parse_options options) { + T actual; + auto result = fast_float::from_chars_advanced(str.data(), str.data() + str.size(), actual, options); + check_basic_test_result(str, result, actual, expected, expected_ec); } void basic_test(float val) { @@ -725,29 +733,27 @@ void basic_test(float val) { } } -#define verify_runtime(lhs, rhs) \ +#define verify_runtime(...) \ do { \ - INFO(lhs); \ - basic_test(lhs, rhs); \ + basic_test(__VA_ARGS__); \ } while (false) -#define verify_comptime(lhs, rhs) \ +#define verify_comptime(...) \ do { \ constexpr int verify_comptime_var = \ - (basic_test(lhs, rhs), 0); \ + (basic_test(__VA_ARGS__), 0); \ (void)verify_comptime_var; \ } while (false) -#define verify_options_runtime(lhs, rhs) \ +#define verify_options_runtime(...) \ do { \ - INFO(lhs); \ - basic_test(lhs, rhs, options); \ + basic_test(__VA_ARGS__, options); \ } while (false) -#define verify_options_comptime(lhs, rhs) \ +#define verify_options_comptime(...) \ do { \ constexpr int verify_options_comptime_var = \ - (basic_test(lhs, rhs, options), 0); \ + (basic_test(__VA_ARGS__, options), 0); \ (void)verify_options_comptime_var; \ } while (false) @@ -756,16 +762,16 @@ void basic_test(float val) { #error "from_chars must be constexpr for constexpr tests" #endif -#define verify(lhs, rhs) \ +#define verify(...) \ do { \ - verify_runtime(lhs, rhs); \ - verify_comptime(lhs, rhs); \ + verify_runtime(__VA_ARGS__); \ + verify_comptime(__VA_ARGS__); \ } while (false) -#define verify_options(lhs, rhs) \ +#define verify_options(...) \ do { \ - verify_options_runtime(lhs, rhs); \ - verify_options_comptime(lhs, rhs); \ + verify_options_runtime(__VA_ARGS__); \ + verify_options_comptime(__VA_ARGS__); \ } while (false) #else @@ -784,19 +790,19 @@ TEST_CASE("64bit.inf") { verify("-infinity", -std::numeric_limits::infinity()); verify("inf", std::numeric_limits::infinity()); verify("-inf", -std::numeric_limits::infinity()); - verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity()); - verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits::infinity()); - verify("1.8e308", std::numeric_limits::infinity()); - verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity()); - verify("2e30000000000000000", std::numeric_limits::infinity()); - verify("2e3000", std::numeric_limits::infinity()); - verify("1.9e308", std::numeric_limits::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("1.8e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("2e30000000000000000", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("1.9e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); } TEST_CASE("64bit.general") { verify("22250738585072012e-324",0x1p-1022); /* limit between normal and subnormal*/ verify("-22250738585072012e-324",-0x1p-1022); /* limit between normal and subnormal*/ - verify("-1e-999",-0.0); + verify("-1e-999", -0.0, std::errc::result_out_of_range); verify("-2.2222222222223e-322",-0x1.68p-1069); verify("9007199254740993.0", 0x1p+53); verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328); @@ -852,7 +858,7 @@ TEST_CASE("64bit.general") { verify("45035996.273704985", 45035996.273704985); verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375); verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); - verify("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits::infinity()); + verify("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("-2240084132271013504.131248280843119943687942846658579428", -0x1.f1660a65b00bfp+60); } @@ -864,11 +870,11 @@ TEST_CASE("64bit.decimal_point") { }(); // infinities - verify_options("1,8e308", std::numeric_limits::infinity()); - verify_options("1,832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity()); - verify_options("2e30000000000000000", std::numeric_limits::infinity()); - verify_options("2e3000", std::numeric_limits::infinity()); - verify_options("1,9e308", std::numeric_limits::infinity()); + verify_options("1,8e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify_options("1,832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify_options("2e30000000000000000", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify_options("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify_options("1,9e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); // finites verify_options("-2,2222222222223e-322",-0x1.68p-1069); @@ -928,13 +934,13 @@ TEST_CASE("32bit.inf") { verify("-infinity", -std::numeric_limits::infinity()); verify("inf", std::numeric_limits::infinity()); verify("-inf", -std::numeric_limits::infinity()); - verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity()); - verify("2e3000", std::numeric_limits::infinity()); - verify("3.5028234666e38", std::numeric_limits::infinity()); + verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); + verify("3.5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); } TEST_CASE("32bit.general") { - verify("-1e-999",-0.0f); + verify("-1e-999", -0.0f, std::errc::result_out_of_range); verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f); verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f); verify_runtime(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f); @@ -1000,7 +1006,7 @@ TEST_CASE("32bit.general") { verify("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); verify("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); verify("1", 1.f); - verify("7.0060e-46", 0.f); + verify("7.0060e-46", 0.f, std::errc::result_out_of_range); verify("3.4028234664e38", 0x1.fffffep+127f); verify("3.4028234665e38", 0x1.fffffep+127f); verify("3.4028234666e38", 0x1.fffffep+127f); @@ -1018,7 +1024,7 @@ TEST_CASE("32bit.decimal_point") { }(); // infinity - verify_options("3,5028234666e38", std::numeric_limits::infinity()); + verify_options("3,5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); // finites verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f); @@ -1043,7 +1049,7 @@ TEST_CASE("32bit.decimal_point") { verify_options("3,1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); verify_options("2,3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); verify_options("1", 1.f); - verify_options("7,0060e-46", 0.f); + verify_options("7,0060e-46", 0.f, std::errc::result_out_of_range); verify_options("3,4028234664e38", 0x1.fffffep+127f); verify_options("3,4028234665e38", 0x1.fffffep+127f); verify_options("3,4028234666e38", 0x1.fffffep+127f); diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 78da51e..4329dae 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -62,6 +62,7 @@ template bool test() { std::string input = "0.1 1e1000 100000 3.14159265359 -1e-500 001 1e01 1e0000001 -inf"; std::vector answers = {T(0.1), std::numeric_limits::infinity(), 100000, T(3.14159265359), -0.0, 1, 10, 10, -std::numeric_limits::infinity()}; + std::vector expected_ec = {std::errc(), std::errc::result_out_of_range, std::errc(), std::errc(), std::errc::result_out_of_range, std::errc(), std::errc(), std::errc(), std::errc()}; const char * begin = input.data(); const char * end = input.data() + input.size(); for(size_t i = 0; i < answers.size(); i++) { @@ -69,7 +70,7 @@ bool test() { while((begin < end) && (std::isspace(*begin))) { begin++; } auto result = fast_float::from_chars(begin, end, result_value); - if (result.ec != std::errc()) { + if (result.ec != expected_ec[i]) { printf("parsing %.*s\n", int(end - begin), begin); std::cerr << " I could not parse " << std::endl; return false; From 37127b022fc96669d0ee57e79565335b2e2d576f Mon Sep 17 00:00:00 2001 From: Adam Lugowski Date: Thu, 30 Mar 2023 13:14:35 -0700 Subject: [PATCH 03/10] Add subnormal numbers to powers of ten table On some platforms std::pow returns 0 instead of a subnormal number with `-ffast-math -O2` compiler options. --- tests/basictest.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index ff55920..d677028 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -509,6 +509,9 @@ TEST_CASE("test_fixed_only") { static const double testing_power_of_ten[] = { + 1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316, 1e-315, + 1e-314, 1e-313, 1e-312, 1e-311, 1e-310, 1e-309, 1e-308, + 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, @@ -592,7 +595,7 @@ TEST_CASE("powers_of_ten") { REQUIRE(n < sizeof(buf)); // if false, fails the test and exits double actual; auto result = fast_float::from_chars(buf, buf + 1000, actual); - double expected = ((i >= -307) ? testing_power_of_ten[i + 307] : std::pow(10, i)); + double expected = ((i >= -323) ? testing_power_of_ten[i + 323] : std::pow(10, i)); auto expected_ec = (expected == 0 || std::isinf(expected)) ? std::errc::result_out_of_range : std::errc(); CHECK_MESSAGE(result.ec == expected_ec, " I could not parse " << buf); CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual); From fbd5bd712e44b57b6b98d3117482909839f9320f Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Thu, 30 Mar 2023 18:37:48 -0400 Subject: [PATCH 04/10] Bumping version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d1b9e98..727d135 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.9) -project(fast_float VERSION 3.11.0 LANGUAGES CXX) +project(fast_float VERSION 4.0.0 LANGUAGES CXX) option(FASTFLOAT_TEST "Enable tests" OFF) if(FASTFLOAT_TEST) enable_testing() From ca90e364130257527b9c3ca10b895e5e02085e27 Mon Sep 17 00:00:00 2001 From: Adam Lugowski Date: Fri, 31 Mar 2023 17:09:36 -0700 Subject: [PATCH 05/10] Fix subnormal test fails on GCC 9 --- tests/basictest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/basictest.cpp b/tests/basictest.cpp index d677028..1f8d5d1 100644 --- a/tests/basictest.cpp +++ b/tests/basictest.cpp @@ -596,7 +596,7 @@ TEST_CASE("powers_of_ten") { double actual; auto result = fast_float::from_chars(buf, buf + 1000, actual); double expected = ((i >= -323) ? testing_power_of_ten[i + 323] : std::pow(10, i)); - auto expected_ec = (expected == 0 || std::isinf(expected)) ? std::errc::result_out_of_range : std::errc(); + auto expected_ec = (i < -323 || i > 308) ? std::errc::result_out_of_range : std::errc(); CHECK_MESSAGE(result.ec == expected_ec, " I could not parse " << buf); CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual); } From ca43e6722e2d38396506aec85f05adc785a83c77 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Sun, 2 Apr 2023 17:43:17 -0400 Subject: [PATCH 06/10] We need to update some of our exhaustive tests to the new API --- .github/workflows/ubuntu22-clang.yml | 2 +- .github/workflows/ubuntu22-gcc12.yml | 2 +- .github/workflows/ubuntu22.yml | 2 +- README.md | 10 +++++----- include/fast_float/decimal_to_binary.h | 4 ++-- script/amalgamate.py | 6 +++--- script/mushtak_lemire.py | 8 ++++---- tests/example_comma_test.cpp | 2 +- tests/exhaustive32.cpp | 7 +++++-- tests/exhaustive32_64.cpp | 6 +++--- tests/exhaustive32_midpoint.cpp | 13 ++++++++----- tests/installation_tests/find/CMakeLists.txt | 2 +- tests/long_exhaustive32.cpp | 7 +++++-- tests/long_exhaustive32_64.cpp | 7 +++++-- tests/long_random64.cpp | 11 +++++++---- tests/long_test.cpp | 4 ++-- tests/powersoffive_hardround.cpp | 2 +- tests/random64.cpp | 11 +++++++---- tests/random_string.cpp | 11 ++++++++--- tests/short_random_string.cpp | 11 ++++++++--- tests/string_test.cpp | 6 +++--- 21 files changed, 81 insertions(+), 53 deletions(-) diff --git a/.github/workflows/ubuntu22-clang.yml b/.github/workflows/ubuntu22-clang.yml index d7fd9ee..14cf763 100644 --- a/.github/workflows/ubuntu22-clang.yml +++ b/.github/workflows/ubuntu22-clang.yml @@ -22,4 +22,4 @@ jobs: cd build20 && CXX=clang++-14 cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. && cmake --build . && - ctest --output-on-failure \ No newline at end of file + ctest --output-on-failure \ No newline at end of file diff --git a/.github/workflows/ubuntu22-gcc12.yml b/.github/workflows/ubuntu22-gcc12.yml index a4b6f21..666888a 100644 --- a/.github/workflows/ubuntu22-gcc12.yml +++ b/.github/workflows/ubuntu22-gcc12.yml @@ -20,4 +20,4 @@ jobs: cd build20 && CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. && cmake --build . && - ctest --output-on-failure \ No newline at end of file + ctest --output-on-failure \ No newline at end of file diff --git a/.github/workflows/ubuntu22.yml b/.github/workflows/ubuntu22.yml index 367cb0a..2ba4769 100644 --- a/.github/workflows/ubuntu22.yml +++ b/.github/workflows/ubuntu22.yml @@ -13,4 +13,4 @@ jobs: cd build && cmake -DFASTFLOAT_TEST=ON .. && cmake --build . && - ctest --output-on-failure \ No newline at end of file + ctest --output-on-failure \ No newline at end of file diff --git a/README.md b/README.md index 10cd01b..0b57a5b 100644 --- a/README.md +++ b/README.md @@ -189,11 +189,11 @@ It can parse random floating-point numbers at a speed of 1 GB/s on some systems. $ ./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 +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 ``` See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code. diff --git a/include/fast_float/decimal_to_binary.h b/include/fast_float/decimal_to_binary.h index 6d0f730..fec916f 100644 --- a/include/fast_float/decimal_to_binary.h +++ b/include/fast_float/decimal_to_binary.h @@ -48,9 +48,9 @@ namespace detail { * where * p = log(5**q)/log(2) = q * log(5)/log(2) * - * For negative values of q in (-400,0), we have that + * For negative values of q in (-400,0), we have that * f = (((152170 + 65536) * q ) >> 16); - * is equal to + * is equal to * -ceil(p) + q * where * p = log(5**-q)/log(2) = -q * log(5)/log(2) diff --git a/script/amalgamate.py b/script/amalgamate.py index 7ef44d4..c0f078d 100644 --- a/script/amalgamate.py +++ b/script/amalgamate.py @@ -31,7 +31,7 @@ for filename in ['LICENSE-MIT', 'LICENSE-APACHE']: processed_files[filename] = text # code -for filename in [ 'constexpr_feature_detect.h', 'fast_float.h', 'float_common.h', 'ascii_number.h', +for filename in [ 'constexpr_feature_detect.h', 'fast_float.h', 'float_common.h', 'ascii_number.h', 'fast_table.h', 'decimal_to_binary.h', 'bigint.h', 'ascii_number.h', 'digit_comparison.h', 'parse_number.h']: with open('include/fast_float/' + filename, encoding='utf8') as f: @@ -73,10 +73,10 @@ def license_content(license_arg): return result text = ''.join([ - processed_files['AUTHORS'], processed_files['CONTRIBUTORS'], + processed_files['AUTHORS'], processed_files['CONTRIBUTORS'], *license_content(args.license), processed_files['constexpr_feature_detect.h'], - processed_files['fast_float.h'], processed_files['float_common.h'], + processed_files['fast_float.h'], processed_files['float_common.h'], processed_files['ascii_number.h'], processed_files['fast_table.h'], processed_files['decimal_to_binary.h'], processed_files['bigint.h'], processed_files['ascii_number.h'], processed_files['digit_comparison.h'], diff --git a/script/mushtak_lemire.py b/script/mushtak_lemire.py index 14bf11f..5b98fda 100644 --- a/script/mushtak_lemire.py +++ b/script/mushtak_lemire.py @@ -1,12 +1,12 @@ # -# Reference : +# Reference : # Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear) # all_tqs = [] # Generates all possible values of T[q] -# Appendix B of Number parsing at a gigabyte per second. +# Appendix B of Number parsing at a gigabyte per second. # Software: Practice and Experience 2021;51(8):1700–1727. for q in range(-342, -27): power5 = 5**-q @@ -44,9 +44,9 @@ def continued_fraction(numer, denom): numer, denom = denom, rem return cf -# Given a continued fraction [a0; a1, a2, ..., an], returns +# Given a continued fraction [a0; a1, a2, ..., an], returns # all the convergents of that continued fraction -# as pairs of the form (numer, denom), where numer/denom is +# as pairs of the form (numer, denom), where numer/denom is # a convergent of the continued fraction in simple form. def convergents(cf): p_n_minus_2 = 0 diff --git a/tests/example_comma_test.cpp b/tests/example_comma_test.cpp index 12f488d..aa19735 100644 --- a/tests/example_comma_test.cpp +++ b/tests/example_comma_test.cpp @@ -3,7 +3,7 @@ #include #include #include - + int main() { const std::string input = "3,1416 xyz "; double result; diff --git a/tests/exhaustive32.cpp b/tests/exhaustive32.cpp index 9e1e42f..bddde23 100644 --- a/tests/exhaustive32.cpp +++ b/tests/exhaustive32.cpp @@ -30,7 +30,10 @@ void allvalues() { const char *string_end = to_string(v, buffer); float result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { + // Starting with version 4.0 for fast_float, we return result_out_of_range if the + // value is either too small (too close to zero) or too large (effectively infinity). + // So std::errc::result_out_of_range is normal for well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; abort(); } @@ -46,7 +49,7 @@ void allvalues() { } else if (result_value != v) { std::cerr << "no match ? " << buffer << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; abort(); } diff --git a/tests/exhaustive32_64.cpp b/tests/exhaustive32_64.cpp index e02757b..0c6285c 100644 --- a/tests/exhaustive32_64.cpp +++ b/tests/exhaustive32_64.cpp @@ -21,7 +21,7 @@ bool basic_test_64bit(std::string vals, double val) { double result_value; auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << " I could not parse " << vals << std::endl; return false; } @@ -30,11 +30,11 @@ bool basic_test_64bit(std::string vals, double val) { std::cerr << vals << std::endl; std::cerr << "not nan" << result_value << std::endl; return false; - } + } } else if(copysign(1,result_value) != copysign(1,val)) { std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val << std::endl; - return false; + return false; } else if (result_value != val) { std::cerr << vals << std::endl; std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val diff --git a/tests/exhaustive32_midpoint.cpp b/tests/exhaustive32_midpoint.cpp index fdfd25d..8b6f0a6 100644 --- a/tests/exhaustive32_midpoint.cpp +++ b/tests/exhaustive32_midpoint.cpp @@ -8,7 +8,7 @@ #include #include -#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) +#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. @@ -73,7 +73,7 @@ bool allvalues() { } uint32_t word = uint32_t(w); memcpy(&v, &word, sizeof(v)); - if(std::isfinite(v)) { + if(std::isfinite(v)) { float nextf = std::nextafterf(v, INFINITY); if(copysign(1,v) != copysign(1,nextf)) { continue; } if(!std::isfinite(nextf)) { continue; } @@ -90,7 +90,10 @@ bool allvalues() { float result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { + // Starting with version 4.0 for fast_float, we return result_out_of_range if the + // value is either too small (too close to zero) or too large (effectively infinity). + // So std::errc::result_out_of_range is normal for well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; return false; } @@ -120,7 +123,7 @@ bool allvalues() { std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; std::cout << "started with " << std::hexfloat << midv << std::endl; std::cout << "round down to " << std::hexfloat << str_answer << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; return false; } @@ -133,7 +136,7 @@ bool allvalues() { inline void Assert(bool Assertion) { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; } -#else +#else if (!Assertion) { throw std::runtime_error("bug"); } #endif } diff --git a/tests/installation_tests/find/CMakeLists.txt b/tests/installation_tests/find/CMakeLists.txt index b2ccd40..9a61879 100644 --- a/tests/installation_tests/find/CMakeLists.txt +++ b/tests/installation_tests/find/CMakeLists.txt @@ -14,7 +14,7 @@ find_package(FastFloat REQUIRED) file(WRITE main.cpp " #include \"fast_float/fast_float.h\" #include - + int main() { const std::string input = \"3.1416 xyz \"; double result; diff --git a/tests/long_exhaustive32.cpp b/tests/long_exhaustive32.cpp index 0a6b53d..9c297bd 100644 --- a/tests/long_exhaustive32.cpp +++ b/tests/long_exhaustive32.cpp @@ -29,7 +29,10 @@ void allvalues() { const char *string_end = to_string(v, buffer); float result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { + // Starting with version 4.0 for fast_float, we return result_out_of_range if the + // value is either too small (too close to zero) or too large (effectively infinity). + // So std::errc::result_out_of_range is normal for well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; abort(); } @@ -46,7 +49,7 @@ void allvalues() { } else if (result_value != v) { std::cerr << "no match ? " << buffer << " got " << result_value << " expected " << v << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; abort(); } diff --git a/tests/long_exhaustive32_64.cpp b/tests/long_exhaustive32_64.cpp index cea8497..b6d9d50 100644 --- a/tests/long_exhaustive32_64.cpp +++ b/tests/long_exhaustive32_64.cpp @@ -28,7 +28,10 @@ void all_32bit_values() { const char *string_end = to_string(v, buffer); double result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { + // Starting with version 4.0 for fast_float, we return result_out_of_range if the + // value is either too small (too close to zero) or too large (effectively infinity). + // So std::errc::result_out_of_range is normal for well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; abort(); } @@ -49,7 +52,7 @@ void all_32bit_values() { } else if (result_value != v) { std::cerr << "no match ? " << buffer << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; abort(); } diff --git a/tests/long_random64.cpp b/tests/long_random64.cpp index 713c125..419414e 100644 --- a/tests/long_random64.cpp +++ b/tests/long_random64.cpp @@ -27,9 +27,9 @@ static fast_float::value128 g_lehmer64_state; * Society 68.225 (1999): 249-260. */ -static inline void lehmer64_seed(uint64_t seed) { +static inline void lehmer64_seed(uint64_t seed) { g_lehmer64_state.high = 0; - g_lehmer64_state.low = seed; + g_lehmer64_state.low = seed; } static inline uint64_t lehmer64() { @@ -56,7 +56,10 @@ void random_values(size_t N) { const char *string_end = to_string(v, buffer); double result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { + // Starting with version 4.0 for fast_float, we return result_out_of_range if the + // value is either too small (too close to zero) or too large (effectively infinity). + // So std::errc::result_out_of_range is normal for well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; errors++; if (errors > 10) { @@ -80,7 +83,7 @@ void random_values(size_t N) { } else if (result_value != v) { std::cerr << "no match ? '" << buffer << "'" << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; errors++; if (errors > 10) { diff --git a/tests/long_test.cpp b/tests/long_test.cpp index 36b9210..85ac0be 100644 --- a/tests/long_test.cpp +++ b/tests/long_test.cpp @@ -22,7 +22,7 @@ bool test() { while((begin < end) && (std::isspace(*begin))) { begin++; } auto result = fast_float::from_chars(begin, end, result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(end - begin), begin); std::cerr << " I could not parse " << std::endl; return false; @@ -40,7 +40,7 @@ bool test() { } if(begin != end) { std::cerr << " bad ending " << std::endl; - return false; + return false; } return true; } diff --git a/tests/powersoffive_hardround.cpp b/tests/powersoffive_hardround.cpp index 09b95bd..7f0903f 100644 --- a/tests/powersoffive_hardround.cpp +++ b/tests/powersoffive_hardround.cpp @@ -105,7 +105,7 @@ bool tester() { double result_value; auto result = fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cout << to_be_parsed << std::endl; std::cerr << " I could not parse " << std::endl; return false; diff --git a/tests/random64.cpp b/tests/random64.cpp index 6b3ef50..4836947 100644 --- a/tests/random64.cpp +++ b/tests/random64.cpp @@ -29,9 +29,9 @@ static fast_float::value128 g_lehmer64_state; * Society 68.225 (1999): 249-260. */ -static inline void lehmer64_seed(uint64_t seed) { +static inline void lehmer64_seed(uint64_t seed) { g_lehmer64_state.high = 0; - g_lehmer64_state.low = seed; + g_lehmer64_state.low = seed; } static inline uint64_t lehmer64() { @@ -59,7 +59,10 @@ void random_values(size_t N) { const char *string_end = to_string(v, buffer); double result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); - if (result.ec != std::errc()) { + // Starting with version 4.0 for fast_float, we return result_out_of_range if the + // value is either too small (too close to zero) or too large (effectively infinity). + // So std::errc::result_out_of_range is normal for well-formed input strings. + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; errors++; if (errors > 10) { @@ -83,7 +86,7 @@ void random_values(size_t N) { } else if (result_value != v) { std::cerr << "no match ? " << buffer << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; - std::cout << "got back " << std::hexfloat << result_value << std::endl; + std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; errors++; if (errors > 10) { diff --git a/tests/random_string.cpp b/tests/random_string.cpp index 8cabf5f..7ecfc6b 100644 --- a/tests/random_string.cpp +++ b/tests/random_string.cpp @@ -101,7 +101,12 @@ size_t build_random_string(RandomEngine &rand, char *buffer) { if (i == size_t(location_of_decimal_separator)) { buffer[pos++] = '.'; } - buffer[pos++] = char(rand.next_digit() + '0'); + buffer[pos] = char(rand.next_digit() + '0'); + // We can have a leading zero only if location_of_decimal_separator = 1. + while(i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') { + buffer[pos] = char(rand.next_digit() + '0'); + } + pos++; } if (rand.next_bool()) { if (rand.next_bool()) { @@ -178,7 +183,7 @@ bool tester(uint64_t seed, size_t volume) { double result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; @@ -201,7 +206,7 @@ bool tester(uint64_t seed, size_t volume) { float result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; diff --git a/tests/short_random_string.cpp b/tests/short_random_string.cpp index 3051b74..fd894ab 100644 --- a/tests/short_random_string.cpp +++ b/tests/short_random_string.cpp @@ -97,7 +97,12 @@ size_t build_random_string(RandomEngine &rand, char *buffer) { if (i == size_t(location_of_decimal_separator)) { buffer[pos++] = '.'; } - buffer[pos++] = char(rand.next_digit() + '0'); + buffer[pos] = char(rand.next_digit() + '0'); + // We can have a leading zero only if location_of_decimal_separator = 1. + while(i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') { + buffer[pos] = char(rand.next_digit() + '0'); + } + pos++; } if (rand.next_bool()) { if (rand.next_bool()) { @@ -174,7 +179,7 @@ bool tester(uint64_t seed, size_t volume) { double result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; @@ -197,7 +202,7 @@ bool tester(uint64_t seed, size_t volume) { float result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; diff --git a/tests/string_test.cpp b/tests/string_test.cpp index 4329dae..05f840c 100644 --- a/tests/string_test.cpp +++ b/tests/string_test.cpp @@ -85,7 +85,7 @@ bool test() { } if(begin != end) { std::cerr << " bad ending " << std::endl; - return false; + return false; } return true; } @@ -239,7 +239,7 @@ bool partow_test() { T result_value; auto result = fast_float::from_chars(st.data(), st.data() + st.size(), result_value); - if (result.ec != std::errc()) { + if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(st.size()), st.data()); std::cerr << " I could not parse " << std::endl; return false; @@ -270,7 +270,7 @@ int main() { std::cout << "32 bits checks" << std::endl; Assert(partow_test()); Assert(test()); - + std::cout << "64 bits checks" << std::endl; Assert(partow_test()); Assert(test()); From 21fefa5b44db3d1702470a61e9daed440cdde757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aras=20Pranckevi=C4=8Dius?= Date: Tue, 4 Apr 2023 21:18:57 +0300 Subject: [PATCH 07/10] Fix warnings with -Wundef - FASTFLOAT_ALLOWS_LEADING_PLUS and FASTFLOAT_SKIP_WHITE_SPACE are not defined by default, and compiling with -Wundef is emitting warnigns like "FASTFLOAT_ALLOWS_LEADING_PLUS is not defined, evaluates to 0". - Likewise for FASTFLOAT_VISUAL_STUDIO, change checks to use #ifdef for that like in other places. - __cpp_lib_bit_cast and __cpp_lib_is_constant_evaluated are not defined pre-C++20, and are emitting a warning too. --- include/fast_float/ascii_number.h | 2 +- include/fast_float/constexpr_feature_detect.h | 4 ++-- include/fast_float/float_common.h | 2 +- include/fast_float/parse_number.h | 8 ++++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index 72b8098..b7fcc9b 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -116,7 +116,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_ answer.valid = false; answer.too_many_digits = false; answer.negative = (*p == '-'); -#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default +#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default if ((*p == '-') || (*p == '+')) { #else if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here diff --git a/include/fast_float/constexpr_feature_detect.h b/include/fast_float/constexpr_feature_detect.h index 9ec8611..ba8b65c 100644 --- a/include/fast_float/constexpr_feature_detect.h +++ b/include/fast_float/constexpr_feature_detect.h @@ -14,13 +14,13 @@ #define FASTFLOAT_CONSTEXPR14 #endif -#if __cpp_lib_bit_cast >= 201806L +#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L #define FASTFLOAT_HAS_BIT_CAST 1 #else #define FASTFLOAT_HAS_BIT_CAST 0 #endif -#if __cpp_lib_is_constant_evaluated >= 201811L +#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 #else #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 diff --git a/include/fast_float/float_common.h b/include/fast_float/float_common.h index c878486..0f81f1a 100644 --- a/include/fast_float/float_common.h +++ b/include/fast_float/float_common.h @@ -481,7 +481,7 @@ void to_float(bool negative, adjusted_mantissa am, T &value) { #endif } -#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default +#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default template struct space_lut { static constexpr bool value[] = { diff --git a/include/fast_float/parse_number.h b/include/fast_float/parse_number.h index 6e4f6eb..5c28910 100644 --- a/include/fast_float/parse_number.h +++ b/include/fast_float/parse_number.h @@ -30,7 +30,7 @@ parse_infnan(const char *first, const char *last, T &value) noexcept { minusSign = true; ++first; } -#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default +#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default if (*first == '+') { ++first; } @@ -109,7 +109,7 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept { // // Note: This may fail to be accurate if fast-math has been // enabled, as rounding conventions may not apply. - #if FASTFLOAT_VISUAL_STUDIO + #ifdef FASTFLOAT_VISUAL_STUDIO # pragma warning(push) // todo: is there a VS warning? // see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 @@ -121,7 +121,7 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept { # pragma GCC diagnostic ignored "-Wfloat-equal" #endif return (fmini + 1.0f == 1.0f - fmini); - #if FASTFLOAT_VISUAL_STUDIO + #ifdef FASTFLOAT_VISUAL_STUDIO # pragma warning(pop) #elif defined(__clang__) # pragma clang diagnostic pop @@ -148,7 +148,7 @@ from_chars_result from_chars_advanced(const char *first, const char *last, from_chars_result answer; -#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default +#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default while ((first != last) && fast_float::is_space(uint8_t(*first))) { first++; } From 24374ece716db48f974f49da4aa5851aa371cfa9 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Tue, 4 Apr 2023 14:27:06 -0400 Subject: [PATCH 08/10] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 0b57a5b..1327f5a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ + ## fast_float number parsing library: 4x faster than strtod +[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fast_float.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fast_float) +[![VS17-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs17-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs17-ci.yml) +[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml) The fast_float library provides fast header-only implementations for the C++ from_chars functions for `float` and `double` types. These functions convert ASCII strings representing From 11eeab54631490d795e7310072aaa78017b6a8f8 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Tue, 11 Apr 2023 14:29:30 -0400 Subject: [PATCH 09/10] Removing dead code. --- .../fast_float/simple_decimal_conversion.h | 360 ------------------ 1 file changed, 360 deletions(-) delete mode 100644 include/fast_float/simple_decimal_conversion.h diff --git a/include/fast_float/simple_decimal_conversion.h b/include/fast_float/simple_decimal_conversion.h deleted file mode 100644 index 0484d74..0000000 --- a/include/fast_float/simple_decimal_conversion.h +++ /dev/null @@ -1,360 +0,0 @@ -#ifndef FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H -#define FASTFLOAT_GENERIC_DECIMAL_TO_BINARY_H - -/** - * This code is meant to handle the case where we have more than 19 digits. - * - * It is based on work by Nigel Tao (at https://github.com/google/wuffs/) - * who credits Ken Thompson for the design (via a reference to the Go source - * code). - * - * Rob Pike suggested that this algorithm be called "Simple Decimal Conversion". - * - * It is probably not very fast but it is a fallback that should almost never - * be used in real life. Though it is not fast, it is "easily" understood and debugged. - **/ -#include "ascii_number.h" -#include "decimal_to_binary.h" -#include - -namespace fast_float { - -namespace detail { - -// remove all final zeroes -inline void trim(decimal &h) { - while ((h.num_digits > 0) && (h.digits[h.num_digits - 1] == 0)) { - h.num_digits--; - } -} - - - -inline uint32_t number_of_digits_decimal_left_shift(const decimal &h, uint32_t shift) { - shift &= 63; - constexpr uint16_t number_of_digits_decimal_left_shift_table[65] = { - 0x0000, 0x0800, 0x0801, 0x0803, 0x1006, 0x1009, 0x100D, 0x1812, 0x1817, - 0x181D, 0x2024, 0x202B, 0x2033, 0x203C, 0x2846, 0x2850, 0x285B, 0x3067, - 0x3073, 0x3080, 0x388E, 0x389C, 0x38AB, 0x38BB, 0x40CC, 0x40DD, 0x40EF, - 0x4902, 0x4915, 0x4929, 0x513E, 0x5153, 0x5169, 0x5180, 0x5998, 0x59B0, - 0x59C9, 0x61E3, 0x61FD, 0x6218, 0x6A34, 0x6A50, 0x6A6D, 0x6A8B, 0x72AA, - 0x72C9, 0x72E9, 0x7B0A, 0x7B2B, 0x7B4D, 0x8370, 0x8393, 0x83B7, 0x83DC, - 0x8C02, 0x8C28, 0x8C4F, 0x9477, 0x949F, 0x94C8, 0x9CF2, 0x051C, 0x051C, - 0x051C, 0x051C, - }; - uint32_t x_a = number_of_digits_decimal_left_shift_table[shift]; - uint32_t x_b = number_of_digits_decimal_left_shift_table[shift + 1]; - uint32_t num_new_digits = x_a >> 11; - uint32_t pow5_a = 0x7FF & x_a; - uint32_t pow5_b = 0x7FF & x_b; - constexpr uint8_t - number_of_digits_decimal_left_shift_table_powers_of_5[0x051C] = { - 5, 2, 5, 1, 2, 5, 6, 2, 5, 3, 1, 2, 5, 1, 5, 6, 2, 5, 7, 8, 1, 2, 5, 3, - 9, 0, 6, 2, 5, 1, 9, 5, 3, 1, 2, 5, 9, 7, 6, 5, 6, 2, 5, 4, 8, 8, 2, 8, - 1, 2, 5, 2, 4, 4, 1, 4, 0, 6, 2, 5, 1, 2, 2, 0, 7, 0, 3, 1, 2, 5, 6, 1, - 0, 3, 5, 1, 5, 6, 2, 5, 3, 0, 5, 1, 7, 5, 7, 8, 1, 2, 5, 1, 5, 2, 5, 8, - 7, 8, 9, 0, 6, 2, 5, 7, 6, 2, 9, 3, 9, 4, 5, 3, 1, 2, 5, 3, 8, 1, 4, 6, - 9, 7, 2, 6, 5, 6, 2, 5, 1, 9, 0, 7, 3, 4, 8, 6, 3, 2, 8, 1, 2, 5, 9, 5, - 3, 6, 7, 4, 3, 1, 6, 4, 0, 6, 2, 5, 4, 7, 6, 8, 3, 7, 1, 5, 8, 2, 0, 3, - 1, 2, 5, 2, 3, 8, 4, 1, 8, 5, 7, 9, 1, 0, 1, 5, 6, 2, 5, 1, 1, 9, 2, 0, - 9, 2, 8, 9, 5, 5, 0, 7, 8, 1, 2, 5, 5, 9, 6, 0, 4, 6, 4, 4, 7, 7, 5, 3, - 9, 0, 6, 2, 5, 2, 9, 8, 0, 2, 3, 2, 2, 3, 8, 7, 6, 9, 5, 3, 1, 2, 5, 1, - 4, 9, 0, 1, 1, 6, 1, 1, 9, 3, 8, 4, 7, 6, 5, 6, 2, 5, 7, 4, 5, 0, 5, 8, - 0, 5, 9, 6, 9, 2, 3, 8, 2, 8, 1, 2, 5, 3, 7, 2, 5, 2, 9, 0, 2, 9, 8, 4, - 6, 1, 9, 1, 4, 0, 6, 2, 5, 1, 8, 6, 2, 6, 4, 5, 1, 4, 9, 2, 3, 0, 9, 5, - 7, 0, 3, 1, 2, 5, 9, 3, 1, 3, 2, 2, 5, 7, 4, 6, 1, 5, 4, 7, 8, 5, 1, 5, - 6, 2, 5, 4, 6, 5, 6, 6, 1, 2, 8, 7, 3, 0, 7, 7, 3, 9, 2, 5, 7, 8, 1, 2, - 5, 2, 3, 2, 8, 3, 0, 6, 4, 3, 6, 5, 3, 8, 6, 9, 6, 2, 8, 9, 0, 6, 2, 5, - 1, 1, 6, 4, 1, 5, 3, 2, 1, 8, 2, 6, 9, 3, 4, 8, 1, 4, 4, 5, 3, 1, 2, 5, - 5, 8, 2, 0, 7, 6, 6, 0, 9, 1, 3, 4, 6, 7, 4, 0, 7, 2, 2, 6, 5, 6, 2, 5, - 2, 9, 1, 0, 3, 8, 3, 0, 4, 5, 6, 7, 3, 3, 7, 0, 3, 6, 1, 3, 2, 8, 1, 2, - 5, 1, 4, 5, 5, 1, 9, 1, 5, 2, 2, 8, 3, 6, 6, 8, 5, 1, 8, 0, 6, 6, 4, 0, - 6, 2, 5, 7, 2, 7, 5, 9, 5, 7, 6, 1, 4, 1, 8, 3, 4, 2, 5, 9, 0, 3, 3, 2, - 0, 3, 1, 2, 5, 3, 6, 3, 7, 9, 7, 8, 8, 0, 7, 0, 9, 1, 7, 1, 2, 9, 5, 1, - 6, 6, 0, 1, 5, 6, 2, 5, 1, 8, 1, 8, 9, 8, 9, 4, 0, 3, 5, 4, 5, 8, 5, 6, - 4, 7, 5, 8, 3, 0, 0, 7, 8, 1, 2, 5, 9, 0, 9, 4, 9, 4, 7, 0, 1, 7, 7, 2, - 9, 2, 8, 2, 3, 7, 9, 1, 5, 0, 3, 9, 0, 6, 2, 5, 4, 5, 4, 7, 4, 7, 3, 5, - 0, 8, 8, 6, 4, 6, 4, 1, 1, 8, 9, 5, 7, 5, 1, 9, 5, 3, 1, 2, 5, 2, 2, 7, - 3, 7, 3, 6, 7, 5, 4, 4, 3, 2, 3, 2, 0, 5, 9, 4, 7, 8, 7, 5, 9, 7, 6, 5, - 6, 2, 5, 1, 1, 3, 6, 8, 6, 8, 3, 7, 7, 2, 1, 6, 1, 6, 0, 2, 9, 7, 3, 9, - 3, 7, 9, 8, 8, 2, 8, 1, 2, 5, 5, 6, 8, 4, 3, 4, 1, 8, 8, 6, 0, 8, 0, 8, - 0, 1, 4, 8, 6, 9, 6, 8, 9, 9, 4, 1, 4, 0, 6, 2, 5, 2, 8, 4, 2, 1, 7, 0, - 9, 4, 3, 0, 4, 0, 4, 0, 0, 7, 4, 3, 4, 8, 4, 4, 9, 7, 0, 7, 0, 3, 1, 2, - 5, 1, 4, 2, 1, 0, 8, 5, 4, 7, 1, 5, 2, 0, 2, 0, 0, 3, 7, 1, 7, 4, 2, 2, - 4, 8, 5, 3, 5, 1, 5, 6, 2, 5, 7, 1, 0, 5, 4, 2, 7, 3, 5, 7, 6, 0, 1, 0, - 0, 1, 8, 5, 8, 7, 1, 1, 2, 4, 2, 6, 7, 5, 7, 8, 1, 2, 5, 3, 5, 5, 2, 7, - 1, 3, 6, 7, 8, 8, 0, 0, 5, 0, 0, 9, 2, 9, 3, 5, 5, 6, 2, 1, 3, 3, 7, 8, - 9, 0, 6, 2, 5, 1, 7, 7, 6, 3, 5, 6, 8, 3, 9, 4, 0, 0, 2, 5, 0, 4, 6, 4, - 6, 7, 7, 8, 1, 0, 6, 6, 8, 9, 4, 5, 3, 1, 2, 5, 8, 8, 8, 1, 7, 8, 4, 1, - 9, 7, 0, 0, 1, 2, 5, 2, 3, 2, 3, 3, 8, 9, 0, 5, 3, 3, 4, 4, 7, 2, 6, 5, - 6, 2, 5, 4, 4, 4, 0, 8, 9, 2, 0, 9, 8, 5, 0, 0, 6, 2, 6, 1, 6, 1, 6, 9, - 4, 5, 2, 6, 6, 7, 2, 3, 6, 3, 2, 8, 1, 2, 5, 2, 2, 2, 0, 4, 4, 6, 0, 4, - 9, 2, 5, 0, 3, 1, 3, 0, 8, 0, 8, 4, 7, 2, 6, 3, 3, 3, 6, 1, 8, 1, 6, 4, - 0, 6, 2, 5, 1, 1, 1, 0, 2, 2, 3, 0, 2, 4, 6, 2, 5, 1, 5, 6, 5, 4, 0, 4, - 2, 3, 6, 3, 1, 6, 6, 8, 0, 9, 0, 8, 2, 0, 3, 1, 2, 5, 5, 5, 5, 1, 1, 1, - 5, 1, 2, 3, 1, 2, 5, 7, 8, 2, 7, 0, 2, 1, 1, 8, 1, 5, 8, 3, 4, 0, 4, 5, - 4, 1, 0, 1, 5, 6, 2, 5, 2, 7, 7, 5, 5, 5, 7, 5, 6, 1, 5, 6, 2, 8, 9, 1, - 3, 5, 1, 0, 5, 9, 0, 7, 9, 1, 7, 0, 2, 2, 7, 0, 5, 0, 7, 8, 1, 2, 5, 1, - 3, 8, 7, 7, 7, 8, 7, 8, 0, 7, 8, 1, 4, 4, 5, 6, 7, 5, 5, 2, 9, 5, 3, 9, - 5, 8, 5, 1, 1, 3, 5, 2, 5, 3, 9, 0, 6, 2, 5, 6, 9, 3, 8, 8, 9, 3, 9, 0, - 3, 9, 0, 7, 2, 2, 8, 3, 7, 7, 6, 4, 7, 6, 9, 7, 9, 2, 5, 5, 6, 7, 6, 2, - 6, 9, 5, 3, 1, 2, 5, 3, 4, 6, 9, 4, 4, 6, 9, 5, 1, 9, 5, 3, 6, 1, 4, 1, - 8, 8, 8, 2, 3, 8, 4, 8, 9, 6, 2, 7, 8, 3, 8, 1, 3, 4, 7, 6, 5, 6, 2, 5, - 1, 7, 3, 4, 7, 2, 3, 4, 7, 5, 9, 7, 6, 8, 0, 7, 0, 9, 4, 4, 1, 1, 9, 2, - 4, 4, 8, 1, 3, 9, 1, 9, 0, 6, 7, 3, 8, 2, 8, 1, 2, 5, 8, 6, 7, 3, 6, 1, - 7, 3, 7, 9, 8, 8, 4, 0, 3, 5, 4, 7, 2, 0, 5, 9, 6, 2, 2, 4, 0, 6, 9, 5, - 9, 5, 3, 3, 6, 9, 1, 4, 0, 6, 2, 5, - }; - const uint8_t *pow5 = - &number_of_digits_decimal_left_shift_table_powers_of_5[pow5_a]; - uint32_t i = 0; - uint32_t n = pow5_b - pow5_a; - for (; i < n; i++) { - if (i >= h.num_digits) { - return num_new_digits - 1; - } else if (h.digits[i] == pow5[i]) { - continue; - } else if (h.digits[i] < pow5[i]) { - return num_new_digits - 1; - } else { - return num_new_digits; - } - } - return num_new_digits; -} - -inline uint64_t round(decimal &h) { - if ((h.num_digits == 0) || (h.decimal_point < 0)) { - return 0; - } else if (h.decimal_point > 18) { - return UINT64_MAX; - } - // at this point, we know that h.decimal_point >= 0 - uint32_t dp = uint32_t(h.decimal_point); - uint64_t n = 0; - for (uint32_t i = 0; i < dp; i++) { - n = (10 * n) + ((i < h.num_digits) ? h.digits[i] : 0); - } - bool round_up = false; - if (dp < h.num_digits) { - round_up = h.digits[dp] >= 5; // normally, we round up - // but we may need to round to even! - if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) { - round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1])); - } - } - if (round_up) { - n++; - } - return n; -} - -// computes h * 2^-shift -inline void decimal_left_shift(decimal &h, uint32_t shift) { - if (h.num_digits == 0) { - return; - } - uint32_t num_new_digits = number_of_digits_decimal_left_shift(h, shift); - int32_t read_index = int32_t(h.num_digits - 1); - uint32_t write_index = h.num_digits - 1 + num_new_digits; - uint64_t n = 0; - - while (read_index >= 0) { - n += uint64_t(h.digits[read_index]) << shift; - uint64_t quotient = n / 10; - uint64_t remainder = n - (10 * quotient); - if (write_index < max_digits) { - h.digits[write_index] = uint8_t(remainder); - } else if (remainder > 0) { - h.truncated = true; - } - n = quotient; - write_index--; - read_index--; - } - while (n > 0) { - uint64_t quotient = n / 10; - uint64_t remainder = n - (10 * quotient); - if (write_index < max_digits) { - h.digits[write_index] = uint8_t(remainder); - } else if (remainder > 0) { - h.truncated = true; - } - n = quotient; - write_index--; - } - h.num_digits += num_new_digits; - if (h.num_digits > max_digits) { - h.num_digits = max_digits; - } - h.decimal_point += int32_t(num_new_digits); - trim(h); -} - -// computes h * 2^shift -inline void decimal_right_shift(decimal &h, uint32_t shift) { - uint32_t read_index = 0; - uint32_t write_index = 0; - - uint64_t n = 0; - - while ((n >> shift) == 0) { - if (read_index < h.num_digits) { - n = (10 * n) + h.digits[read_index++]; - } else if (n == 0) { - return; - } else { - while ((n >> shift) == 0) { - n = 10 * n; - read_index++; - } - break; - } - } - h.decimal_point -= int32_t(read_index - 1); - if (h.decimal_point < -decimal_point_range) { // it is zero - h.num_digits = 0; - h.decimal_point = 0; - h.negative = false; - h.truncated = false; - return; - } - uint64_t mask = (uint64_t(1) << shift) - 1; - while (read_index < h.num_digits) { - uint8_t new_digit = uint8_t(n >> shift); - n = (10 * (n & mask)) + h.digits[read_index++]; - h.digits[write_index++] = new_digit; - } - while (n > 0) { - uint8_t new_digit = uint8_t(n >> shift); - n = 10 * (n & mask); - if (write_index < max_digits) { - h.digits[write_index++] = new_digit; - } else if (new_digit > 0) { - h.truncated = true; - } - } - h.num_digits = write_index; - trim(h); -} - -} // namespace detail - -template -adjusted_mantissa compute_float(decimal &d) { - adjusted_mantissa answer; - if (d.num_digits == 0) { - // should be zero - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - // At this point, going further, we can assume that d.num_digits > 0. - // - // We want to guard against excessive decimal point values because - // they can result in long running times. Indeed, we do - // shifts by at most 60 bits. We have that log(10**400)/log(2**60) ~= 22 - // which is fine, but log(10**299995)/log(2**60) ~= 16609 which is not - // fine (runs for a long time). - // - if(d.decimal_point < -324) { - // We have something smaller than 1e-324 which is always zero - // in binary64 and binary32. - // It should be zero. - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } else if(d.decimal_point >= 310) { - // We have something at least as large as 0.1e310 which is - // always infinite. - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - constexpr uint32_t max_shift = 60; - constexpr uint32_t num_powers = 19; - constexpr uint8_t decimal_powers[19] = { - 0, 3, 6, 9, 13, 16, 19, 23, 26, 29, // - 33, 36, 39, 43, 46, 49, 53, 56, 59, // - }; - int32_t exp2 = 0; - while (d.decimal_point > 0) { - uint32_t n = uint32_t(d.decimal_point); - uint32_t shift = (n < num_powers) ? decimal_powers[n] : max_shift; - detail::decimal_right_shift(d, shift); - if (d.decimal_point < -decimal_point_range) { - // should be zero - answer.power2 = 0; - answer.mantissa = 0; - return answer; - } - exp2 += int32_t(shift); - } - // We shift left toward [1/2 ... 1]. - while (d.decimal_point <= 0) { - uint32_t shift; - if (d.decimal_point == 0) { - if (d.digits[0] >= 5) { - break; - } - shift = (d.digits[0] < 2) ? 2 : 1; - } else { - uint32_t n = uint32_t(-d.decimal_point); - shift = (n < num_powers) ? decimal_powers[n] : max_shift; - } - detail::decimal_left_shift(d, shift); - if (d.decimal_point > decimal_point_range) { - // we want to get infinity: - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - exp2 -= int32_t(shift); - } - // We are now in the range [1/2 ... 1] but the binary format uses [1 ... 2]. - exp2--; - constexpr int32_t minimum_exponent = binary::minimum_exponent(); - while ((minimum_exponent + 1) > exp2) { - uint32_t n = uint32_t((minimum_exponent + 1) - exp2); - if (n > max_shift) { - n = max_shift; - } - detail::decimal_right_shift(d, n); - exp2 += int32_t(n); - } - if ((exp2 - minimum_exponent) >= binary::infinite_power()) { - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - - const int mantissa_size_in_bits = binary::mantissa_explicit_bits() + 1; - detail::decimal_left_shift(d, mantissa_size_in_bits); - - uint64_t mantissa = detail::round(d); - // It is possible that we have an overflow, in which case we need - // to shift back. - if(mantissa >= (uint64_t(1) << mantissa_size_in_bits)) { - detail::decimal_right_shift(d, 1); - exp2 += 1; - mantissa = detail::round(d); - if ((exp2 - minimum_exponent) >= binary::infinite_power()) { - answer.power2 = binary::infinite_power(); - answer.mantissa = 0; - return answer; - } - } - answer.power2 = exp2 - binary::minimum_exponent(); - if(mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) { answer.power2--; } - answer.mantissa = mantissa & ((uint64_t(1) << binary::mantissa_explicit_bits()) - 1); - return answer; -} - -template -adjusted_mantissa parse_long_mantissa(const char *first, const char* last, parse_options options) { - decimal d = parse_decimal(first, last, options); - return compute_float(d); -} - -} // namespace fast_float -#endif From 1b9afafed5e48a011b4b3d85100393c6fb6bd4e7 Mon Sep 17 00:00:00 2001 From: Daniel Lemire Date: Fri, 21 Apr 2023 15:56:56 -0400 Subject: [PATCH 10/10] Removing old CI --- .github/workflows/ubuntu18.yml | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 .github/workflows/ubuntu18.yml diff --git a/.github/workflows/ubuntu18.yml b/.github/workflows/ubuntu18.yml deleted file mode 100644 index 791582d..0000000 --- a/.github/workflows/ubuntu18.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Ubuntu 18.04 CI (GCC 7) - -on: [push, pull_request] - -jobs: - ubuntu-build: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v3 - - name: Setup cmake - uses: jwlawson/actions-setup-cmake@v1.4 - with: - cmake-version: '3.11.x' - #- name: Install older compilers - # run: | - # sudo -E dpkg --add-architecture i386 - # sudo -E apt-get update - # sudo -E apt-get install -y --force-yes g++-5 g++-6 g++-5-multilib g++-6-multilib g++-multilib linux-libc-dev:i386 libc6:i386 libc6-dev:i386 libc6-dbg:i386 - - name: Prepare build dir - run: mkdir build - - name: Configure - run: cd build && cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON .. - - name: Build - run: cmake --build build - - name: Run basic tests - run: cd build && ctest --output-on-failure -R basictest