| // Copyright (c) Microsoft Corporation. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| |
| #include "test.hpp" |
| |
| #include <algorithm> |
| #include <array> |
| #include <assert.h> |
| #include <charconv> |
| #include <chrono> |
| #include <cmath> |
| #include <functional> |
| #include <iterator> |
| #include <limits> |
| #include <locale> |
| #include <optional> |
| #include <random> |
| #include <set> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <string> |
| #include <string_view> |
| #include <system_error> |
| #include <type_traits> |
| #include <utility> |
| #include <vector> |
| |
| #include "double_fixed_precision_to_chars_test_cases_1.hpp" |
| #include "double_fixed_precision_to_chars_test_cases_2.hpp" |
| #include "double_fixed_precision_to_chars_test_cases_3.hpp" |
| #include "double_fixed_precision_to_chars_test_cases_4.hpp" |
| #include "double_from_chars_test_cases.hpp" |
| #include "double_general_precision_to_chars_test_cases.hpp" |
| #include "double_hex_precision_to_chars_test_cases.hpp" |
| #include "double_scientific_precision_to_chars_test_cases_1.hpp" |
| #include "double_scientific_precision_to_chars_test_cases_2.hpp" |
| #include "double_scientific_precision_to_chars_test_cases_3.hpp" |
| #include "double_scientific_precision_to_chars_test_cases_4.hpp" |
| #include "double_to_chars_test_cases.hpp" |
| #include "float_fixed_precision_to_chars_test_cases.hpp" |
| #include "float_from_chars_test_cases.hpp" |
| #include "float_general_precision_to_chars_test_cases.hpp" |
| #include "float_hex_precision_to_chars_test_cases.hpp" |
| #include "float_scientific_precision_to_chars_test_cases.hpp" |
| #include "float_to_chars_test_cases.hpp" |
| |
| using namespace std; |
| |
| void initialize_randomness(mt19937_64& mt64, const int argc, char** const /*argv*/) { |
| constexpr std::size_t n = mt19937_64::state_size; |
| constexpr std::size_t w = mt19937_64::word_size; |
| static_assert(w % 32 == 0); |
| constexpr std::size_t k = w / 32; |
| |
| vector<std::uint32_t> vec(n * k); |
| |
| puts("USAGE:"); |
| puts("test.exe : generates seed data from random_device."); |
| |
| if (argc == 1) { |
| random_device rd; |
| generate(vec.begin(), vec.end(), ref(rd)); |
| puts("Generated seed data."); |
| } else { |
| puts("ERROR: Too many command-line arguments."); |
| abort(); |
| } |
| |
| puts("SEED DATA:"); |
| for (const auto& elem : vec) { |
| printf("%zu ", static_cast<std::size_t>(elem)); |
| } |
| printf("\n"); |
| |
| seed_seq seq(vec.cbegin(), vec.cend()); |
| |
| mt64.seed(seq); |
| |
| puts("Successfully seeded mt64. First three values:"); |
| for (int i = 0; i < 3; ++i) { |
| // libc++ uses long for 64-bit values. |
| printf("0x%016llX\n", static_cast<unsigned long long>(mt64())); |
| } |
| } |
| |
| static_assert((chars_format::scientific & chars_format::fixed) == chars_format{}); |
| static_assert((chars_format::scientific & chars_format::hex) == chars_format{}); |
| static_assert((chars_format::fixed & chars_format::hex) == chars_format{}); |
| static_assert(chars_format::general == (chars_format::fixed | chars_format::scientific)); |
| |
| template <typename T, typename Optional> |
| void test_common_to_chars( |
| const T value, const Optional opt_arg, const optional<int> opt_precision, const string_view correct) { |
| |
| // Important: Test every effective buffer size from 0 through correct.size() and slightly beyond. For the sizes |
| // less than correct.size(), this verifies that the too-small buffer is correctly detected, and that we don't |
| // attempt to write outside of it, even by a single char. (This exhaustive validation is necessary because the |
| // implementation must check whenever it attempts to write. Sometimes we can calculate the total size and perform |
| // a single check, but sometimes we need to check when writing each part of the result.) Testing correct.size() |
| // verifies that we can succeed without overrunning, and testing slightly larger sizes verifies that we can succeed |
| // without attempting to write to extra chars even when they're available. Finally, we also verify that we aren't |
| // underrunning the buffer. This is a concern because sometimes we walk backwards when rounding. |
| |
| constexpr std::size_t BufferPrefix = 20; // detect buffer underruns (specific value isn't important) |
| |
| constexpr std::size_t Space = is_integral_v<T> ? 1 + 64 // worst case: -2^63 in binary |
| : is_same_v<T, float> |
| ? 1 + 151 // worst case: negative min subnormal float, fixed notation |
| : 1 + 1076; // worst case: negative min subnormal double, fixed notation |
| |
| constexpr std::size_t BufferSuffix = 30; // detect buffer overruns (specific value isn't important) |
| |
| array<char, BufferPrefix + Space + BufferSuffix> buff; |
| |
| char* const buff_begin = buff.data(); |
| char* const first = buff_begin + BufferPrefix; |
| char* const buff_end = buff_begin + buff.size(); |
| |
| constexpr std::size_t ExtraChars = 3; |
| static_assert(ExtraChars + 10 < BufferSuffix, |
| "The specific values aren't important, but there should be plenty of room to detect buffer overruns."); |
| |
| for (std::size_t n = 0; n <= correct.size() + ExtraChars; ++n) { |
| assert(n <= static_cast<std::size_t>(buff_end - first)); |
| char* const last = first + n; |
| |
| buff.fill('@'); |
| const auto is_fill_char = [](const char c) { return c == '@'; }; |
| |
| to_chars_result result{}; |
| if (opt_precision.has_value()) { |
| assert(opt_arg.has_value()); |
| |
| if constexpr (is_floating_point_v<T>) { |
| result = to_chars(first, last, value, opt_arg.value(), opt_precision.value()); |
| } else { |
| abort(); |
| } |
| } else if (opt_arg.has_value()) { |
| result = to_chars(first, last, value, opt_arg.value()); |
| } else { |
| result = to_chars(first, last, value); |
| } |
| |
| if (n < correct.size()) { |
| assert(result.ptr == last); |
| assert(result.ec == errc::value_too_large); |
| assert(all_of(buff_begin, first, is_fill_char)); |
| // [first, last) is unspecified |
| assert(all_of(last, buff_end, is_fill_char)); |
| } else { |
| assert(result.ptr == first + correct.size()); |
| assert(result.ec == errc{}); |
| assert(all_of(buff_begin, first, is_fill_char)); |
| assert(equal(first, result.ptr, correct.begin(), correct.end())); |
| assert(all_of(result.ptr, buff_end, is_fill_char)); |
| } |
| } |
| } |
| |
| template <typename T> |
| void test_integer_to_chars(const T value, const optional<int> opt_base, const string_view correct) { |
| |
| test_common_to_chars(value, opt_base, nullopt, correct); |
| |
| { // Also test successful from_chars() scenarios. |
| const char* const correct_first = correct.data(); |
| const char* const correct_last = correct_first + correct.size(); |
| |
| T dest = 0; |
| |
| const from_chars_result from_res = |
| (opt_base.has_value() ? from_chars(correct_first, correct_last, dest, opt_base.value()) |
| : from_chars(correct_first, correct_last, dest)); |
| |
| assert(from_res.ptr == correct_last); |
| assert(from_res.ec == errc{}); |
| assert(dest == value); |
| } |
| } |
| |
| // https://www.wolframalpha.com : Table[BaseForm[n * 2 - 1, n], {n, 2, 36}] |
| constexpr const char* output_max_digit[] = {"skip0", "skip1", "11", "12", "13", "14", "15", "16", "17", "18", "19", |
| "1a", "1b", "1c", "1d", "1e", "1f", "1g", "1h", "1i", "1j", "1k", "1l", "1m", "1n", "1o", "1p", "1q", "1r", "1s", |
| "1t", "1u", "1v", "1w", "1x", "1y", "1z"}; |
| |
| // https://www.wolframalpha.com : Table[BaseForm[k, n], {k, {MEOW, MEOW, MEOW}}, {n, 2, 36}] |
| constexpr std::uint64_t stress_chunks_positive = 12000000345000678900ULL; |
| constexpr pair<std::uint64_t, array<const char*, 37>> output_positive[] = { |
| {123U, {{"skip0", "skip1", "1111011", "11120", "1323", "443", "323", "234", "173", "146", "123", "102", "a3", "96", |
| "8b", "83", "7b", "74", "6f", "69", "63", "5i", "5d", "58", "53", "4n", "4j", "4f", "4b", "47", "43", |
| "3u", "3r", "3o", "3l", "3i", "3f"}}}, |
| {std::uint64_t{INT8_MAX}, {{"skip0", "skip1", "1111111", "11201", "1333", "1002", "331", "241", "177", "151", "127", |
| "106", "a7", "9a", "91", "87", "7f", "78", "71", "6d", "67", "61", "5h", "5c", "57", "52", |
| "4n", "4j", "4f", "4b", "47", "43", "3v", "3s", "3p", "3m", "3j"}}}, |
| {161U, {{"skip0", "skip1", "10100001", "12222", "2201", "1121", "425", "320", "241", "188", "161", "137", "115", |
| "c5", "b7", "ab", "a1", "98", "8h", "89", "81", "7e", "77", "70", "6h", "6b", "65", "5q", "5l", "5g", |
| "5b", "56", "51", "4t", "4p", "4l", "4h"}}}, |
| {UINT8_MAX, {{"skip0", "skip1", "11111111", "100110", "3333", "2010", "1103", "513", "377", "313", "255", "212", |
| "193", "168", "143", "120", "ff", "f0", "e3", "d8", "cf", "c3", "bd", "b2", "af", "a5", "9l", "9c", |
| "93", "8n", "8f", "87", "7v", "7o", "7h", "7a", "73"}}}, |
| {1729U, {{"skip0", "skip1", "11011000001", "2101001", "123001", "23404", "12001", "5020", "3301", "2331", "1729", |
| "1332", "1001", "a30", "8b7", "7a4", "6c1", "5gc", "561", "4f0", "469", "3j7", "3cd", "364", "301", |
| "2j4", "2ed", "2a1", "25l", "21i", "1rj", "1oo", "1m1", "1jd", "1gt", "1ee", "1c1"}}}, |
| {std::uint64_t{INT16_MAX}, {{"skip0", "skip1", "111111111111111", "1122221121", "13333333", "2022032", "411411", |
| "164350", "77777", "48847", "32767", "22689", "16b67", "11bb7", "bd27", "9a97", "7fff", |
| "6b68", "5b27", "4eeb", "41i7", "3b67", "31f9", "2flf", "28l7", "22ah", "1mc7", "1hpg", |
| "1dm7", "19rq", "16c7", "1330", "vvv", "u2v", "sbp", "qq7", "pa7"}}}, |
| {57494U, {{"skip0", "skip1", "1110000010010110", "2220212102", "32002112", "3314434", "1122102", "326423", "160226", |
| "86772", "57494", "3a218", "29332", "20228", "16d4a", "1207e", "e096", "bbg0", "9f82", "8750", "73ee", |
| "647h", "58h8", "4gfh", "43je", "3goj", "3718", "2onb", "2h9a", "2aag", "23qe", "1spk", "1o4m", "1jq8", |
| "1fp0", "1bwo", "18d2"}}}, |
| {UINT16_MAX, {{"skip0", "skip1", "1111111111111111", "10022220020", "33333333", "4044120", "1223223", "362031", |
| "177777", "108806", "65535", "45268", "31b13", "23aa2", "19c51", "14640", "ffff", "d5d0", "b44f", |
| "9aa4", "83gf", "71cf", "638j", "58k8", "4hif", "44la", "3iof", "38o6", "2rgf", "2jqo", "2cof", |
| "2661", "1vvv", "1r5u", "1mnh", "1ihf", "1ekf"}}}, |
| {71125478U, {{"skip0", "skip1", "100001111010100100111100110", "11221211112210222", "10033110213212", |
| "121202003403", "11020244342", "1522361624", "417244746", "157745728", "71125478", "3716a696", |
| "1b9a06b2", "11973ba8", "9636514", "639e338", "43d49e6", "2g19gfb", "21b9d18", "19dec94", "124addi", |
| "h8f25b", "dhdfa6", "b13hg2", "8m91he", "7720j3", "5pgj58", "4pmelq", "43k17i", "3dg8ek", "2ro898", |
| "2f0et8", "23qif6", "1qw5lh", "1j7l7s", "1cdvli", "16cgrq"}}}, |
| {std::uint64_t{INT32_MAX}, |
| {{"skip0", "skip1", "1111111111111111111111111111111", "12112122212110202101", "1333333333333333", |
| "13344223434042", "553032005531", "104134211161", "17777777777", "5478773671", "2147483647", "a02220281", |
| "4bb2308a7", "282ba4aaa", "1652ca931", "c87e66b7", "7fffffff", "53g7f548", "3928g3h1", "27c57h32", |
| "1db1f927", "140h2d91", "ikf5bf1", "ebelf95", "b5gge57", "8jmdnkm", "6oj8ion", "5ehncka", "4clm98f", |
| "3hk7987", "2sb6cs7", "2d09uc1", "1vvvvvv", "1lsqtl1", "1d8xqrp", "15v22um", "zik0zj"}}}, |
| {3522553278ULL, |
| {{"skip0", "skip1", "11010001111101011110010110111110", "100002111022020200020", "3101331132112332", |
| "24203233201103", "1341312313010", "153202131426", "32175362676", "10074266606", "3522553278", "1548431462", |
| "823842766", "441a34c6a", "255b8d486", "1593b4753", "d1f5e5be", "89ffb3b6", "5da3e606", "3hgbfb5i", |
| "2f0fj33i", "1k1ac536", "191b46e2", "10i6fmk8", "ia967l6", "eahia63", "baca9ga", "92d86i6", "78iq4i6", |
| "5qlc1dc", "4osos2i", "3u1862s", "38vbpdu", "2o0a7ro", "29hx9e6", "1w2dnod", "1m98ji6"}}}, |
| {UINT32_MAX, |
| {{"skip0", "skip1", "11111111111111111111111111111111", "102002022201221111210", "3333333333333333", |
| "32244002423140", "1550104015503", "211301422353", "37777777777", "12068657453", "4294967295", "1904440553", |
| "9ba461593", "535a79888", "2ca5b7463", "1a20dcd80", "ffffffff", "a7ffda90", "704he7g3", "4f5aff65", |
| "3723ai4f", "281d55i3", "1fj8b183", "1606k7ib", "mb994af", "hek2mgk", "dnchbnl", "b28jpdl", "8pfgih3", |
| "76beigf", "5qmcpqf", "4q0jto3", "3vvvvvv", "3aokq93", "2qhxjlh", "2br45qa", "1z141z3"}}}, |
| {545890816626160ULL, |
| {{"skip0", "skip1", "1111100000111110000011100001101100000110111110000", "2122120211122121121021010202111", |
| "1330013300130031200313300", "1033022333343024014120", "5213002440142255104", "222661211220253465", |
| "17407603415406760", "2576748547233674", "545890816626160", "148a34aa4706535", "51285369b87494", |
| "1a57a38b045a95", "98b3383b9766c", "4319d1601875a", "1f07c1c360df0", "ffd471f34f13", "88g09ff9dh84", |
| "4d0d5e232c53", "2d63h403i580", "1bf5h8185hdj", "kc3g550fkcg", "d41id5k9984", "8ef5n0him4g", "5i2dijfe1la", |
| "3me22fm5fhi", "2hfmhgg73kd", "1ngpfabr53c", "18i7220bh11", "rm0lcjngpa", "kk1elesni1", "fgfge3c3fg", |
| "bp4q5l6bjg", "8xna46jp0k", "6wejomvji5", "5di2s1qhv4"}}}, |
| {std::uint64_t{INT64_MAX}, |
| {{"skip0", "skip1", "111111111111111111111111111111111111111111111111111111111111111", |
| "2021110011022210012102010021220101220221", "13333333333333333333333333333333", |
| "1104332401304422434310311212", "1540241003031030222122211", "22341010611245052052300", |
| "777777777777777777777", "67404283172107811827", "9223372036854775807", "1728002635214590697", |
| "41a792678515120367", "10b269549075433c37", "4340724c6c71dc7a7", "160e2ad3246366807", "7fffffffffffffff", |
| "33d3d8307b214008", "16agh595df825fa7", "ba643dci0ffeehh", "5cbfjia3fh26ja7", "2heiciiie82dh97", |
| "1adaibb21dckfa7", "i6k448cf4192c2", "acd772jnc9l0l7", "64ie1focnn5g77", "3igoecjbmca687", "27c48l5b37oaop", |
| "1bk39f3ah3dmq7", "q1se8f0m04isb", "hajppbc1fc207", "bm03i95hia437", "7vvvvvvvvvvvv", "5hg4ck9jd4u37", |
| "3tdtk1v8j6tpp", "2pijmikexrxp7", "1y2p0ij32e8e7"}}}, |
| {stress_chunks_positive, |
| {{"skip0", "skip1", "1010011010001000100100001011110000101100010101001001010111110100", |
| "2221221122020020011022001202200200202200", "22122020210023300230111021113310", |
| "1301130403021123030133211100", "2311004450342244200504500", "30325064311430214266301", |
| "1232104413605425112764", "87848206138052620680", "12000000345000678900", "2181782a1686924456a", |
| "54aa47a9058877b130", "150593a5b002c87b16", "571cad2b93c7760a8", "1c60d2676d4e53e00", "a68890bc2c5495f4", |
| "43499224707a4f4g", "1e052gdga1d26f40", "f06dh4g564c8a91", "769df0d9ace4h50", "3ee7bcj1ajghi4f", |
| "1k9agc4gfl0l43a", "10id7dakdlcjd22", "dge08fe0l5hl7c", "8184326d31ib60", "4ljbglf3cpim76", |
| "2pph66481kiiki", "1niph2ao132e58", "14qgbgk3c3iffg", "mhc35an1bhb00", "f78o8ur705ln5", "ad24gngm595fk", |
| "76e1n5i5v0ivl", "50wu8jsnks82g", "3ja41smfvqb1f", "2j64t3qgq0ut0"}}}, |
| {14454900944617508688ULL, |
| {{"skip0", "skip1", "1100100010011010000111111101001011100011011000101000111101010000", |
| "10120022020112011211121221212101012220210", "30202122013331023203120220331100", |
| "1432224030234034034040234223", "3014532424232535441404120", "34610451042001242144165", |
| "1442320775134330507520", "116266464747855335823", "14454900944617508688", "266642a9a9471339935", |
| "662251403263939640", "1895280092bc310481", "68cb9c8292557406c", "23023deab20002893", "c89a1fd2e3628f50", |
| "50e7147a7db8ef84", "22a34a05086f78ec", "i1dgef04357g7i1", "8g90b882jcj8be8", "49c1kk35i0k24ic", |
| "272a16i54ebkacg", "15fdih7l3m7k8md", "gbj7303eg9nge0", "9hckfdkj3kkdmd", "5lc7hifdkl4nne", |
| "3f86e4mgpna5ol", "266pj428na273c", "1bomgjbnlg4m3f", "r5tf1f7f009ji", "iarsig29iqhhm", "ch6gvqbhm53qg", |
| "8lwtvcdj6rlqr", "61w23lajggp44", "49p1f3dsqqcdx", "31tkqqkxypopc"}}}, |
| {UINT64_MAX, |
| {{"skip0", "skip1", "1111111111111111111111111111111111111111111111111111111111111111", |
| "11112220022122120101211020120210210211220", "33333333333333333333333333333333", |
| "2214220303114400424121122430", "3520522010102100444244423", "45012021522523134134601", |
| "1777777777777777777777", "145808576354216723756", "18446744073709551615", "335500516a429071284", |
| "839365134a2a240713", "219505a9511a867b72", "8681049adb03db171", "2c1d56b648c6cd110", "ffffffffffffffff", |
| "67979g60f5428010", "2d3fgb0b9cg4bd2f", "141c8786h1ccaagg", "b53bjh07be4dj0f", "5e8g4ggg7g56dif", |
| "2l4lf104353j8kf", "1ddh88h2782i515", "l12ee5fn0ji1if", "c9c336o0mlb7ef", "7b7n2pcniokcgf", |
| "4eo8hfam6fllmo", "2nc6j26l66rhof", "1n3rsh11f098rn", "14l9lkmo30o40f", "nd075ib45k86f", "fvvvvvvvvvvvv", |
| "b1w8p7j5q9r6f", "7orp63sh4dphh", "5g24a25twkwff", "3w5e11264sgsf"}}}, |
| }; |
| |
| // https://www.wolframalpha.com : Table[BaseForm[k, n], {k, {MEOW, MEOW, MEOW}}, {n, 2, 36}] |
| constexpr std::int64_t stress_chunks_negative = -9000876000000054321LL; |
| constexpr pair<std::int64_t, array<const char*, 37>> output_negative[] = { |
| {-85, {{"skip0", "skip1", "-1010101", "-10011", "-1111", "-320", "-221", "-151", "-125", "-104", "-85", "-78", |
| "-71", "-67", "-61", "-5a", "-55", "-50", "-4d", "-49", "-45", "-41", "-3j", "-3g", "-3d", "-3a", "-37", |
| "-34", "-31", "-2r", "-2p", "-2n", "-2l", "-2j", "-2h", "-2f", "-2d"}}}, |
| {INT8_MIN, {{"skip0", "skip1", "-10000000", "-11202", "-2000", "-1003", "-332", "-242", "-200", "-152", "-128", |
| "-107", "-a8", "-9b", "-92", "-88", "-80", "-79", "-72", "-6e", "-68", "-62", "-5i", "-5d", "-58", |
| "-53", "-4o", "-4k", "-4g", "-4c", "-48", "-44", "-40", "-3t", "-3q", "-3n", "-3k"}}}, |
| {-1591, {{"skip0", "skip1", "-11000110111", "-2011221", "-120313", "-22331", "-11211", "-4432", "-3067", "-2157", |
| "-1591", "-1217", "-b07", "-955", "-819", "-711", "-637", "-58a", "-4g7", "-47e", "-3jb", "-3cg", |
| "-367", "-304", "-2i7", "-2dg", "-295", "-24p", "-20n", "-1pp", "-1n1", "-1ka", "-1hn", "-1f7", "-1cr", |
| "-1ag", "-187"}}}, |
| {INT16_MIN, {{"skip0", "skip1", "-1000000000000000", "-1122221122", "-20000000", "-2022033", "-411412", "-164351", |
| "-100000", "-48848", "-32768", "-2268a", "-16b68", "-11bb8", "-bd28", "-9a98", "-8000", "-6b69", |
| "-5b28", "-4eec", "-41i8", "-3b68", "-31fa", "-2flg", "-28l8", "-22ai", "-1mc8", "-1hph", "-1dm8", |
| "-19rr", "-16c8", "-1331", "-1000", "-u2w", "-sbq", "-qq8", "-pa8"}}}, |
| {-66748412, |
| {{"skip0", "skip1", "-11111110100111111111111100", "-11122121011121102", "-3332213333330", "-114041422122", |
| "-10342352232", "-1440231533", "-376477774", "-148534542", "-66748412", "-34750085", "-1a42b678", |
| "-10aa0803", "-8c1731a", "-5cd7492", "-3fa7ffc", "-2d03163", "-1h5f3b2", "-17i39c6", "-10h3b0c", "-g749jh", |
| "-ckkdkg", "-a8c0ak", "-894afk", "-6klmbc", "-5g1i6g", "-4hg4gb", "-3ogi7o", "-37anqb", "-2mc4r2", |
| "-2a8h7i", "-1vkvvs", "-1n9ca5", "-1fw8sk", "-19gshh", "-13qnek"}}}, |
| {INT32_MIN, {{"skip0", "skip1", "-10000000000000000000000000000000", "-12112122212110202102", "-2000000000000000", |
| "-13344223434043", "-553032005532", "-104134211162", "-20000000000", "-5478773672", "-2147483648", |
| "-a02220282", "-4bb2308a8", "-282ba4aab", "-1652ca932", "-c87e66b8", "-80000000", "-53g7f549", |
| "-3928g3h2", "-27c57h33", "-1db1f928", "-140h2d92", "-ikf5bf2", "-ebelf96", "-b5gge58", "-8jmdnkn", |
| "-6oj8ioo", "-5ehnckb", "-4clm98g", "-3hk7988", "-2sb6cs8", "-2d09uc2", "-2000000", "-1lsqtl2", |
| "-1d8xqrq", "-15v22un", "-zik0zk"}}}, |
| {-297139747082649553LL, |
| {{"skip0", "skip1", "-10000011111101001110000011010010001100000101011111111010001", |
| "-1222110012002112101210012211022102101", "-100133221300122101200223333101", "-4443033200104011124241203", |
| "-21313431255203203120401", "-350320603201030412545", "-20375160322140537721", "-1873162471705738371", |
| "-297139747082649553", "-65150976074a24025", "-173522497b5373101", "-5a60a99bc3b71654", "-1ca51a06cc38ba25", |
| "-a2a25babe62241d", "-41fa7069182bfd1", "-1d00134fba1769g", "-e4f799fc5f7e81", "-714ebbh8388188", |
| "-3cahb17836b3hd", "-1j8659jf5hbg3j", "-112bbb2jege5c5", "-dcjfmk2kjb4cc", "-836bm4klbgl61", |
| "-4ofia1416ee73", "-32ommgjef1l2h", "-1qc52eal5m8ba", "-17n53r05a4r15", "-oa88m2qiqjik", "-gn67qoat5r8d", |
| "-blgd6n5s90al", "-87t70q8o5fuh", "-5t09hwaqu9qg", "-47vssihaoa4x", "-32p24fbjye7x", "-299r8zck3841"}}}, |
| {stress_chunks_negative, |
| {{"skip0", "skip1", "-111110011101001100010010000100010000111010101111001010000110001", |
| "-2012222010200021010000112111002001111200", "-13303221202100202013111321100301", |
| "-1101001100304341000003214241", "-1522150121302454031001413", "-22054250360123016161454", |
| "-763514220420725712061", "-65863607100474061450", "-9000876000000054321", "-1689813530958833498", |
| "-408258185a67069269", "-106b01597a47ba2948", "-41c02922bc776d49b", "-1584cd10979dc84b6", |
| "-7ce9890887579431", "-327cf6cbc67023c3", "-1604b5f6a0de8129", "-b50d3ef02f124a4", "-59h9bfif0006fg1", |
| "-2g5d8ekh05d2dfi", "-19i418c38g1chfj", "-hjgf7d0k0gla9a", "-a6b21ncehfa3f9", "-61060fnl003bml", |
| "-3g88bakondgf8l", "-25q3i730ed21di", "-1al84glo518iip", "-pcli8ig7pjhbo", "-gs31q8id2jnkl", |
| "-bd7kaglgdrbgk", "-7pqc9123lf51h", "-5d2sd1r5ms7su", "-3q833s8kdrun3", "-2n7vmqigfueqb", |
| "-1wdu892toj0a9"}}}, |
| {INT64_MIN, {{"skip0", "skip1", "-1000000000000000000000000000000000000000000000000000000000000000", |
| "-2021110011022210012102010021220101220222", "-20000000000000000000000000000000", |
| "-1104332401304422434310311213", "-1540241003031030222122212", "-22341010611245052052301", |
| "-1000000000000000000000", "-67404283172107811828", "-9223372036854775808", "-1728002635214590698", |
| "-41a792678515120368", "-10b269549075433c38", "-4340724c6c71dc7a8", "-160e2ad3246366808", |
| "-8000000000000000", "-33d3d8307b214009", "-16agh595df825fa8", "-ba643dci0ffeehi", |
| "-5cbfjia3fh26ja8", "-2heiciiie82dh98", "-1adaibb21dckfa8", "-i6k448cf4192c3", "-acd772jnc9l0l8", |
| "-64ie1focnn5g78", "-3igoecjbmca688", "-27c48l5b37oaoq", "-1bk39f3ah3dmq8", "-q1se8f0m04isc", |
| "-hajppbc1fc208", "-bm03i95hia438", "-8000000000000", "-5hg4ck9jd4u38", "-3tdtk1v8j6tpq", |
| "-2pijmikexrxp8", "-1y2p0ij32e8e8"}}}, |
| }; |
| |
| template <typename T> |
| void test_integer_to_chars() { |
| for (int base = 2; base <= 36; ++base) { |
| test_integer_to_chars(static_cast<T>(0), base, "0"); |
| test_integer_to_chars(static_cast<T>(1), base, "1"); |
| |
| // tests [3, 71] |
| test_integer_to_chars(static_cast<T>(base * 2 - 1), base, output_max_digit[base]); |
| |
| for (const auto& p : output_positive) { |
| if (p.first <= static_cast<std::uint64_t>(numeric_limits<T>::max())) { |
| test_integer_to_chars(static_cast<T>(p.first), base, p.second[static_cast<std::size_t>(base)]); |
| } |
| } |
| |
| if constexpr (is_signed_v<T>) { |
| test_integer_to_chars(static_cast<T>(-1), base, "-1"); |
| |
| for (const auto& p : output_negative) { |
| if (p.first >= static_cast<std::int64_t>(numeric_limits<T>::min())) { |
| test_integer_to_chars(static_cast<T>(p.first), base, p.second[static_cast<std::size_t>(base)]); |
| } |
| } |
| } |
| } |
| |
| test_integer_to_chars(static_cast<T>(42), nullopt, "42"); |
| } |
| |
| enum class TestFromCharsMode { Normal, SignalingNaN }; |
| |
| template <typename T, typename BaseOrFmt> |
| void test_from_chars(const string_view input, const BaseOrFmt base_or_fmt, const std::size_t correct_idx, |
| const errc correct_ec, const optional<T> opt_correct = nullopt, |
| const TestFromCharsMode mode = TestFromCharsMode::Normal) { |
| |
| if constexpr (is_integral_v<T>) { |
| assert(mode == TestFromCharsMode::Normal); |
| } |
| |
| constexpr T unmodified = 111; |
| |
| T dest = unmodified; |
| |
| const from_chars_result result = from_chars(input.data(), input.data() + input.size(), dest, base_or_fmt); |
| |
| assert(result.ptr == input.data() + correct_idx); |
| assert(result.ec == correct_ec); |
| |
| if (correct_ec == errc{} || (is_floating_point_v<T> && correct_ec == errc::result_out_of_range)) { |
| if constexpr (is_floating_point_v<T>) { |
| if (mode == TestFromCharsMode::Normal) { |
| using Uint = conditional_t<is_same_v<T, float>, std::uint32_t, std::uint64_t>; |
| assert(opt_correct.has_value()); |
| assert(_Bit_cast<Uint>(dest) == _Bit_cast<Uint>(opt_correct.value())); |
| } else { |
| assert(mode == TestFromCharsMode::SignalingNaN); |
| assert(!opt_correct.has_value()); |
| assert(isnan(dest)); |
| } |
| } else { |
| assert(opt_correct.has_value()); |
| assert(dest == opt_correct.value()); |
| } |
| } else { |
| assert(!opt_correct.has_value()); |
| assert(dest == unmodified); |
| } |
| } |
| |
| constexpr errc inv_arg = errc::invalid_argument; |
| constexpr errc out_ran = errc::result_out_of_range; |
| |
| template <typename T> |
| void test_integer_from_chars() { |
| for (int base = 2; base <= 36; ++base) { |
| test_from_chars<T>("", base, 0, inv_arg); // no characters |
| test_from_chars<T>("@1", base, 0, inv_arg); // '@' is bogus |
| test_from_chars<T>(".1", base, 0, inv_arg); // '.' is bogus, for integers |
| test_from_chars<T>("+1", base, 0, inv_arg); // '+' is bogus, N4713 23.20.3 [charconv.from.chars]/3 |
| // "a minus sign is the only sign that may appear" |
| test_from_chars<T>(" 1", base, 0, inv_arg); // ' ' is bogus, no whitespace in subject sequence |
| |
| if constexpr (is_unsigned_v<T>) { // N4713 23.20.3 [charconv.from.chars]/3 |
| test_from_chars<T>("-1", base, 0, inv_arg); // "and only if value has a signed type" |
| } |
| |
| // N4713 23.20.3 [charconv.from.chars]/1 "[ Note: If the pattern allows for an optional sign, |
| // but the string has no digit characters following the sign, no characters match the pattern. -end note ]" |
| test_from_chars<T>("-", base, 0, inv_arg); // '-' followed by no characters |
| test_from_chars<T>("-@1", base, 0, inv_arg); // '-' followed by bogus '@' |
| test_from_chars<T>("-.1", base, 0, inv_arg); // '-' followed by bogus '.' |
| test_from_chars<T>("-+1", base, 0, inv_arg); // '-' followed by bogus '+' |
| test_from_chars<T>("- 1", base, 0, inv_arg); // '-' followed by bogus ' ' |
| test_from_chars<T>("--1", base, 0, inv_arg); // '-' can't be repeated |
| |
| vector<char> bogus_digits; |
| |
| if (base < 10) { |
| bogus_digits = {static_cast<char>('0' + base), 'A', 'a'}; |
| } else { |
| // '[' and '{' are bogus for base 36 |
| bogus_digits = {static_cast<char>('A' + (base - 10)), static_cast<char>('a' + (base - 10))}; |
| } |
| |
| for (const auto& bogus : bogus_digits) { |
| test_from_chars<T>(bogus + "1"s, base, 0, inv_arg); // bogus digit (for this base) |
| test_from_chars<T>("-"s + bogus + "1"s, base, 0, inv_arg); // '-' followed by bogus digit |
| } |
| |
| // Test leading zeroes. |
| test_from_chars<T>(string(100, '0'), base, 100, errc{}, static_cast<T>(0)); |
| test_from_chars<T>(string(100, '0') + "11"s, base, 102, errc{}, static_cast<T>(base + 1)); |
| |
| // Test negative zero and negative leading zeroes. |
| if constexpr (is_signed_v<T>) { |
| test_from_chars<T>("-0", base, 2, errc{}, static_cast<T>(0)); |
| test_from_chars<T>("-"s + string(100, '0'), base, 101, errc{}, static_cast<T>(0)); |
| test_from_chars<T>("-"s + string(100, '0') + "11"s, base, 103, errc{}, static_cast<T>(-base - 1)); |
| } |
| |
| // N4713 23.20.3 [charconv.from.chars]/1 "The member ptr of the return value points to the |
| // first character not matching the pattern, or has the value last if all characters match." |
| test_from_chars<T>("11", base, 2, errc{}, static_cast<T>(base + 1)); |
| test_from_chars<T>("11@@@", base, 2, errc{}, static_cast<T>(base + 1)); |
| |
| // When overflowing, we need to keep consuming valid digits, in order to return ptr correctly. |
| test_from_chars<T>(string(100, '1'), base, 100, out_ran); |
| test_from_chars<T>(string(100, '1') + "@@@"s, base, 100, out_ran); |
| |
| if constexpr (is_signed_v<T>) { |
| test_from_chars<T>("-"s + string(100, '1'), base, 101, out_ran); |
| test_from_chars<T>("-"s + string(100, '1') + "@@@"s, base, 101, out_ran); |
| } |
| } |
| |
| // N4713 23.20.3 [charconv.from.chars]/3 "The pattern is the expected form of the subject sequence |
| // in the "C" locale for the given nonzero base, as described for strtol" |
| // C11 7.22.1.4/3 "The letters from a (or A) through z (or Z) are ascribed the values 10 through 35" |
| for (int i = 0; i < 26; ++i) { |
| test_from_chars<T>(string(1, static_cast<char>('A' + i)), 36, 1, errc{}, static_cast<T>(10 + i)); |
| test_from_chars<T>(string(1, static_cast<char>('a' + i)), 36, 1, errc{}, static_cast<T>(10 + i)); |
| } |
| |
| // N4713 23.20.3 [charconv.from.chars]/3 "no "0x" or "0X" prefix shall appear if the value of base is 16" |
| test_from_chars<T>("0x1729", 16, 1, errc{}, static_cast<T>(0)); // reads '0', stops at 'x' |
| test_from_chars<T>("0X1729", 16, 1, errc{}, static_cast<T>(0)); // reads '0', stops at 'X' |
| |
| if constexpr (is_signed_v<T>) { |
| test_from_chars<T>("-0x1729", 16, 2, errc{}, static_cast<T>(0)); // reads "-0", stops at 'x' |
| test_from_chars<T>("-0X1729", 16, 2, errc{}, static_cast<T>(0)); // reads "-0", stops at 'X' |
| } |
| } |
| |
| template <typename T> |
| void test_integer() { |
| test_integer_to_chars<T>(); |
| test_integer_from_chars<T>(); |
| } |
| |
| void all_integer_tests() { |
| test_integer<char>(); |
| test_integer<signed char>(); |
| test_integer<unsigned char>(); |
| test_integer<short>(); |
| test_integer<unsigned short>(); |
| test_integer<int>(); |
| test_integer<unsigned int>(); |
| test_integer<long>(); |
| test_integer<unsigned long>(); |
| test_integer<long long>(); |
| test_integer<unsigned long long>(); |
| |
| // Test overflow scenarios. |
| test_from_chars<unsigned int>("4294967289", 10, 10, errc{}, 4294967289U); // not risky |
| test_from_chars<unsigned int>("4294967294", 10, 10, errc{}, 4294967294U); // risky with good digit |
| test_from_chars<unsigned int>("4294967295", 10, 10, errc{}, 4294967295U); // risky with max digit |
| test_from_chars<unsigned int>("4294967296", 10, 10, out_ran); // risky with bad digit |
| test_from_chars<unsigned int>("4294967300", 10, 10, out_ran); // beyond risky |
| |
| test_from_chars<int>("2147483639", 10, 10, errc{}, 2147483639); // not risky |
| test_from_chars<int>("2147483646", 10, 10, errc{}, 2147483646); // risky with good digit |
| test_from_chars<int>("2147483647", 10, 10, errc{}, 2147483647); // risky with max digit |
| test_from_chars<int>("2147483648", 10, 10, out_ran); // risky with bad digit |
| test_from_chars<int>("2147483650", 10, 10, out_ran); // beyond risky |
| |
| test_from_chars<int>("-2147483639", 10, 11, errc{}, -2147483639); // not risky |
| test_from_chars<int>("-2147483647", 10, 11, errc{}, -2147483647); // risky with good digit |
| test_from_chars<int>("-2147483648", 10, 11, errc{}, -2147483647 - 1); // risky with max digit |
| test_from_chars<int>("-2147483649", 10, 11, out_ran); // risky with bad digit |
| test_from_chars<int>("-2147483650", 10, 11, out_ran); // beyond risky |
| } |
| |
| void assert_message_bits(const bool b, const char* const msg, const std::uint32_t bits) { |
| if (!b) { |
| fprintf(stderr, "%s failed for 0x%08zX\n", msg, static_cast<std::size_t>(bits)); |
| fprintf(stderr, "This is a randomized test.\n"); |
| fprintf(stderr, "DO NOT IGNORE/RERUN THIS FAILURE.\n"); |
| fprintf(stderr, "You must report it to the STL maintainers.\n"); |
| abort(); |
| } |
| } |
| |
| void assert_message_bits(const bool b, const char* const msg, const std::uint64_t bits) { |
| if (!b) { |
| // libc++ uses long for 64-bit values. |
| fprintf(stderr, "%s failed for 0x%016llX\n", msg, static_cast<unsigned long long>(bits)); |
| fprintf(stderr, "This is a randomized test.\n"); |
| fprintf(stderr, "DO NOT IGNORE/RERUN THIS FAILURE.\n"); |
| fprintf(stderr, "You must report it to the STL maintainers.\n"); |
| abort(); |
| } |
| } |
| |
| constexpr std::uint32_t FractionBits = 10; // Tunable for test coverage vs. performance. |
| static_assert(FractionBits >= 1, "Must test at least 1 fraction bit."); |
| static_assert(FractionBits <= 23, "There are only 23 fraction bits in a float."); |
| |
| constexpr std::uint32_t Fractions = 1U << FractionBits; |
| constexpr std::uint32_t Mask32 = ~((1U << FractionBits) - 1U); |
| constexpr std::uint64_t Mask64 = ~((1ULL << FractionBits) - 1ULL); |
| |
| constexpr std::uint32_t PrefixesToTest = 100; // Tunable for test coverage vs. performance. |
| static_assert(PrefixesToTest >= 1, "Must test at least 1 prefix."); |
| |
| constexpr std::uint32_t PrefixLimit = 2 // sign bit |
| * 255 // non-INF/NAN exponents for float |
| * (1U << (23 - FractionBits)); // fraction bits in prefix |
| static_assert(PrefixesToTest <= PrefixLimit, "Too many prefixes."); |
| |
| template <bool IsDouble> |
| void test_floating_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint32_t> prefix) { |
| |
| using UIntType = conditional_t<IsDouble, std::uint64_t, std::uint32_t>; |
| using FloatingType = conditional_t<IsDouble, double, float>; |
| |
| // "-1.2345678901234567e-100" or "-1.23456789e-10" |
| constexpr std::size_t buffer_size = IsDouble ? 24 : 15; |
| char buffer[buffer_size]; |
| // TODO Enable once std::from_chars has floating point support. |
| #if 0 |
| FloatingType val; |
| #endif |
| |
| // Exact sizes are difficult to prove for fixed notation. |
| // This must be at least (IsDouble ? 327 : 48), and I suspect that that's exact. |
| // Here's a loose upper bound: |
| // 1 character for a negative sign |
| // + 325 (for double; 46 for float) characters in the "0.000~~~000" prefix of the min subnormal |
| // + 17 (for double; 9 for float) characters for round-trip digits |
| constexpr std::size_t fixed_buffer_size = IsDouble ? 1 + 325 + 17 : 1 + 46 + 9; |
| char fixed_buffer[fixed_buffer_size]; |
| |
| // worst case: negative sign + max normal + null terminator |
| constexpr std::size_t stdio_buffer_size = 1 + (IsDouble ? 309 : 39) + 1; |
| char stdio_buffer[stdio_buffer_size]; |
| |
| for (std::uint32_t frac = 0; frac < Fractions; ++frac) { |
| const UIntType bits = prefix + frac; |
| const FloatingType input = _Bit_cast<FloatingType>(bits); |
| |
| { |
| const auto to_result = to_chars(buffer, end(buffer), input, chars_format::scientific); |
| assert_message_bits(to_result.ec == errc{}, "to_result.ec", bits); |
| // TODO Enable once std::from_chars has floating point support. |
| #if 0 |
| const char* const last = to_result.ptr; |
| |
| const auto from_result = from_chars(buffer, last, val); |
| |
| assert_message_bits(from_result.ptr == last, "from_result.ptr", bits); |
| assert_message_bits(from_result.ec == errc{}, "from_result.ec", bits); |
| assert_message_bits(_Bit_cast<UIntType>(val) == bits, "round-trip", bits); |
| #endif |
| } |
| |
| { |
| // Also verify that to_chars() and sprintf_s() emit the same output for integers in fixed notation. |
| const auto fixed_result = to_chars(fixed_buffer, end(fixed_buffer), input, chars_format::fixed); |
| assert_message_bits(fixed_result.ec == errc{}, "fixed_result.ec", bits); |
| const string_view fixed_sv(fixed_buffer, static_cast<std::size_t>(fixed_result.ptr - fixed_buffer)); |
| |
| if (find(fixed_sv.begin(), fixed_sv.end(), '.') == fixed_sv.end()) { |
| const int stdio_ret = sprintf_s(stdio_buffer, size(stdio_buffer), "%.0f", input); |
| assert_message_bits(stdio_ret != -1, "stdio_ret", bits); |
| const string_view stdio_sv(stdio_buffer); |
| assert_message_bits(fixed_sv == stdio_sv, "fixed_sv", bits); |
| } |
| } |
| } |
| } |
| |
| template <bool IsDouble> |
| void test_floating_hex_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint32_t> prefix) { |
| |
| using UIntType = conditional_t<IsDouble, std::uint64_t, std::uint32_t>; |
| using FloatingType = conditional_t<IsDouble, double, float>; |
| |
| // The precision is the number of hexits after the decimal point. |
| // These hexits correspond to the explicitly stored fraction bits. |
| // double explicitly stores 52 fraction bits. 52 / 4 == 13, so we need 13 hexits. |
| // float explicitly stores 23 fraction bits. 23 / 4 == 5.75, so we need 6 hexits. |
| |
| // "-1.fffffffffffffp+1023" or "-1.fffffep+127" |
| constexpr std::size_t buffer_size = IsDouble ? 22 : 14; |
| char buffer[buffer_size]; |
| // TODO Enable once std::from_chars has floating point support. |
| #if 0 |
| FloatingType val; |
| #endif |
| |
| for (std::uint32_t frac = 0; frac < Fractions; ++frac) { |
| const UIntType bits = prefix + frac; |
| const FloatingType input = _Bit_cast<FloatingType>(bits); |
| |
| const auto to_result = to_chars(buffer, end(buffer), input, chars_format::hex); |
| assert_message_bits(to_result.ec == errc{}, "(hex) to_result.ec", bits); |
| // TODO Enable once std::from_chars has floating point support. |
| #if 0 |
| const char* const last = to_result.ptr; |
| |
| const auto from_result = from_chars(buffer, last, val, chars_format::hex); |
| |
| assert_message_bits(from_result.ptr == last, "(hex) from_result.ptr", bits); |
| assert_message_bits(from_result.ec == errc{}, "(hex) from_result.ec", bits); |
| assert_message_bits(_Bit_cast<UIntType>(val) == bits, "(hex) round-trip", bits); |
| #endif |
| } |
| } |
| |
| template <bool IsDouble> |
| void test_floating_precision_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint32_t> prefix) { |
| |
| using UIntType = conditional_t<IsDouble, std::uint64_t, std::uint32_t>; |
| using FloatingType = conditional_t<IsDouble, double, float>; |
| |
| // Precision for min subnormal in fixed notation. (More than enough for scientific notation.) |
| constexpr int precision = IsDouble ? 1074 : 149; |
| |
| // Number of digits for max normal in fixed notation. |
| constexpr int max_integer_length = IsDouble ? 309 : 39; |
| |
| // Size for fixed notation. (More than enough for scientific notation.) |
| constexpr std::size_t charconv_buffer_size = 1 // negative sign |
| + max_integer_length // integer digits |
| + 1 // decimal point |
| + precision; // fractional digits |
| char charconv_buffer[charconv_buffer_size]; |
| |
| constexpr std::size_t stdio_buffer_size = charconv_buffer_size + 1; // null terminator |
| char stdio_buffer[stdio_buffer_size]; |
| |
| // 1 character for a negative sign |
| // + worst cases: 0x1.fffffffffffffp-1022 and 0x1.fffffep-126f |
| constexpr std::size_t general_buffer_size = 1 + (IsDouble ? 773 : 117); |
| char general_buffer[general_buffer_size]; |
| char general_stdio_buffer[general_buffer_size + 1]; // + null terminator |
| |
| for (std::uint32_t frac = 0; frac < Fractions; ++frac) { |
| const UIntType bits = prefix + frac; |
| const FloatingType input = _Bit_cast<FloatingType>(bits); |
| |
| auto result = to_chars(charconv_buffer, end(charconv_buffer), input, chars_format::fixed, precision); |
| assert_message_bits(result.ec == errc{}, "to_chars fixed precision", bits); |
| string_view charconv_sv(charconv_buffer, static_cast<std::size_t>(result.ptr - charconv_buffer)); |
| |
| int stdio_ret = sprintf_s(stdio_buffer, size(stdio_buffer), "%.*f", precision, input); |
| assert_message_bits(stdio_ret != -1, "sprintf_s fixed precision", bits); |
| string_view stdio_sv(stdio_buffer); |
| |
| assert_message_bits(charconv_sv == stdio_sv, "fixed precision output", bits); |
| |
| |
| result = to_chars(charconv_buffer, end(charconv_buffer), input, chars_format::scientific, precision); |
| assert_message_bits(result.ec == errc{}, "to_chars scientific precision", bits); |
| charconv_sv = string_view(charconv_buffer, static_cast<std::size_t>(result.ptr - charconv_buffer)); |
| |
| stdio_ret = sprintf_s(stdio_buffer, size(stdio_buffer), "%.*e", precision, input); |
| assert_message_bits(stdio_ret != -1, "sprintf_s scientific precision", bits); |
| stdio_sv = stdio_buffer; |
| |
| assert_message_bits(charconv_sv == stdio_sv, "scientific precision output", bits); |
| |
| |
| result = to_chars(general_buffer, end(general_buffer), input, chars_format::general, 5000); |
| assert_message_bits(result.ec == errc{}, "to_chars general precision", bits); |
| charconv_sv = string_view(general_buffer, static_cast<std::size_t>(result.ptr - general_buffer)); |
| |
| stdio_ret = sprintf_s(general_stdio_buffer, size(general_stdio_buffer), "%.5000g", input); |
| assert_message_bits(stdio_ret != -1, "sprintf_s general precision", bits); |
| stdio_sv = general_stdio_buffer; |
| |
| assert_message_bits(charconv_sv == stdio_sv, "general precision output", bits); |
| } |
| } |
| |
| void test_floating_prefixes(mt19937_64& mt64) { |
| { |
| set<std::uint64_t> prefixes64; |
| |
| while (prefixes64.size() < PrefixesToTest) { |
| const std::uint64_t val = mt64(); |
| |
| if ((val & 0x7FF0000000000000ULL) != 0x7FF0000000000000ULL) { // skip INF/NAN |
| prefixes64.insert(val & Mask64); |
| } |
| } |
| |
| for (const auto& prefix : prefixes64) { |
| test_floating_prefix<true>(prefix); |
| test_floating_precision_prefix<true>(prefix); |
| } |
| |
| test_floating_hex_prefix<true>(*prefixes64.begin()); // save time by testing fewer hexfloats |
| } |
| |
| { |
| set<std::uint32_t> prefixes32; |
| |
| while (prefixes32.size() < PrefixesToTest) { |
| const std::uint32_t val = static_cast<std::uint32_t>(mt64()); |
| |
| if ((val & 0x7F800000U) != 0x7F800000U) { // skip INF/NAN |
| prefixes32.insert(val & Mask32); |
| } |
| } |
| |
| for (const auto& prefix : prefixes32) { |
| test_floating_prefix<false>(prefix); |
| test_floating_precision_prefix<false>(prefix); |
| } |
| |
| test_floating_hex_prefix<false>(*prefixes32.begin()); // save time by testing fewer hexfloats |
| } |
| } |
| |
| // TODO Enable once std::from_chars has floating point support. |
| #if 0 |
| template <typename T> |
| void test_floating_from_chars(const chars_format fmt) { |
| test_from_chars<T>("", fmt, 0, inv_arg); // no characters |
| test_from_chars<T>("@1", fmt, 0, inv_arg); // '@' is bogus |
| test_from_chars<T>("z1", fmt, 0, inv_arg); // 'z' is bogus |
| test_from_chars<T>(".", fmt, 0, inv_arg); // '.' without digits is bogus |
| test_from_chars<T>("+1", fmt, 0, inv_arg); // '+' is bogus |
| test_from_chars<T>(" 1", fmt, 0, inv_arg); // ' ' is bogus |
| test_from_chars<T>("p5", fmt, 0, inv_arg); // binary-exponent-part without digits is bogus |
| test_from_chars<T>("in", fmt, 0, inv_arg); // incomplete inf is bogus |
| test_from_chars<T>("na", fmt, 0, inv_arg); // incomplete nan is bogus |
| |
| test_from_chars<T>("-", fmt, 0, inv_arg); // '-' followed by no characters |
| test_from_chars<T>("-@1", fmt, 0, inv_arg); // '-' followed by bogus '@' |
| test_from_chars<T>("-z1", fmt, 0, inv_arg); // '-' followed by bogus 'z' |
| test_from_chars<T>("-.", fmt, 0, inv_arg); // '-' followed by bogus '.' |
| test_from_chars<T>("-+1", fmt, 0, inv_arg); // '-' followed by bogus '+' |
| test_from_chars<T>("- 1", fmt, 0, inv_arg); // '-' followed by bogus ' ' |
| test_from_chars<T>("-p5", fmt, 0, inv_arg); // '-' followed by bogus binary-exponent-part |
| test_from_chars<T>("-in", fmt, 0, inv_arg); // '-' followed by bogus incomplete inf |
| test_from_chars<T>("-na", fmt, 0, inv_arg); // '-' followed by bogus incomplete nan |
| test_from_chars<T>("--1", fmt, 0, inv_arg); // '-' can't be repeated |
| |
| if (fmt != chars_format::hex) { // "e5" are valid hexits |
| test_from_chars<T>("e5", fmt, 0, inv_arg); // exponent-part without digits is bogus |
| test_from_chars<T>("-e5", fmt, 0, inv_arg); // '-' followed by bogus exponent-part |
| } |
| |
| constexpr T inf = numeric_limits<T>::infinity(); |
| constexpr T qnan = numeric_limits<T>::quiet_NaN(); |
| |
| test_from_chars<T>("InF", fmt, 3, errc{}, inf); |
| test_from_chars<T>("infinite", fmt, 3, errc{}, inf); |
| test_from_chars<T>("iNfInItY", fmt, 8, errc{}, inf); |
| test_from_chars<T>("InfinityMeow", fmt, 8, errc{}, inf); |
| |
| test_from_chars<T>("-InF", fmt, 4, errc{}, -inf); |
| test_from_chars<T>("-infinite", fmt, 4, errc{}, -inf); |
| test_from_chars<T>("-iNfInItY", fmt, 9, errc{}, -inf); |
| test_from_chars<T>("-InfinityMeow", fmt, 9, errc{}, -inf); |
| |
| test_from_chars<T>("NaN", fmt, 3, errc{}, qnan); |
| test_from_chars<T>("nanotech", fmt, 3, errc{}, qnan); |
| test_from_chars<T>("nan(", fmt, 3, errc{}, qnan); |
| test_from_chars<T>("nan(@)", fmt, 3, errc{}, qnan); |
| test_from_chars<T>("nan(()", fmt, 3, errc{}, qnan); |
| test_from_chars<T>("nan(abc", fmt, 3, errc{}, qnan); |
| test_from_chars<T>("nan()", fmt, 5, errc{}, qnan); |
| test_from_chars<T>("nan(abc)def", fmt, 8, errc{}, qnan); |
| test_from_chars<T>("nan(_09AZaz)", fmt, 12, errc{}, qnan); |
| test_from_chars<T>("nan(int)", fmt, 8, errc{}, qnan); |
| test_from_chars<T>("nan(snap)", fmt, 9, errc{}, qnan); |
| |
| test_from_chars<T>("-NaN", fmt, 4, errc{}, -qnan); |
| test_from_chars<T>("-nanotech", fmt, 4, errc{}, -qnan); |
| test_from_chars<T>("-nan(", fmt, 4, errc{}, -qnan); |
| test_from_chars<T>("-nan(@)", fmt, 4, errc{}, -qnan); |
| test_from_chars<T>("-nan(()", fmt, 4, errc{}, -qnan); |
| test_from_chars<T>("-nan(abc", fmt, 4, errc{}, -qnan); |
| test_from_chars<T>("-nan()", fmt, 6, errc{}, -qnan); |
| test_from_chars<T>("-nan(abc)def", fmt, 9, errc{}, -qnan); |
| test_from_chars<T>("-nan(_09AZaz)", fmt, 13, errc{}, -qnan); |
| test_from_chars<T>("-nan(int)", fmt, 9, errc{}, -qnan); |
| test_from_chars<T>("-nan(snap)", fmt, 10, errc{}, -qnan); |
| |
| // The UCRT considers indeterminate NaN to be negative quiet NaN with no payload bits set. |
| // It parses "nan(ind)" and "-nan(ind)" identically. |
| test_from_chars<T>("nan(InD)", fmt, 8, errc{}, -qnan); |
| test_from_chars<T>("-nan(InD)", fmt, 9, errc{}, -qnan); |
| |
| test_from_chars<T>("nan(SnAn)", fmt, 9, errc{}, nullopt, TestFromCharsMode::SignalingNaN); |
| test_from_chars<T>("-nan(SnAn)", fmt, 10, errc{}, nullopt, TestFromCharsMode::SignalingNaN); |
| |
| switch (fmt) { |
| case chars_format::general: |
| test_from_chars<T>("1729", fmt, 4, errc{}, T{1729}); |
| test_from_chars<T>("1729e3", fmt, 6, errc{}, T{1729000}); |
| test_from_chars<T>("10", fmt, 2, errc{}, T{10}); |
| test_from_chars<T>("11.", fmt, 3, errc{}, T{11}); |
| test_from_chars<T>("12.13", fmt, 5, errc{}, static_cast<T>(12.13)); // avoid truncation warning |
| test_from_chars<T>(".14", fmt, 3, errc{}, static_cast<T>(.14)); // avoid truncation warning |
| test_from_chars<T>("20e5", fmt, 4, errc{}, T{2000000}); |
| test_from_chars<T>("21.e5", fmt, 5, errc{}, T{2100000}); |
| test_from_chars<T>("22.23e5", fmt, 7, errc{}, T{2223000}); |
| test_from_chars<T>(".24e5", fmt, 5, errc{}, T{24000}); |
| test_from_chars<T>("33e+5", fmt, 5, errc{}, T{3300000}); |
| test_from_chars<T>("33e-5", fmt, 5, errc{}, static_cast<T>(.00033)); // avoid truncation warning |
| test_from_chars<T>("4E7", fmt, 3, errc{}, T{40000000}); |
| test_from_chars<T>("-00123abc", fmt, 6, errc{}, T{-123}); |
| test_from_chars<T>(".0045000", fmt, 8, errc{}, static_cast<T>(.0045)); // avoid truncation warning |
| test_from_chars<T>("000", fmt, 3, errc{}, T{0}); |
| test_from_chars<T>("0e9999", fmt, 6, errc{}, T{0}); |
| test_from_chars<T>("0e-9999", fmt, 7, errc{}, T{0}); |
| test_from_chars<T>("-000", fmt, 4, errc{}, T{-0.0}); |
| test_from_chars<T>("-0e9999", fmt, 7, errc{}, T{-0.0}); |
| test_from_chars<T>("-0e-9999", fmt, 8, errc{}, T{-0.0}); |
| test_from_chars<T>("1e9999", fmt, 6, errc::result_out_of_range, inf); |
| test_from_chars<T>("-1e9999", fmt, 7, errc::result_out_of_range, -inf); |
| test_from_chars<T>("1e-9999", fmt, 7, errc::result_out_of_range, T{0}); |
| test_from_chars<T>("-1e-9999", fmt, 8, errc::result_out_of_range, T{-0.0}); |
| test_from_chars<T>("1" + string(6000, '0'), fmt, 6001, errc::result_out_of_range, inf); |
| test_from_chars<T>("-1" + string(6000, '0'), fmt, 6002, errc::result_out_of_range, -inf); |
| test_from_chars<T>("." + string(6000, '0') + "1", fmt, 6002, errc::result_out_of_range, T{0}); |
| test_from_chars<T>("-." + string(6000, '0') + "1", fmt, 6003, errc::result_out_of_range, T{-0.0}); |
| test_from_chars<T>("1" + string(500, '0'), fmt, 501, errc::result_out_of_range, inf); |
| test_from_chars<T>("-1" + string(500, '0'), fmt, 502, errc::result_out_of_range, -inf); |
| test_from_chars<T>("." + string(500, '0') + "1", fmt, 502, errc::result_out_of_range, T{0}); |
| test_from_chars<T>("-." + string(500, '0') + "1", fmt, 503, errc::result_out_of_range, T{-0.0}); |
| break; |
| case chars_format::scientific: |
| test_from_chars<T>("1729", fmt, 0, inv_arg); |
| test_from_chars<T>("1729e3", fmt, 6, errc{}, T{1729000}); |
| break; |
| case chars_format::fixed: |
| test_from_chars<T>("1729", fmt, 4, errc{}, T{1729}); |
| test_from_chars<T>("1729e3", fmt, 4, errc{}, T{1729}); |
| break; |
| case chars_format::hex: |
| test_from_chars<T>("0x123", fmt, 1, errc{}, T{0}); |
| test_from_chars<T>("a0", fmt, 2, errc{}, T{160}); |
| test_from_chars<T>("a1.", fmt, 3, errc{}, T{161}); |
| test_from_chars<T>("a2.a3", fmt, 5, errc{}, T{162.63671875}); |
| test_from_chars<T>(".a4", fmt, 3, errc{}, T{0.640625}); |
| test_from_chars<T>("a0p5", fmt, 4, errc{}, T{5120}); |
| test_from_chars<T>("a1.p5", fmt, 5, errc{}, T{5152}); |
| test_from_chars<T>("a2.a3p5", fmt, 7, errc{}, T{5204.375}); |
| test_from_chars<T>(".a4p5", fmt, 5, errc{}, T{20.5}); |
| test_from_chars<T>("a0p+5", fmt, 5, errc{}, T{5120}); |
| test_from_chars<T>("a0p-5", fmt, 5, errc{}, T{5}); |
| test_from_chars<T>("ABCDEFP3", fmt, 8, errc{}, T{90075000}); |
| test_from_chars<T>("-00cdrom", fmt, 5, errc{}, T{-205}); |
| test_from_chars<T>(".00ef000", fmt, 8, errc{}, T{0.0036468505859375}); |
| test_from_chars<T>("000", fmt, 3, errc{}, T{0}); |
| test_from_chars<T>("0p9999", fmt, 6, errc{}, T{0}); |
| test_from_chars<T>("0p-9999", fmt, 7, errc{}, T{0}); |
| test_from_chars<T>("-000", fmt, 4, errc{}, T{-0.0}); |
| test_from_chars<T>("-0p9999", fmt, 7, errc{}, T{-0.0}); |
| test_from_chars<T>("-0p-9999", fmt, 8, errc{}, T{-0.0}); |
| test_from_chars<T>("1p9999", fmt, 6, errc::result_out_of_range, inf); |
| test_from_chars<T>("-1p9999", fmt, 7, errc::result_out_of_range, -inf); |
| test_from_chars<T>("1p-9999", fmt, 7, errc::result_out_of_range, T{0}); |
| test_from_chars<T>("-1p-9999", fmt, 8, errc::result_out_of_range, T{-0.0}); |
| test_from_chars<T>("1" + string(2000, '0'), fmt, 2001, errc::result_out_of_range, inf); |
| test_from_chars<T>("-1" + string(2000, '0'), fmt, 2002, errc::result_out_of_range, -inf); |
| test_from_chars<T>("." + string(2000, '0') + "1", fmt, 2002, errc::result_out_of_range, T{0}); |
| test_from_chars<T>("-." + string(2000, '0') + "1", fmt, 2003, errc::result_out_of_range, T{-0.0}); |
| test_from_chars<T>("1" + string(300, '0'), fmt, 301, errc::result_out_of_range, inf); |
| test_from_chars<T>("-1" + string(300, '0'), fmt, 302, errc::result_out_of_range, -inf); |
| test_from_chars<T>("." + string(300, '0') + "1", fmt, 302, errc::result_out_of_range, T{0}); |
| test_from_chars<T>("-." + string(300, '0') + "1", fmt, 303, errc::result_out_of_range, T{-0.0}); |
| break; |
| } |
| } |
| #endif |
| |
| template <typename T> |
| void test_floating_to_chars( |
| const T value, const optional<chars_format> opt_fmt, const optional<int> opt_precision, const string_view correct) { |
| |
| test_common_to_chars(value, opt_fmt, opt_precision, correct); |
| } |
| |
| void all_floating_tests(mt19937_64& mt64) { |
| test_floating_prefixes(mt64); |
| |
| // TODO Enable once std::from_chars has floating point support. |
| #if 0 |
| for (const auto& fmt : {chars_format::general, chars_format::scientific, chars_format::fixed, chars_format::hex}) { |
| test_floating_from_chars<float>(fmt); |
| test_floating_from_chars<double>(fmt); |
| } |
| |
| // Test rounding. |
| |
| // See float_from_chars_test_cases.hpp in this directory. |
| for (const auto& t : float_from_chars_test_cases) { |
| test_from_chars<float>(t.input, t.fmt, t.correct_idx, t.correct_ec, t.correct_value); |
| } |
| |
| // See double_from_chars_test_cases.hpp in this directory. |
| for (const auto& t : double_from_chars_test_cases) { |
| test_from_chars<double>(t.input, t.fmt, t.correct_idx, t.correct_ec, t.correct_value); |
| } |
| |
| { |
| // See LWG-2403. This number (exactly 0x1.fffffd00000004 in infinite precision) behaves differently |
| // when parsed as double and converted to float, versus being parsed as float directly. |
| const char* const lwg_2403 = "1.999999821186065729339276231257827021181583404541015625"; |
| constexpr float correct_float = 0x1.fffffep0f; |
| constexpr double correct_double = 0x1.fffffdp0; |
| constexpr float twice_rounded_float = 0x1.fffffcp0f; |
| |
| test_from_chars<float>(lwg_2403, chars_format::general, 56, errc{}, correct_float); |
| test_from_chars<double>(lwg_2403, chars_format::general, 56, errc{}, correct_double); |
| static_assert(static_cast<float>(correct_double) == twice_rounded_float); |
| } |
| |
| // See floating_point_test_cases.hpp. |
| for (const auto& p : floating_point_test_cases_float) { |
| test_from_chars<float>(p.first, chars_format::general, strlen(p.first), errc{}, _Bit_cast<float>(p.second)); |
| } |
| |
| for (const auto& p : floating_point_test_cases_double) { |
| test_from_chars<double>(p.first, chars_format::general, strlen(p.first), errc{}, _Bit_cast<double>(p.second)); |
| } |
| #endif |
| // See float_to_chars_test_cases.hpp in this directory. |
| for (const auto& t : float_to_chars_test_cases) { |
| if (t.fmt == chars_format{}) { |
| test_floating_to_chars(t.value, nullopt, nullopt, t.correct); |
| } else { |
| test_floating_to_chars(t.value, t.fmt, nullopt, t.correct); |
| } |
| } |
| |
| // See double_to_chars_test_cases.hpp in this directory. |
| for (const auto& t : double_to_chars_test_cases) { |
| if (t.fmt == chars_format{}) { |
| test_floating_to_chars(t.value, nullopt, nullopt, t.correct); |
| } else { |
| test_floating_to_chars(t.value, t.fmt, nullopt, t.correct); |
| } |
| } |
| |
| // See corresponding headers in this directory. |
| for (const auto& t : float_hex_precision_to_chars_test_cases) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : float_fixed_precision_to_chars_test_cases) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : float_scientific_precision_to_chars_test_cases) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : float_general_precision_to_chars_test_cases) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_hex_precision_to_chars_test_cases) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_fixed_precision_to_chars_test_cases_1) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_fixed_precision_to_chars_test_cases_2) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_fixed_precision_to_chars_test_cases_3) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_fixed_precision_to_chars_test_cases_4) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_scientific_precision_to_chars_test_cases_1) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_scientific_precision_to_chars_test_cases_2) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_scientific_precision_to_chars_test_cases_3) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_scientific_precision_to_chars_test_cases_4) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| for (const auto& t : double_general_precision_to_chars_test_cases) { |
| test_floating_to_chars(t.value, t.fmt, t.precision, t.correct); |
| } |
| } |
| |
| int main(int argc, char** argv) { |
| const auto start = chrono::steady_clock::now(); |
| |
| mt19937_64 mt64; |
| |
| initialize_randomness(mt64, argc, argv); |
| |
| all_integer_tests(); |
| |
| all_floating_tests(mt64); |
| |
| const auto finish = chrono::steady_clock::now(); |
| const long long ms = chrono::duration_cast<chrono::milliseconds>(finish - start).count(); |
| |
| puts("PASS"); |
| printf("Randomized test cases: %zu\n", static_cast<std::size_t>(PrefixesToTest * Fractions)); |
| printf("Total time: %lld ms\n", ms); |
| |
| if (ms < 3'000) { |
| puts("That was fast. Consider tuning PrefixesToTest and FractionBits to test more cases."); |
| } else if (ms > 30'000) { |
| puts("That was slow. Consider tuning PrefixesToTest and FractionBits to test fewer cases."); |
| } |
| } |