| 1 | // Copyright (c) Microsoft Corporation. |
| 2 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 3 | |
| 4 | #include "test.hpp" |
| 5 | |
| 6 | #include <algorithm> |
| 7 | #include <array> |
| 8 | #include <assert.h> |
| 9 | #include <charconv> |
| 10 | #include <chrono> |
| 11 | #include <cmath> |
| 12 | #include <functional> |
| 13 | #include <iterator> |
| 14 | #include <limits> |
| 15 | #include <locale> |
| 16 | #include <optional> |
| 17 | #include <random> |
| 18 | #include <set> |
| 19 | #include <stdint.h> |
| 20 | #include <stdio.h> |
| 21 | #include <stdlib.h> |
| 22 | #include <string.h> |
| 23 | #include <string> |
| 24 | #include <string_view> |
| 25 | #include <system_error> |
| 26 | #include <type_traits> |
| 27 | #include <utility> |
| 28 | #include <vector> |
| 29 | |
| 30 | #include "double_fixed_precision_to_chars_test_cases_1.hpp" |
| 31 | #include "double_fixed_precision_to_chars_test_cases_2.hpp" |
| 32 | #include "double_fixed_precision_to_chars_test_cases_3.hpp" |
| 33 | #include "double_fixed_precision_to_chars_test_cases_4.hpp" |
| 34 | #include "double_from_chars_test_cases.hpp" |
| 35 | #include "double_general_precision_to_chars_test_cases.hpp" |
| 36 | #include "double_hex_precision_to_chars_test_cases.hpp" |
| 37 | #include "double_scientific_precision_to_chars_test_cases_1.hpp" |
| 38 | #include "double_scientific_precision_to_chars_test_cases_2.hpp" |
| 39 | #include "double_scientific_precision_to_chars_test_cases_3.hpp" |
| 40 | #include "double_scientific_precision_to_chars_test_cases_4.hpp" |
| 41 | #include "double_to_chars_test_cases.hpp" |
| 42 | #include "float_fixed_precision_to_chars_test_cases.hpp" |
| 43 | #include "float_from_chars_test_cases.hpp" |
| 44 | #include "float_general_precision_to_chars_test_cases.hpp" |
| 45 | #include "float_hex_precision_to_chars_test_cases.hpp" |
| 46 | #include "float_scientific_precision_to_chars_test_cases.hpp" |
| 47 | #include "float_to_chars_test_cases.hpp" |
| 48 | #include "floating_point_test_cases.hpp" |
| 49 | |
| 50 | using namespace std; |
| 51 | |
| 52 | void initialize_randomness(mt19937_64& mt64, const int argc, char** const /*argv*/) { |
| 53 | constexpr std::size_t n = mt19937_64::state_size; |
| 54 | constexpr std::size_t w = mt19937_64::word_size; |
| 55 | static_assert(w % 32 == 0); |
| 56 | constexpr std::size_t k = w / 32; |
| 57 | |
| 58 | vector<std::uint32_t> vec(n * k); |
| 59 | |
| 60 | puts(s: "USAGE:" ); |
| 61 | puts(s: "test.exe : generates seed data from random_device." ); |
| 62 | |
| 63 | if (argc == 1) { |
| 64 | random_device rd; |
| 65 | generate(first: vec.begin(), last: vec.end(), gen: ref(t&: rd)); |
| 66 | puts(s: "Generated seed data." ); |
| 67 | } else { |
| 68 | puts(s: "ERROR: Too many command-line arguments." ); |
| 69 | abort(); |
| 70 | } |
| 71 | |
| 72 | puts(s: "SEED DATA:" ); |
| 73 | for (const auto& elem : vec) { |
| 74 | printf(format: "%zu " , static_cast<std::size_t>(elem)); |
| 75 | } |
| 76 | printf(format: "\n" ); |
| 77 | |
| 78 | seed_seq seq(vec.cbegin(), vec.cend()); |
| 79 | |
| 80 | mt64.seed(q&: seq); |
| 81 | |
| 82 | puts(s: "Successfully seeded mt64. First three values:" ); |
| 83 | for (int i = 0; i < 3; ++i) { |
| 84 | // libc++ uses long for 64-bit values. |
| 85 | printf(format: "0x%016llX\n" , static_cast<unsigned long long>(mt64())); |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | static_assert((chars_format::scientific & chars_format::fixed) == chars_format{}); |
| 90 | static_assert((chars_format::scientific & chars_format::hex) == chars_format{}); |
| 91 | static_assert((chars_format::fixed & chars_format::hex) == chars_format{}); |
| 92 | static_assert(chars_format::general == (chars_format::fixed | chars_format::scientific)); |
| 93 | |
| 94 | template <typename T, typename Optional> |
| 95 | void test_common_to_chars( |
| 96 | const T value, const Optional opt_arg, const optional<int> opt_precision, const string_view correct) { |
| 97 | |
| 98 | // Important: Test every effective buffer size from 0 through correct.size() and slightly beyond. For the sizes |
| 99 | // less than correct.size(), this verifies that the too-small buffer is correctly detected, and that we don't |
| 100 | // attempt to write outside of it, even by a single char. (This exhaustive validation is necessary because the |
| 101 | // implementation must check whenever it attempts to write. Sometimes we can calculate the total size and perform |
| 102 | // a single check, but sometimes we need to check when writing each part of the result.) Testing correct.size() |
| 103 | // verifies that we can succeed without overrunning, and testing slightly larger sizes verifies that we can succeed |
| 104 | // without attempting to write to extra chars even when they're available. Finally, we also verify that we aren't |
| 105 | // underrunning the buffer. This is a concern because sometimes we walk backwards when rounding. |
| 106 | |
| 107 | constexpr std::size_t BufferPrefix = 20; // detect buffer underruns (specific value isn't important) |
| 108 | |
| 109 | constexpr std::size_t Space = is_integral_v<T> ? 1 + 64 // worst case: -2^63 in binary |
| 110 | : is_same_v<T, float> |
| 111 | ? 1 + 151 // worst case: negative min subnormal float, fixed notation |
| 112 | : 1 + 1076; // worst case: negative min subnormal double, fixed notation |
| 113 | |
| 114 | constexpr std::size_t BufferSuffix = 30; // detect buffer overruns (specific value isn't important) |
| 115 | |
| 116 | array<char, BufferPrefix + Space + BufferSuffix> buff; |
| 117 | |
| 118 | char* const buff_begin = buff.data(); |
| 119 | char* const first = buff_begin + BufferPrefix; |
| 120 | char* const buff_end = buff_begin + buff.size(); |
| 121 | |
| 122 | constexpr std::size_t = 3; |
| 123 | static_assert(ExtraChars + 10 < BufferSuffix, |
| 124 | "The specific values aren't important, but there should be plenty of room to detect buffer overruns." ); |
| 125 | |
| 126 | for (std::size_t n = 0; n <= correct.size() + ExtraChars; ++n) { |
| 127 | assert(n <= static_cast<std::size_t>(buff_end - first)); |
| 128 | char* const last = first + n; |
| 129 | |
| 130 | buff.fill('@'); |
| 131 | const auto is_fill_char = [](const char c) { return c == '@'; }; |
| 132 | |
| 133 | to_chars_result result{}; |
| 134 | if (opt_precision.has_value()) { |
| 135 | assert(opt_arg.has_value()); |
| 136 | |
| 137 | if constexpr (is_floating_point_v<T>) { |
| 138 | result = to_chars(first, last, value, opt_arg.value(), opt_precision.value()); |
| 139 | } else { |
| 140 | abort(); |
| 141 | } |
| 142 | } else if (opt_arg.has_value()) { |
| 143 | result = to_chars(first, last, value, opt_arg.value()); |
| 144 | } else { |
| 145 | result = to_chars(first, last, value); |
| 146 | } |
| 147 | |
| 148 | if (n < correct.size()) { |
| 149 | assert(result.ptr == last); |
| 150 | assert(result.ec == errc::value_too_large); |
| 151 | assert(all_of(buff_begin, first, is_fill_char)); |
| 152 | // [first, last) is unspecified |
| 153 | assert(all_of(last, buff_end, is_fill_char)); |
| 154 | } else { |
| 155 | assert(result.ptr == first + correct.size()); |
| 156 | assert(result.ec == errc{}); |
| 157 | assert(all_of(buff_begin, first, is_fill_char)); |
| 158 | assert(equal(first, result.ptr, correct.begin(), correct.end())); |
| 159 | assert(all_of(result.ptr, buff_end, is_fill_char)); |
| 160 | } |
| 161 | } |
| 162 | } |
| 163 | |
| 164 | template <typename T> |
| 165 | void test_integer_to_chars(const T value, const optional<int> opt_base, const string_view correct) { |
| 166 | |
| 167 | test_common_to_chars(value, opt_base, nullopt, correct); |
| 168 | |
| 169 | { // Also test successful from_chars() scenarios. |
| 170 | const char* const correct_first = correct.data(); |
| 171 | const char* const correct_last = correct_first + correct.size(); |
| 172 | |
| 173 | T dest = 0; |
| 174 | |
| 175 | const from_chars_result from_res = |
| 176 | (opt_base.has_value() ? from_chars(correct_first, correct_last, dest, opt_base.value()) |
| 177 | : from_chars(correct_first, correct_last, dest)); |
| 178 | |
| 179 | assert(from_res.ptr == correct_last); |
| 180 | assert(from_res.ec == errc{}); |
| 181 | assert(dest == value); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | // https://www.wolframalpha.com : Table[BaseForm[n * 2 - 1, n], {n, 2, 36}] |
| 186 | constexpr const char* output_max_digit[] = {"skip0" , "skip1" , "11" , "12" , "13" , "14" , "15" , "16" , "17" , "18" , "19" , |
| 187 | "1a" , "1b" , "1c" , "1d" , "1e" , "1f" , "1g" , "1h" , "1i" , "1j" , "1k" , "1l" , "1m" , "1n" , "1o" , "1p" , "1q" , "1r" , "1s" , |
| 188 | "1t" , "1u" , "1v" , "1w" , "1x" , "1y" , "1z" }; |
| 189 | |
| 190 | // https://www.wolframalpha.com : Table[BaseForm[k, n], {k, {MEOW, MEOW, MEOW}}, {n, 2, 36}] |
| 191 | constexpr std::uint64_t stress_chunks_positive = 12000000345000678900ULL; |
| 192 | constexpr pair<std::uint64_t, array<const char*, 37>> output_positive[] = { |
| 193 | {123U, {._M_elems: {"skip0" , "skip1" , "1111011" , "11120" , "1323" , "443" , "323" , "234" , "173" , "146" , "123" , "102" , "a3" , "96" , |
| 194 | "8b" , "83" , "7b" , "74" , "6f" , "69" , "63" , "5i" , "5d" , "58" , "53" , "4n" , "4j" , "4f" , "4b" , "47" , "43" , |
| 195 | "3u" , "3r" , "3o" , "3l" , "3i" , "3f" }}}, |
| 196 | {std::uint64_t{INT8_MAX}, {._M_elems: {"skip0" , "skip1" , "1111111" , "11201" , "1333" , "1002" , "331" , "241" , "177" , "151" , "127" , |
| 197 | "106" , "a7" , "9a" , "91" , "87" , "7f" , "78" , "71" , "6d" , "67" , "61" , "5h" , "5c" , "57" , "52" , |
| 198 | "4n" , "4j" , "4f" , "4b" , "47" , "43" , "3v" , "3s" , "3p" , "3m" , "3j" }}}, |
| 199 | {161U, {._M_elems: {"skip0" , "skip1" , "10100001" , "12222" , "2201" , "1121" , "425" , "320" , "241" , "188" , "161" , "137" , "115" , |
| 200 | "c5" , "b7" , "ab" , "a1" , "98" , "8h" , "89" , "81" , "7e" , "77" , "70" , "6h" , "6b" , "65" , "5q" , "5l" , "5g" , |
| 201 | "5b" , "56" , "51" , "4t" , "4p" , "4l" , "4h" }}}, |
| 202 | {UINT8_MAX, {._M_elems: {"skip0" , "skip1" , "11111111" , "100110" , "3333" , "2010" , "1103" , "513" , "377" , "313" , "255" , "212" , |
| 203 | "193" , "168" , "143" , "120" , "ff" , "f0" , "e3" , "d8" , "cf" , "c3" , "bd" , "b2" , "af" , "a5" , "9l" , "9c" , |
| 204 | "93" , "8n" , "8f" , "87" , "7v" , "7o" , "7h" , "7a" , "73" }}}, |
| 205 | {1729U, {._M_elems: {"skip0" , "skip1" , "11011000001" , "2101001" , "123001" , "23404" , "12001" , "5020" , "3301" , "2331" , "1729" , |
| 206 | "1332" , "1001" , "a30" , "8b7" , "7a4" , "6c1" , "5gc" , "561" , "4f0" , "469" , "3j7" , "3cd" , "364" , "301" , |
| 207 | "2j4" , "2ed" , "2a1" , "25l" , "21i" , "1rj" , "1oo" , "1m1" , "1jd" , "1gt" , "1ee" , "1c1" }}}, |
| 208 | {std::uint64_t{INT16_MAX}, {._M_elems: {"skip0" , "skip1" , "111111111111111" , "1122221121" , "13333333" , "2022032" , "411411" , |
| 209 | "164350" , "77777" , "48847" , "32767" , "22689" , "16b67" , "11bb7" , "bd27" , "9a97" , "7fff" , |
| 210 | "6b68" , "5b27" , "4eeb" , "41i7" , "3b67" , "31f9" , "2flf" , "28l7" , "22ah" , "1mc7" , "1hpg" , |
| 211 | "1dm7" , "19rq" , "16c7" , "1330" , "vvv" , "u2v" , "sbp" , "qq7" , "pa7" }}}, |
| 212 | {57494U, {._M_elems: {"skip0" , "skip1" , "1110000010010110" , "2220212102" , "32002112" , "3314434" , "1122102" , "326423" , "160226" , |
| 213 | "86772" , "57494" , "3a218" , "29332" , "20228" , "16d4a" , "1207e" , "e096" , "bbg0" , "9f82" , "8750" , "73ee" , |
| 214 | "647h" , "58h8" , "4gfh" , "43je" , "3goj" , "3718" , "2onb" , "2h9a" , "2aag" , "23qe" , "1spk" , "1o4m" , "1jq8" , |
| 215 | "1fp0" , "1bwo" , "18d2" }}}, |
| 216 | {UINT16_MAX, {._M_elems: {"skip0" , "skip1" , "1111111111111111" , "10022220020" , "33333333" , "4044120" , "1223223" , "362031" , |
| 217 | "177777" , "108806" , "65535" , "45268" , "31b13" , "23aa2" , "19c51" , "14640" , "ffff" , "d5d0" , "b44f" , |
| 218 | "9aa4" , "83gf" , "71cf" , "638j" , "58k8" , "4hif" , "44la" , "3iof" , "38o6" , "2rgf" , "2jqo" , "2cof" , |
| 219 | "2661" , "1vvv" , "1r5u" , "1mnh" , "1ihf" , "1ekf" }}}, |
| 220 | {71125478U, {._M_elems: {"skip0" , "skip1" , "100001111010100100111100110" , "11221211112210222" , "10033110213212" , |
| 221 | "121202003403" , "11020244342" , "1522361624" , "417244746" , "157745728" , "71125478" , "3716a696" , |
| 222 | "1b9a06b2" , "11973ba8" , "9636514" , "639e338" , "43d49e6" , "2g19gfb" , "21b9d18" , "19dec94" , "124addi" , |
| 223 | "h8f25b" , "dhdfa6" , "b13hg2" , "8m91he" , "7720j3" , "5pgj58" , "4pmelq" , "43k17i" , "3dg8ek" , "2ro898" , |
| 224 | "2f0et8" , "23qif6" , "1qw5lh" , "1j7l7s" , "1cdvli" , "16cgrq" }}}, |
| 225 | {std::uint64_t{INT32_MAX}, |
| 226 | {._M_elems: {"skip0" , "skip1" , "1111111111111111111111111111111" , "12112122212110202101" , "1333333333333333" , |
| 227 | "13344223434042" , "553032005531" , "104134211161" , "17777777777" , "5478773671" , "2147483647" , "a02220281" , |
| 228 | "4bb2308a7" , "282ba4aaa" , "1652ca931" , "c87e66b7" , "7fffffff" , "53g7f548" , "3928g3h1" , "27c57h32" , |
| 229 | "1db1f927" , "140h2d91" , "ikf5bf1" , "ebelf95" , "b5gge57" , "8jmdnkm" , "6oj8ion" , "5ehncka" , "4clm98f" , |
| 230 | "3hk7987" , "2sb6cs7" , "2d09uc1" , "1vvvvvv" , "1lsqtl1" , "1d8xqrp" , "15v22um" , "zik0zj" }}}, |
| 231 | {3522553278ULL, |
| 232 | {._M_elems: {"skip0" , "skip1" , "11010001111101011110010110111110" , "100002111022020200020" , "3101331132112332" , |
| 233 | "24203233201103" , "1341312313010" , "153202131426" , "32175362676" , "10074266606" , "3522553278" , "1548431462" , |
| 234 | "823842766" , "441a34c6a" , "255b8d486" , "1593b4753" , "d1f5e5be" , "89ffb3b6" , "5da3e606" , "3hgbfb5i" , |
| 235 | "2f0fj33i" , "1k1ac536" , "191b46e2" , "10i6fmk8" , "ia967l6" , "eahia63" , "baca9ga" , "92d86i6" , "78iq4i6" , |
| 236 | "5qlc1dc" , "4osos2i" , "3u1862s" , "38vbpdu" , "2o0a7ro" , "29hx9e6" , "1w2dnod" , "1m98ji6" }}}, |
| 237 | {UINT32_MAX, |
| 238 | {._M_elems: {"skip0" , "skip1" , "11111111111111111111111111111111" , "102002022201221111210" , "3333333333333333" , |
| 239 | "32244002423140" , "1550104015503" , "211301422353" , "37777777777" , "12068657453" , "4294967295" , "1904440553" , |
| 240 | "9ba461593" , "535a79888" , "2ca5b7463" , "1a20dcd80" , "ffffffff" , "a7ffda90" , "704he7g3" , "4f5aff65" , |
| 241 | "3723ai4f" , "281d55i3" , "1fj8b183" , "1606k7ib" , "mb994af" , "hek2mgk" , "dnchbnl" , "b28jpdl" , "8pfgih3" , |
| 242 | "76beigf" , "5qmcpqf" , "4q0jto3" , "3vvvvvv" , "3aokq93" , "2qhxjlh" , "2br45qa" , "1z141z3" }}}, |
| 243 | {545890816626160ULL, |
| 244 | {._M_elems: {"skip0" , "skip1" , "1111100000111110000011100001101100000110111110000" , "2122120211122121121021010202111" , |
| 245 | "1330013300130031200313300" , "1033022333343024014120" , "5213002440142255104" , "222661211220253465" , |
| 246 | "17407603415406760" , "2576748547233674" , "545890816626160" , "148a34aa4706535" , "51285369b87494" , |
| 247 | "1a57a38b045a95" , "98b3383b9766c" , "4319d1601875a" , "1f07c1c360df0" , "ffd471f34f13" , "88g09ff9dh84" , |
| 248 | "4d0d5e232c53" , "2d63h403i580" , "1bf5h8185hdj" , "kc3g550fkcg" , "d41id5k9984" , "8ef5n0him4g" , "5i2dijfe1la" , |
| 249 | "3me22fm5fhi" , "2hfmhgg73kd" , "1ngpfabr53c" , "18i7220bh11" , "rm0lcjngpa" , "kk1elesni1" , "fgfge3c3fg" , |
| 250 | "bp4q5l6bjg" , "8xna46jp0k" , "6wejomvji5" , "5di2s1qhv4" }}}, |
| 251 | {std::uint64_t{INT64_MAX}, |
| 252 | {._M_elems: {"skip0" , "skip1" , "111111111111111111111111111111111111111111111111111111111111111" , |
| 253 | "2021110011022210012102010021220101220221" , "13333333333333333333333333333333" , |
| 254 | "1104332401304422434310311212" , "1540241003031030222122211" , "22341010611245052052300" , |
| 255 | "777777777777777777777" , "67404283172107811827" , "9223372036854775807" , "1728002635214590697" , |
| 256 | "41a792678515120367" , "10b269549075433c37" , "4340724c6c71dc7a7" , "160e2ad3246366807" , "7fffffffffffffff" , |
| 257 | "33d3d8307b214008" , "16agh595df825fa7" , "ba643dci0ffeehh" , "5cbfjia3fh26ja7" , "2heiciiie82dh97" , |
| 258 | "1adaibb21dckfa7" , "i6k448cf4192c2" , "acd772jnc9l0l7" , "64ie1focnn5g77" , "3igoecjbmca687" , "27c48l5b37oaop" , |
| 259 | "1bk39f3ah3dmq7" , "q1se8f0m04isb" , "hajppbc1fc207" , "bm03i95hia437" , "7vvvvvvvvvvvv" , "5hg4ck9jd4u37" , |
| 260 | "3tdtk1v8j6tpp" , "2pijmikexrxp7" , "1y2p0ij32e8e7" }}}, |
| 261 | {stress_chunks_positive, |
| 262 | {._M_elems: {"skip0" , "skip1" , "1010011010001000100100001011110000101100010101001001010111110100" , |
| 263 | "2221221122020020011022001202200200202200" , "22122020210023300230111021113310" , |
| 264 | "1301130403021123030133211100" , "2311004450342244200504500" , "30325064311430214266301" , |
| 265 | "1232104413605425112764" , "87848206138052620680" , "12000000345000678900" , "2181782a1686924456a" , |
| 266 | "54aa47a9058877b130" , "150593a5b002c87b16" , "571cad2b93c7760a8" , "1c60d2676d4e53e00" , "a68890bc2c5495f4" , |
| 267 | "43499224707a4f4g" , "1e052gdga1d26f40" , "f06dh4g564c8a91" , "769df0d9ace4h50" , "3ee7bcj1ajghi4f" , |
| 268 | "1k9agc4gfl0l43a" , "10id7dakdlcjd22" , "dge08fe0l5hl7c" , "8184326d31ib60" , "4ljbglf3cpim76" , |
| 269 | "2pph66481kiiki" , "1niph2ao132e58" , "14qgbgk3c3iffg" , "mhc35an1bhb00" , "f78o8ur705ln5" , "ad24gngm595fk" , |
| 270 | "76e1n5i5v0ivl" , "50wu8jsnks82g" , "3ja41smfvqb1f" , "2j64t3qgq0ut0" }}}, |
| 271 | {14454900944617508688ULL, |
| 272 | {._M_elems: {"skip0" , "skip1" , "1100100010011010000111111101001011100011011000101000111101010000" , |
| 273 | "10120022020112011211121221212101012220210" , "30202122013331023203120220331100" , |
| 274 | "1432224030234034034040234223" , "3014532424232535441404120" , "34610451042001242144165" , |
| 275 | "1442320775134330507520" , "116266464747855335823" , "14454900944617508688" , "266642a9a9471339935" , |
| 276 | "662251403263939640" , "1895280092bc310481" , "68cb9c8292557406c" , "23023deab20002893" , "c89a1fd2e3628f50" , |
| 277 | "50e7147a7db8ef84" , "22a34a05086f78ec" , "i1dgef04357g7i1" , "8g90b882jcj8be8" , "49c1kk35i0k24ic" , |
| 278 | "272a16i54ebkacg" , "15fdih7l3m7k8md" , "gbj7303eg9nge0" , "9hckfdkj3kkdmd" , "5lc7hifdkl4nne" , |
| 279 | "3f86e4mgpna5ol" , "266pj428na273c" , "1bomgjbnlg4m3f" , "r5tf1f7f009ji" , "iarsig29iqhhm" , "ch6gvqbhm53qg" , |
| 280 | "8lwtvcdj6rlqr" , "61w23lajggp44" , "49p1f3dsqqcdx" , "31tkqqkxypopc" }}}, |
| 281 | {UINT64_MAX, |
| 282 | {._M_elems: {"skip0" , "skip1" , "1111111111111111111111111111111111111111111111111111111111111111" , |
| 283 | "11112220022122120101211020120210210211220" , "33333333333333333333333333333333" , |
| 284 | "2214220303114400424121122430" , "3520522010102100444244423" , "45012021522523134134601" , |
| 285 | "1777777777777777777777" , "145808576354216723756" , "18446744073709551615" , "335500516a429071284" , |
| 286 | "839365134a2a240713" , "219505a9511a867b72" , "8681049adb03db171" , "2c1d56b648c6cd110" , "ffffffffffffffff" , |
| 287 | "67979g60f5428010" , "2d3fgb0b9cg4bd2f" , "141c8786h1ccaagg" , "b53bjh07be4dj0f" , "5e8g4ggg7g56dif" , |
| 288 | "2l4lf104353j8kf" , "1ddh88h2782i515" , "l12ee5fn0ji1if" , "c9c336o0mlb7ef" , "7b7n2pcniokcgf" , |
| 289 | "4eo8hfam6fllmo" , "2nc6j26l66rhof" , "1n3rsh11f098rn" , "14l9lkmo30o40f" , "nd075ib45k86f" , "fvvvvvvvvvvvv" , |
| 290 | "b1w8p7j5q9r6f" , "7orp63sh4dphh" , "5g24a25twkwff" , "3w5e11264sgsf" }}}, |
| 291 | }; |
| 292 | |
| 293 | // https://www.wolframalpha.com : Table[BaseForm[k, n], {k, {MEOW, MEOW, MEOW}}, {n, 2, 36}] |
| 294 | constexpr std::int64_t stress_chunks_negative = -9000876000000054321LL; |
| 295 | constexpr pair<std::int64_t, array<const char*, 37>> output_negative[] = { |
| 296 | {-85, {._M_elems: {"skip0" , "skip1" , "-1010101" , "-10011" , "-1111" , "-320" , "-221" , "-151" , "-125" , "-104" , "-85" , "-78" , |
| 297 | "-71" , "-67" , "-61" , "-5a" , "-55" , "-50" , "-4d" , "-49" , "-45" , "-41" , "-3j" , "-3g" , "-3d" , "-3a" , "-37" , |
| 298 | "-34" , "-31" , "-2r" , "-2p" , "-2n" , "-2l" , "-2j" , "-2h" , "-2f" , "-2d" }}}, |
| 299 | {INT8_MIN, {._M_elems: {"skip0" , "skip1" , "-10000000" , "-11202" , "-2000" , "-1003" , "-332" , "-242" , "-200" , "-152" , "-128" , |
| 300 | "-107" , "-a8" , "-9b" , "-92" , "-88" , "-80" , "-79" , "-72" , "-6e" , "-68" , "-62" , "-5i" , "-5d" , "-58" , |
| 301 | "-53" , "-4o" , "-4k" , "-4g" , "-4c" , "-48" , "-44" , "-40" , "-3t" , "-3q" , "-3n" , "-3k" }}}, |
| 302 | {-1591, {._M_elems: {"skip0" , "skip1" , "-11000110111" , "-2011221" , "-120313" , "-22331" , "-11211" , "-4432" , "-3067" , "-2157" , |
| 303 | "-1591" , "-1217" , "-b07" , "-955" , "-819" , "-711" , "-637" , "-58a" , "-4g7" , "-47e" , "-3jb" , "-3cg" , |
| 304 | "-367" , "-304" , "-2i7" , "-2dg" , "-295" , "-24p" , "-20n" , "-1pp" , "-1n1" , "-1ka" , "-1hn" , "-1f7" , "-1cr" , |
| 305 | "-1ag" , "-187" }}}, |
| 306 | {INT16_MIN, {._M_elems: {"skip0" , "skip1" , "-1000000000000000" , "-1122221122" , "-20000000" , "-2022033" , "-411412" , "-164351" , |
| 307 | "-100000" , "-48848" , "-32768" , "-2268a" , "-16b68" , "-11bb8" , "-bd28" , "-9a98" , "-8000" , "-6b69" , |
| 308 | "-5b28" , "-4eec" , "-41i8" , "-3b68" , "-31fa" , "-2flg" , "-28l8" , "-22ai" , "-1mc8" , "-1hph" , "-1dm8" , |
| 309 | "-19rr" , "-16c8" , "-1331" , "-1000" , "-u2w" , "-sbq" , "-qq8" , "-pa8" }}}, |
| 310 | {-66748412, |
| 311 | {._M_elems: {"skip0" , "skip1" , "-11111110100111111111111100" , "-11122121011121102" , "-3332213333330" , "-114041422122" , |
| 312 | "-10342352232" , "-1440231533" , "-376477774" , "-148534542" , "-66748412" , "-34750085" , "-1a42b678" , |
| 313 | "-10aa0803" , "-8c1731a" , "-5cd7492" , "-3fa7ffc" , "-2d03163" , "-1h5f3b2" , "-17i39c6" , "-10h3b0c" , "-g749jh" , |
| 314 | "-ckkdkg" , "-a8c0ak" , "-894afk" , "-6klmbc" , "-5g1i6g" , "-4hg4gb" , "-3ogi7o" , "-37anqb" , "-2mc4r2" , |
| 315 | "-2a8h7i" , "-1vkvvs" , "-1n9ca5" , "-1fw8sk" , "-19gshh" , "-13qnek" }}}, |
| 316 | {INT32_MIN, {._M_elems: {"skip0" , "skip1" , "-10000000000000000000000000000000" , "-12112122212110202102" , "-2000000000000000" , |
| 317 | "-13344223434043" , "-553032005532" , "-104134211162" , "-20000000000" , "-5478773672" , "-2147483648" , |
| 318 | "-a02220282" , "-4bb2308a8" , "-282ba4aab" , "-1652ca932" , "-c87e66b8" , "-80000000" , "-53g7f549" , |
| 319 | "-3928g3h2" , "-27c57h33" , "-1db1f928" , "-140h2d92" , "-ikf5bf2" , "-ebelf96" , "-b5gge58" , "-8jmdnkn" , |
| 320 | "-6oj8ioo" , "-5ehnckb" , "-4clm98g" , "-3hk7988" , "-2sb6cs8" , "-2d09uc2" , "-2000000" , "-1lsqtl2" , |
| 321 | "-1d8xqrq" , "-15v22un" , "-zik0zk" }}}, |
| 322 | {-297139747082649553LL, |
| 323 | {._M_elems: {"skip0" , "skip1" , "-10000011111101001110000011010010001100000101011111111010001" , |
| 324 | "-1222110012002112101210012211022102101" , "-100133221300122101200223333101" , "-4443033200104011124241203" , |
| 325 | "-21313431255203203120401" , "-350320603201030412545" , "-20375160322140537721" , "-1873162471705738371" , |
| 326 | "-297139747082649553" , "-65150976074a24025" , "-173522497b5373101" , "-5a60a99bc3b71654" , "-1ca51a06cc38ba25" , |
| 327 | "-a2a25babe62241d" , "-41fa7069182bfd1" , "-1d00134fba1769g" , "-e4f799fc5f7e81" , "-714ebbh8388188" , |
| 328 | "-3cahb17836b3hd" , "-1j8659jf5hbg3j" , "-112bbb2jege5c5" , "-dcjfmk2kjb4cc" , "-836bm4klbgl61" , |
| 329 | "-4ofia1416ee73" , "-32ommgjef1l2h" , "-1qc52eal5m8ba" , "-17n53r05a4r15" , "-oa88m2qiqjik" , "-gn67qoat5r8d" , |
| 330 | "-blgd6n5s90al" , "-87t70q8o5fuh" , "-5t09hwaqu9qg" , "-47vssihaoa4x" , "-32p24fbjye7x" , "-299r8zck3841" }}}, |
| 331 | {stress_chunks_negative, |
| 332 | {._M_elems: {"skip0" , "skip1" , "-111110011101001100010010000100010000111010101111001010000110001" , |
| 333 | "-2012222010200021010000112111002001111200" , "-13303221202100202013111321100301" , |
| 334 | "-1101001100304341000003214241" , "-1522150121302454031001413" , "-22054250360123016161454" , |
| 335 | "-763514220420725712061" , "-65863607100474061450" , "-9000876000000054321" , "-1689813530958833498" , |
| 336 | "-408258185a67069269" , "-106b01597a47ba2948" , "-41c02922bc776d49b" , "-1584cd10979dc84b6" , |
| 337 | "-7ce9890887579431" , "-327cf6cbc67023c3" , "-1604b5f6a0de8129" , "-b50d3ef02f124a4" , "-59h9bfif0006fg1" , |
| 338 | "-2g5d8ekh05d2dfi" , "-19i418c38g1chfj" , "-hjgf7d0k0gla9a" , "-a6b21ncehfa3f9" , "-61060fnl003bml" , |
| 339 | "-3g88bakondgf8l" , "-25q3i730ed21di" , "-1al84glo518iip" , "-pcli8ig7pjhbo" , "-gs31q8id2jnkl" , |
| 340 | "-bd7kaglgdrbgk" , "-7pqc9123lf51h" , "-5d2sd1r5ms7su" , "-3q833s8kdrun3" , "-2n7vmqigfueqb" , |
| 341 | "-1wdu892toj0a9" }}}, |
| 342 | {INT64_MIN, {._M_elems: {"skip0" , "skip1" , "-1000000000000000000000000000000000000000000000000000000000000000" , |
| 343 | "-2021110011022210012102010021220101220222" , "-20000000000000000000000000000000" , |
| 344 | "-1104332401304422434310311213" , "-1540241003031030222122212" , "-22341010611245052052301" , |
| 345 | "-1000000000000000000000" , "-67404283172107811828" , "-9223372036854775808" , "-1728002635214590698" , |
| 346 | "-41a792678515120368" , "-10b269549075433c38" , "-4340724c6c71dc7a8" , "-160e2ad3246366808" , |
| 347 | "-8000000000000000" , "-33d3d8307b214009" , "-16agh595df825fa8" , "-ba643dci0ffeehi" , |
| 348 | "-5cbfjia3fh26ja8" , "-2heiciiie82dh98" , "-1adaibb21dckfa8" , "-i6k448cf4192c3" , "-acd772jnc9l0l8" , |
| 349 | "-64ie1focnn5g78" , "-3igoecjbmca688" , "-27c48l5b37oaoq" , "-1bk39f3ah3dmq8" , "-q1se8f0m04isc" , |
| 350 | "-hajppbc1fc208" , "-bm03i95hia438" , "-8000000000000" , "-5hg4ck9jd4u38" , "-3tdtk1v8j6tpq" , |
| 351 | "-2pijmikexrxp8" , "-1y2p0ij32e8e8" }}}, |
| 352 | }; |
| 353 | |
| 354 | template <typename T> |
| 355 | void test_integer_to_chars() { |
| 356 | for (int base = 2; base <= 36; ++base) { |
| 357 | test_integer_to_chars(static_cast<T>(0), base, "0" ); |
| 358 | test_integer_to_chars(static_cast<T>(1), base, "1" ); |
| 359 | |
| 360 | // tests [3, 71] |
| 361 | test_integer_to_chars(static_cast<T>(base * 2 - 1), base, output_max_digit[base]); |
| 362 | |
| 363 | for (const auto& p : output_positive) { |
| 364 | if (p.first <= static_cast<std::uint64_t>(numeric_limits<T>::max())) { |
| 365 | test_integer_to_chars(static_cast<T>(p.first), base, p.second[static_cast<std::size_t>(base)]); |
| 366 | } |
| 367 | } |
| 368 | |
| 369 | if constexpr (is_signed_v<T>) { |
| 370 | test_integer_to_chars(static_cast<T>(-1), base, "-1" ); |
| 371 | |
| 372 | for (const auto& p : output_negative) { |
| 373 | if (p.first >= static_cast<std::int64_t>(numeric_limits<T>::min())) { |
| 374 | test_integer_to_chars(static_cast<T>(p.first), base, p.second[static_cast<std::size_t>(base)]); |
| 375 | } |
| 376 | } |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | test_integer_to_chars(static_cast<T>(42), nullopt, "42" ); |
| 381 | } |
| 382 | |
| 383 | enum class TestFromCharsMode { Normal, SignalingNaN }; |
| 384 | |
| 385 | template <typename T, typename BaseOrFmt> |
| 386 | void (const string_view input, const BaseOrFmt base_or_fmt, const std::size_t correct_idx, |
| 387 | const errc correct_ec, const optional<T> opt_correct = nullopt, |
| 388 | const TestFromCharsMode mode = TestFromCharsMode::Normal) { |
| 389 | |
| 390 | if constexpr (is_integral_v<T>) { |
| 391 | assert(mode == TestFromCharsMode::Normal); |
| 392 | } |
| 393 | |
| 394 | constexpr T unmodified = 111; |
| 395 | |
| 396 | T dest = unmodified; |
| 397 | |
| 398 | const from_chars_result result = from_chars(input.data(), input.data() + input.size(), dest, base_or_fmt); |
| 399 | |
| 400 | assert(result.ptr == input.data() + correct_idx); |
| 401 | assert(result.ec == correct_ec); |
| 402 | |
| 403 | if (correct_ec == errc{} || (is_floating_point_v<T> && correct_ec == errc::result_out_of_range)) { |
| 404 | if constexpr (is_floating_point_v<T>) { |
| 405 | if (mode == TestFromCharsMode::Normal) { |
| 406 | using Uint = conditional_t<is_same_v<T, float>, std::uint32_t, std::uint64_t>; |
| 407 | assert(opt_correct.has_value()); |
| 408 | assert(_Bit_cast<Uint>(dest) == _Bit_cast<Uint>(opt_correct.value())); |
| 409 | } else { |
| 410 | assert(mode == TestFromCharsMode::SignalingNaN); |
| 411 | assert(!opt_correct.has_value()); |
| 412 | assert(isnan(dest)); |
| 413 | } |
| 414 | } else { |
| 415 | assert(opt_correct.has_value()); |
| 416 | assert(dest == opt_correct.value()); |
| 417 | } |
| 418 | } else { |
| 419 | assert(!opt_correct.has_value()); |
| 420 | assert(dest == unmodified); |
| 421 | } |
| 422 | } |
| 423 | |
| 424 | constexpr errc inv_arg = errc::invalid_argument; |
| 425 | constexpr errc out_ran = errc::result_out_of_range; |
| 426 | |
| 427 | template <typename T> |
| 428 | void test_integer_from_chars() { |
| 429 | for (int base = 2; base <= 36; ++base) { |
| 430 | test_from_chars<T>("" , base, 0, inv_arg); // no characters |
| 431 | test_from_chars<T>("@1" , base, 0, inv_arg); // '@' is bogus |
| 432 | test_from_chars<T>(".1" , base, 0, inv_arg); // '.' is bogus, for integers |
| 433 | test_from_chars<T>("+1" , base, 0, inv_arg); // '+' is bogus, N4713 23.20.3 [charconv.from.chars]/3 |
| 434 | // "a minus sign is the only sign that may appear" |
| 435 | test_from_chars<T>(" 1" , base, 0, inv_arg); // ' ' is bogus, no whitespace in subject sequence |
| 436 | |
| 437 | if constexpr (is_unsigned_v<T>) { // N4713 23.20.3 [charconv.from.chars]/3 |
| 438 | test_from_chars<T>("-1" , base, 0, inv_arg); // "and only if value has a signed type" |
| 439 | } |
| 440 | |
| 441 | // N4713 23.20.3 [charconv.from.chars]/1 "[ Note: If the pattern allows for an optional sign, |
| 442 | // but the string has no digit characters following the sign, no characters match the pattern. -end note ]" |
| 443 | test_from_chars<T>("-" , base, 0, inv_arg); // '-' followed by no characters |
| 444 | test_from_chars<T>("-@1" , base, 0, inv_arg); // '-' followed by bogus '@' |
| 445 | test_from_chars<T>("-.1" , base, 0, inv_arg); // '-' followed by bogus '.' |
| 446 | test_from_chars<T>("-+1" , base, 0, inv_arg); // '-' followed by bogus '+' |
| 447 | test_from_chars<T>("- 1" , base, 0, inv_arg); // '-' followed by bogus ' ' |
| 448 | test_from_chars<T>("--1" , base, 0, inv_arg); // '-' can't be repeated |
| 449 | |
| 450 | vector<char> bogus_digits; |
| 451 | |
| 452 | if (base < 10) { |
| 453 | bogus_digits = {static_cast<char>('0' + base), 'A', 'a'}; |
| 454 | } else { |
| 455 | // '[' and '{' are bogus for base 36 |
| 456 | bogus_digits = {static_cast<char>('A' + (base - 10)), static_cast<char>('a' + (base - 10))}; |
| 457 | } |
| 458 | |
| 459 | for (const auto& bogus : bogus_digits) { |
| 460 | test_from_chars<T>(bogus + "1"s , base, 0, inv_arg); // bogus digit (for this base) |
| 461 | test_from_chars<T>("-"s + bogus + "1"s , base, 0, inv_arg); // '-' followed by bogus digit |
| 462 | } |
| 463 | |
| 464 | // Test leading zeroes. |
| 465 | test_from_chars<T>(string(100, '0'), base, 100, errc{}, static_cast<T>(0)); |
| 466 | test_from_chars<T>(string(100, '0') + "11"s , base, 102, errc{}, static_cast<T>(base + 1)); |
| 467 | |
| 468 | // Test negative zero and negative leading zeroes. |
| 469 | if constexpr (is_signed_v<T>) { |
| 470 | test_from_chars<T>("-0" , base, 2, errc{}, static_cast<T>(0)); |
| 471 | test_from_chars<T>("-"s + string(100, '0'), base, 101, errc{}, static_cast<T>(0)); |
| 472 | test_from_chars<T>("-"s + string(100, '0') + "11"s , base, 103, errc{}, static_cast<T>(-base - 1)); |
| 473 | } |
| 474 | |
| 475 | // N4713 23.20.3 [charconv.from.chars]/1 "The member ptr of the return value points to the |
| 476 | // first character not matching the pattern, or has the value last if all characters match." |
| 477 | test_from_chars<T>("11" , base, 2, errc{}, static_cast<T>(base + 1)); |
| 478 | test_from_chars<T>("11@@@" , base, 2, errc{}, static_cast<T>(base + 1)); |
| 479 | |
| 480 | // When overflowing, we need to keep consuming valid digits, in order to return ptr correctly. |
| 481 | test_from_chars<T>(string(100, '1'), base, 100, out_ran); |
| 482 | test_from_chars<T>(string(100, '1') + "@@@"s , base, 100, out_ran); |
| 483 | |
| 484 | if constexpr (is_signed_v<T>) { |
| 485 | test_from_chars<T>("-"s + string(100, '1'), base, 101, out_ran); |
| 486 | test_from_chars<T>("-"s + string(100, '1') + "@@@"s , base, 101, out_ran); |
| 487 | } |
| 488 | } |
| 489 | |
| 490 | // N4713 23.20.3 [charconv.from.chars]/3 "The pattern is the expected form of the subject sequence |
| 491 | // in the "C" locale for the given nonzero base, as described for strtol" |
| 492 | // C11 7.22.1.4/3 "The letters from a (or A) through z (or Z) are ascribed the values 10 through 35" |
| 493 | for (int i = 0; i < 26; ++i) { |
| 494 | test_from_chars<T>(string(1, static_cast<char>('A' + i)), 36, 1, errc{}, static_cast<T>(10 + i)); |
| 495 | test_from_chars<T>(string(1, static_cast<char>('a' + i)), 36, 1, errc{}, static_cast<T>(10 + i)); |
| 496 | } |
| 497 | |
| 498 | // N4713 23.20.3 [charconv.from.chars]/3 "no "0x" or "0X" prefix shall appear if the value of base is 16" |
| 499 | test_from_chars<T>("0x1729" , 16, 1, errc{}, static_cast<T>(0)); // reads '0', stops at 'x' |
| 500 | test_from_chars<T>("0X1729" , 16, 1, errc{}, static_cast<T>(0)); // reads '0', stops at 'X' |
| 501 | |
| 502 | if constexpr (is_signed_v<T>) { |
| 503 | test_from_chars<T>("-0x1729" , 16, 2, errc{}, static_cast<T>(0)); // reads "-0", stops at 'x' |
| 504 | test_from_chars<T>("-0X1729" , 16, 2, errc{}, static_cast<T>(0)); // reads "-0", stops at 'X' |
| 505 | } |
| 506 | } |
| 507 | |
| 508 | template <typename T> |
| 509 | void test_integer() { |
| 510 | test_integer_to_chars<T>(); |
| 511 | test_integer_from_chars<T>(); |
| 512 | } |
| 513 | |
| 514 | void all_integer_tests() { |
| 515 | test_integer<char>(); |
| 516 | test_integer<signed char>(); |
| 517 | test_integer<unsigned char>(); |
| 518 | test_integer<short>(); |
| 519 | test_integer<unsigned short>(); |
| 520 | test_integer<int>(); |
| 521 | test_integer<unsigned int>(); |
| 522 | test_integer<long>(); |
| 523 | test_integer<unsigned long>(); |
| 524 | test_integer<long long>(); |
| 525 | test_integer<unsigned long long>(); |
| 526 | |
| 527 | // Test overflow scenarios. |
| 528 | test_from_chars<unsigned int>(input: "4294967289" , base_or_fmt: 10, correct_idx: 10, correct_ec: errc{}, opt_correct: 4294967289U); // not risky |
| 529 | test_from_chars<unsigned int>(input: "4294967294" , base_or_fmt: 10, correct_idx: 10, correct_ec: errc{}, opt_correct: 4294967294U); // risky with good digit |
| 530 | test_from_chars<unsigned int>(input: "4294967295" , base_or_fmt: 10, correct_idx: 10, correct_ec: errc{}, opt_correct: 4294967295U); // risky with max digit |
| 531 | test_from_chars<unsigned int>(input: "4294967296" , base_or_fmt: 10, correct_idx: 10, correct_ec: out_ran); // risky with bad digit |
| 532 | test_from_chars<unsigned int>(input: "4294967300" , base_or_fmt: 10, correct_idx: 10, correct_ec: out_ran); // beyond risky |
| 533 | |
| 534 | test_from_chars<int>(input: "2147483639" , base_or_fmt: 10, correct_idx: 10, correct_ec: errc{}, opt_correct: 2147483639); // not risky |
| 535 | test_from_chars<int>(input: "2147483646" , base_or_fmt: 10, correct_idx: 10, correct_ec: errc{}, opt_correct: 2147483646); // risky with good digit |
| 536 | test_from_chars<int>(input: "2147483647" , base_or_fmt: 10, correct_idx: 10, correct_ec: errc{}, opt_correct: 2147483647); // risky with max digit |
| 537 | test_from_chars<int>(input: "2147483648" , base_or_fmt: 10, correct_idx: 10, correct_ec: out_ran); // risky with bad digit |
| 538 | test_from_chars<int>(input: "2147483650" , base_or_fmt: 10, correct_idx: 10, correct_ec: out_ran); // beyond risky |
| 539 | |
| 540 | test_from_chars<int>(input: "-2147483639" , base_or_fmt: 10, correct_idx: 11, correct_ec: errc{}, opt_correct: -2147483639); // not risky |
| 541 | test_from_chars<int>(input: "-2147483647" , base_or_fmt: 10, correct_idx: 11, correct_ec: errc{}, opt_correct: -2147483647); // risky with good digit |
| 542 | test_from_chars<int>(input: "-2147483648" , base_or_fmt: 10, correct_idx: 11, correct_ec: errc{}, opt_correct: -2147483647 - 1); // risky with max digit |
| 543 | test_from_chars<int>(input: "-2147483649" , base_or_fmt: 10, correct_idx: 11, correct_ec: out_ran); // risky with bad digit |
| 544 | test_from_chars<int>(input: "-2147483650" , base_or_fmt: 10, correct_idx: 11, correct_ec: out_ran); // beyond risky |
| 545 | } |
| 546 | |
| 547 | void assert_message_bits(const bool b, const char* const msg, const std::uint32_t bits) { |
| 548 | if (!b) { |
| 549 | fprintf(stderr, format: "%s failed for 0x%08zX\n" , msg, static_cast<std::size_t>(bits)); |
| 550 | fprintf(stderr, format: "This is a randomized test.\n" ); |
| 551 | fprintf(stderr, format: "DO NOT IGNORE/RERUN THIS FAILURE.\n" ); |
| 552 | fprintf(stderr, format: "You must report it to the STL maintainers.\n" ); |
| 553 | abort(); |
| 554 | } |
| 555 | } |
| 556 | |
| 557 | void assert_message_bits(const bool b, const char* const msg, const std::uint64_t bits) { |
| 558 | if (!b) { |
| 559 | // libc++ uses long for 64-bit values. |
| 560 | fprintf(stderr, format: "%s failed for 0x%016llX\n" , msg, static_cast<unsigned long long>(bits)); |
| 561 | fprintf(stderr, format: "This is a randomized test.\n" ); |
| 562 | fprintf(stderr, format: "DO NOT IGNORE/RERUN THIS FAILURE.\n" ); |
| 563 | fprintf(stderr, format: "You must report it to the STL maintainers.\n" ); |
| 564 | abort(); |
| 565 | } |
| 566 | } |
| 567 | |
| 568 | constexpr std::uint32_t FractionBits = 10; // Tunable for test coverage vs. performance. |
| 569 | static_assert(FractionBits >= 1, "Must test at least 1 fraction bit." ); |
| 570 | static_assert(FractionBits <= 23, "There are only 23 fraction bits in a float." ); |
| 571 | |
| 572 | constexpr std::uint32_t Fractions = 1U << FractionBits; |
| 573 | constexpr std::uint32_t Mask32 = ~((1U << FractionBits) - 1U); |
| 574 | constexpr std::uint64_t Mask64 = ~((1ULL << FractionBits) - 1ULL); |
| 575 | |
| 576 | constexpr std::uint32_t PrefixesToTest = 100; // Tunable for test coverage vs. performance. |
| 577 | static_assert(PrefixesToTest >= 1, "Must test at least 1 prefix." ); |
| 578 | |
| 579 | constexpr std::uint32_t PrefixLimit = 2 // sign bit |
| 580 | * 255 // non-INF/NAN exponents for float |
| 581 | * (1U << (23 - FractionBits)); // fraction bits in prefix |
| 582 | static_assert(PrefixesToTest <= PrefixLimit, "Too many prefixes." ); |
| 583 | |
| 584 | template <bool IsDouble> |
| 585 | void test_floating_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint32_t> prefix) { |
| 586 | |
| 587 | using UIntType = conditional_t<IsDouble, std::uint64_t, std::uint32_t>; |
| 588 | using FloatingType = conditional_t<IsDouble, double, float>; |
| 589 | |
| 590 | // "-1.2345678901234567e-100" or "-1.23456789e-10" |
| 591 | constexpr std::size_t buffer_size = IsDouble ? 24 : 15; |
| 592 | char buffer[buffer_size]; |
| 593 | |
| 594 | #ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 595 | FloatingType val; |
| 596 | #endif |
| 597 | |
| 598 | // Exact sizes are difficult to prove for fixed notation. |
| 599 | // This must be at least (IsDouble ? 327 : 48), and I suspect that that's exact. |
| 600 | // Here's a loose upper bound: |
| 601 | // 1 character for a negative sign |
| 602 | // + 325 (for double; 46 for float) characters in the "0.000~~~000" prefix of the min subnormal |
| 603 | // + 17 (for double; 9 for float) characters for round-trip digits |
| 604 | constexpr std::size_t fixed_buffer_size = IsDouble ? 1 + 325 + 17 : 1 + 46 + 9; |
| 605 | char fixed_buffer[fixed_buffer_size]; |
| 606 | |
| 607 | // worst case: negative sign + max normal + null terminator |
| 608 | constexpr std::size_t stdio_buffer_size = 1 + (IsDouble ? 309 : 39) + 1; |
| 609 | char stdio_buffer[stdio_buffer_size]; |
| 610 | |
| 611 | for (std::uint32_t frac = 0; frac < Fractions; ++frac) { |
| 612 | const UIntType bits = prefix + frac; |
| 613 | const FloatingType input = _Bit_cast<FloatingType>(bits); |
| 614 | |
| 615 | { |
| 616 | const auto to_result = to_chars(buffer, end(buffer), input, chars_format::scientific); |
| 617 | assert_message_bits(to_result.ec == errc{}, "to_result.ec" , bits); |
| 618 | #ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 619 | const char* const last = to_result.ptr; |
| 620 | |
| 621 | const auto from_result = from_chars(buffer, last, val); |
| 622 | |
| 623 | assert_message_bits(from_result.ptr == last, "from_result.ptr" , bits); |
| 624 | assert_message_bits(from_result.ec == errc{}, "from_result.ec" , bits); |
| 625 | assert_message_bits(_Bit_cast<UIntType>(val) == bits, "round-trip" , bits); |
| 626 | #endif // TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 627 | } |
| 628 | |
| 629 | { |
| 630 | // Also verify that to_chars() and sprintf_s() emit the same output for integers in fixed notation. |
| 631 | const auto fixed_result = to_chars(fixed_buffer, end(fixed_buffer), input, chars_format::fixed); |
| 632 | assert_message_bits(fixed_result.ec == errc{}, "fixed_result.ec" , bits); |
| 633 | const string_view fixed_sv(fixed_buffer, static_cast<std::size_t>(fixed_result.ptr - fixed_buffer)); |
| 634 | |
| 635 | if (find(first: fixed_sv.begin(), last: fixed_sv.end(), val: '.') == fixed_sv.end()) { |
| 636 | const int stdio_ret = sprintf_s(stdio_buffer, size(stdio_buffer), "%.0f" , input); |
| 637 | assert_message_bits(stdio_ret != -1, "stdio_ret" , bits); |
| 638 | const string_view stdio_sv(stdio_buffer); |
| 639 | assert_message_bits(fixed_sv == stdio_sv, "fixed_sv" , bits); |
| 640 | } |
| 641 | } |
| 642 | } |
| 643 | } |
| 644 | |
| 645 | template <bool IsDouble> |
| 646 | void test_floating_hex_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint32_t> prefix) { |
| 647 | |
| 648 | using UIntType = conditional_t<IsDouble, std::uint64_t, std::uint32_t>; |
| 649 | using FloatingType = conditional_t<IsDouble, double, float>; |
| 650 | |
| 651 | // The precision is the number of hexits after the decimal point. |
| 652 | // These hexits correspond to the explicitly stored fraction bits. |
| 653 | // double explicitly stores 52 fraction bits. 52 / 4 == 13, so we need 13 hexits. |
| 654 | // float explicitly stores 23 fraction bits. 23 / 4 == 5.75, so we need 6 hexits. |
| 655 | |
| 656 | // "-1.fffffffffffffp+1023" or "-1.fffffep+127" |
| 657 | constexpr std::size_t buffer_size = IsDouble ? 22 : 14; |
| 658 | char buffer[buffer_size]; |
| 659 | |
| 660 | #ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 661 | FloatingType val; |
| 662 | #endif |
| 663 | |
| 664 | for (std::uint32_t frac = 0; frac < Fractions; ++frac) { |
| 665 | const UIntType bits = prefix + frac; |
| 666 | const FloatingType input = _Bit_cast<FloatingType>(bits); |
| 667 | |
| 668 | const auto to_result = to_chars(buffer, end(buffer), input, chars_format::hex); |
| 669 | assert_message_bits(to_result.ec == errc{}, "(hex) to_result.ec" , bits); |
| 670 | |
| 671 | #ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 672 | const char* const last = to_result.ptr; |
| 673 | |
| 674 | const auto from_result = from_chars(buffer, last, val, chars_format::hex); |
| 675 | |
| 676 | assert_message_bits(from_result.ptr == last, "(hex) from_result.ptr" , bits); |
| 677 | assert_message_bits(from_result.ec == errc{}, "(hex) from_result.ec" , bits); |
| 678 | assert_message_bits(_Bit_cast<UIntType>(val) == bits, "(hex) round-trip" , bits); |
| 679 | #endif // TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 680 | } |
| 681 | } |
| 682 | |
| 683 | template <bool IsDouble> |
| 684 | void test_floating_precision_prefix(const conditional_t<IsDouble, std::uint64_t, std::uint32_t> prefix) { |
| 685 | |
| 686 | using UIntType = conditional_t<IsDouble, std::uint64_t, std::uint32_t>; |
| 687 | using FloatingType = conditional_t<IsDouble, double, float>; |
| 688 | |
| 689 | // Precision for min subnormal in fixed notation. (More than enough for scientific notation.) |
| 690 | constexpr int precision = IsDouble ? 1074 : 149; |
| 691 | |
| 692 | // Number of digits for max normal in fixed notation. |
| 693 | constexpr int max_integer_length = IsDouble ? 309 : 39; |
| 694 | |
| 695 | // Size for fixed notation. (More than enough for scientific notation.) |
| 696 | constexpr std::size_t charconv_buffer_size = 1 // negative sign |
| 697 | + max_integer_length // integer digits |
| 698 | + 1 // decimal point |
| 699 | + precision; // fractional digits |
| 700 | char charconv_buffer[charconv_buffer_size]; |
| 701 | |
| 702 | constexpr std::size_t stdio_buffer_size = charconv_buffer_size + 1; // null terminator |
| 703 | char stdio_buffer[stdio_buffer_size]; |
| 704 | |
| 705 | // 1 character for a negative sign |
| 706 | // + worst cases: 0x1.fffffffffffffp-1022 and 0x1.fffffep-126f |
| 707 | constexpr std::size_t general_buffer_size = 1 + (IsDouble ? 773 : 117); |
| 708 | char general_buffer[general_buffer_size]; |
| 709 | char general_stdio_buffer[general_buffer_size + 1]; // + null terminator |
| 710 | |
| 711 | for (std::uint32_t frac = 0; frac < Fractions; ++frac) { |
| 712 | const UIntType bits = prefix + frac; |
| 713 | const FloatingType input = _Bit_cast<FloatingType>(bits); |
| 714 | |
| 715 | auto result = to_chars(charconv_buffer, end(charconv_buffer), input, chars_format::fixed, precision); |
| 716 | assert_message_bits(result.ec == errc{}, "to_chars fixed precision" , bits); |
| 717 | string_view charconv_sv(charconv_buffer, static_cast<std::size_t>(result.ptr - charconv_buffer)); |
| 718 | |
| 719 | int stdio_ret = sprintf_s(stdio_buffer, size(stdio_buffer), "%.*f" , precision, input); |
| 720 | assert_message_bits(stdio_ret != -1, "sprintf_s fixed precision" , bits); |
| 721 | string_view stdio_sv(stdio_buffer); |
| 722 | |
| 723 | assert_message_bits(charconv_sv == stdio_sv, "fixed precision output" , bits); |
| 724 | |
| 725 | |
| 726 | result = to_chars(charconv_buffer, end(charconv_buffer), input, chars_format::scientific, precision); |
| 727 | assert_message_bits(result.ec == errc{}, "to_chars scientific precision" , bits); |
| 728 | charconv_sv = string_view(charconv_buffer, static_cast<std::size_t>(result.ptr - charconv_buffer)); |
| 729 | |
| 730 | stdio_ret = sprintf_s(stdio_buffer, size(stdio_buffer), "%.*e" , precision, input); |
| 731 | assert_message_bits(stdio_ret != -1, "sprintf_s scientific precision" , bits); |
| 732 | stdio_sv = stdio_buffer; |
| 733 | |
| 734 | assert_message_bits(charconv_sv == stdio_sv, "scientific precision output" , bits); |
| 735 | |
| 736 | |
| 737 | result = to_chars(general_buffer, end(general_buffer), input, chars_format::general, 5000); |
| 738 | assert_message_bits(result.ec == errc{}, "to_chars general precision" , bits); |
| 739 | charconv_sv = string_view(general_buffer, static_cast<std::size_t>(result.ptr - general_buffer)); |
| 740 | |
| 741 | stdio_ret = sprintf_s(general_stdio_buffer, size(general_stdio_buffer), "%.5000g" , input); |
| 742 | assert_message_bits(stdio_ret != -1, "sprintf_s general precision" , bits); |
| 743 | stdio_sv = general_stdio_buffer; |
| 744 | |
| 745 | assert_message_bits(charconv_sv == stdio_sv, "general precision output" , bits); |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | void test_floating_prefixes(mt19937_64& mt64) { |
| 750 | { |
| 751 | set<std::uint64_t> prefixes64; |
| 752 | |
| 753 | while (prefixes64.size() < PrefixesToTest) { |
| 754 | const std::uint64_t val = mt64(); |
| 755 | |
| 756 | if ((val & 0x7FF0000000000000ULL) != 0x7FF0000000000000ULL) { // skip INF/NAN |
| 757 | prefixes64.insert(x: val & Mask64); |
| 758 | } |
| 759 | } |
| 760 | |
| 761 | for (const auto& prefix : prefixes64) { |
| 762 | test_floating_prefix<true>(prefix); |
| 763 | test_floating_precision_prefix<true>(prefix); |
| 764 | } |
| 765 | |
| 766 | test_floating_hex_prefix<true>(prefix: *prefixes64.begin()); // save time by testing fewer hexfloats |
| 767 | } |
| 768 | |
| 769 | { |
| 770 | set<std::uint32_t> prefixes32; |
| 771 | |
| 772 | while (prefixes32.size() < PrefixesToTest) { |
| 773 | const std::uint32_t val = static_cast<std::uint32_t>(mt64()); |
| 774 | |
| 775 | if ((val & 0x7F800000U) != 0x7F800000U) { // skip INF/NAN |
| 776 | prefixes32.insert(x: val & Mask32); |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | for (const auto& prefix : prefixes32) { |
| 781 | test_floating_prefix<false>(prefix); |
| 782 | test_floating_precision_prefix<false>(prefix); |
| 783 | } |
| 784 | |
| 785 | test_floating_hex_prefix<false>(prefix: *prefixes32.begin()); // save time by testing fewer hexfloats |
| 786 | } |
| 787 | } |
| 788 | |
| 789 | #ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 790 | template <typename T> |
| 791 | void test_floating_from_chars(const chars_format fmt) { |
| 792 | test_from_chars<T>("" , fmt, 0, inv_arg); // no characters |
| 793 | test_from_chars<T>("@1" , fmt, 0, inv_arg); // '@' is bogus |
| 794 | test_from_chars<T>("z1" , fmt, 0, inv_arg); // 'z' is bogus |
| 795 | test_from_chars<T>("." , fmt, 0, inv_arg); // '.' without digits is bogus |
| 796 | test_from_chars<T>("+1" , fmt, 0, inv_arg); // '+' is bogus |
| 797 | test_from_chars<T>(" 1" , fmt, 0, inv_arg); // ' ' is bogus |
| 798 | test_from_chars<T>("p5" , fmt, 0, inv_arg); // binary-exponent-part without digits is bogus |
| 799 | test_from_chars<T>("in" , fmt, 0, inv_arg); // incomplete inf is bogus |
| 800 | test_from_chars<T>("na" , fmt, 0, inv_arg); // incomplete nan is bogus |
| 801 | |
| 802 | test_from_chars<T>("-" , fmt, 0, inv_arg); // '-' followed by no characters |
| 803 | test_from_chars<T>("-@1" , fmt, 0, inv_arg); // '-' followed by bogus '@' |
| 804 | test_from_chars<T>("-z1" , fmt, 0, inv_arg); // '-' followed by bogus 'z' |
| 805 | test_from_chars<T>("-." , fmt, 0, inv_arg); // '-' followed by bogus '.' |
| 806 | test_from_chars<T>("-+1" , fmt, 0, inv_arg); // '-' followed by bogus '+' |
| 807 | test_from_chars<T>("- 1" , fmt, 0, inv_arg); // '-' followed by bogus ' ' |
| 808 | test_from_chars<T>("-p5" , fmt, 0, inv_arg); // '-' followed by bogus binary-exponent-part |
| 809 | test_from_chars<T>("-in" , fmt, 0, inv_arg); // '-' followed by bogus incomplete inf |
| 810 | test_from_chars<T>("-na" , fmt, 0, inv_arg); // '-' followed by bogus incomplete nan |
| 811 | test_from_chars<T>("--1" , fmt, 0, inv_arg); // '-' can't be repeated |
| 812 | |
| 813 | if (fmt != chars_format::hex) { // "e5" are valid hexits |
| 814 | test_from_chars<T>("e5" , fmt, 0, inv_arg); // exponent-part without digits is bogus |
| 815 | test_from_chars<T>("-e5" , fmt, 0, inv_arg); // '-' followed by bogus exponent-part |
| 816 | } |
| 817 | |
| 818 | constexpr T inf = numeric_limits<T>::infinity(); |
| 819 | constexpr T qnan = numeric_limits<T>::quiet_NaN(); |
| 820 | |
| 821 | test_from_chars<T>("InF" , fmt, 3, errc{}, inf); |
| 822 | test_from_chars<T>("infinite" , fmt, 3, errc{}, inf); |
| 823 | test_from_chars<T>("iNfInItY" , fmt, 8, errc{}, inf); |
| 824 | test_from_chars<T>("InfinityMeow" , fmt, 8, errc{}, inf); |
| 825 | |
| 826 | test_from_chars<T>("-InF" , fmt, 4, errc{}, -inf); |
| 827 | test_from_chars<T>("-infinite" , fmt, 4, errc{}, -inf); |
| 828 | test_from_chars<T>("-iNfInItY" , fmt, 9, errc{}, -inf); |
| 829 | test_from_chars<T>("-InfinityMeow" , fmt, 9, errc{}, -inf); |
| 830 | |
| 831 | test_from_chars<T>("NaN" , fmt, 3, errc{}, qnan); |
| 832 | test_from_chars<T>("nanotech" , fmt, 3, errc{}, qnan); |
| 833 | test_from_chars<T>("nan(" , fmt, 3, errc{}, qnan); |
| 834 | test_from_chars<T>("nan(@)" , fmt, 3, errc{}, qnan); |
| 835 | test_from_chars<T>("nan(()" , fmt, 3, errc{}, qnan); |
| 836 | test_from_chars<T>("nan(abc" , fmt, 3, errc{}, qnan); |
| 837 | test_from_chars<T>("nan()" , fmt, 5, errc{}, qnan); |
| 838 | test_from_chars<T>("nan(abc)def" , fmt, 8, errc{}, qnan); |
| 839 | test_from_chars<T>("nan(_09AZaz)" , fmt, 12, errc{}, qnan); |
| 840 | test_from_chars<T>("nan(int)" , fmt, 8, errc{}, qnan); |
| 841 | test_from_chars<T>("nan(snap)" , fmt, 9, errc{}, qnan); |
| 842 | |
| 843 | test_from_chars<T>("-NaN" , fmt, 4, errc{}, -qnan); |
| 844 | test_from_chars<T>("-nanotech" , fmt, 4, errc{}, -qnan); |
| 845 | test_from_chars<T>("-nan(" , fmt, 4, errc{}, -qnan); |
| 846 | test_from_chars<T>("-nan(@)" , fmt, 4, errc{}, -qnan); |
| 847 | test_from_chars<T>("-nan(()" , fmt, 4, errc{}, -qnan); |
| 848 | test_from_chars<T>("-nan(abc" , fmt, 4, errc{}, -qnan); |
| 849 | test_from_chars<T>("-nan()" , fmt, 6, errc{}, -qnan); |
| 850 | test_from_chars<T>("-nan(abc)def" , fmt, 9, errc{}, -qnan); |
| 851 | test_from_chars<T>("-nan(_09AZaz)" , fmt, 13, errc{}, -qnan); |
| 852 | test_from_chars<T>("-nan(int)" , fmt, 9, errc{}, -qnan); |
| 853 | test_from_chars<T>("-nan(snap)" , fmt, 10, errc{}, -qnan); |
| 854 | |
| 855 | // The UCRT considers indeterminate NaN to be negative quiet NaN with no payload bits set. |
| 856 | // It parses "nan(ind)" and "-nan(ind)" identically. |
| 857 | # ifdef _MSC_VER |
| 858 | test_from_chars<T>("nan(InD)" , fmt, 8, errc{}, -qnan); |
| 859 | test_from_chars<T>("-nan(InD)" , fmt, 9, errc{}, -qnan); |
| 860 | |
| 861 | test_from_chars<T>("nan(SnAn)" , fmt, 9, errc{}, nullopt, TestFromCharsMode::SignalingNaN); |
| 862 | test_from_chars<T>("-nan(SnAn)" , fmt, 10, errc{}, nullopt, TestFromCharsMode::SignalingNaN); |
| 863 | # endif |
| 864 | |
| 865 | switch (fmt) { |
| 866 | case chars_format::general: |
| 867 | test_from_chars<T>("1729" , fmt, 4, errc{}, T{1729}); |
| 868 | test_from_chars<T>("1729e3" , fmt, 6, errc{}, T{1729000}); |
| 869 | test_from_chars<T>("10" , fmt, 2, errc{}, T{10}); |
| 870 | test_from_chars<T>("11." , fmt, 3, errc{}, T{11}); |
| 871 | test_from_chars<T>("12.13" , fmt, 5, errc{}, static_cast<T>(12.13)); // avoid truncation warning |
| 872 | test_from_chars<T>(".14" , fmt, 3, errc{}, static_cast<T>(.14)); // avoid truncation warning |
| 873 | test_from_chars<T>("20e5" , fmt, 4, errc{}, T{2000000}); |
| 874 | test_from_chars<T>("21.e5" , fmt, 5, errc{}, T{2100000}); |
| 875 | test_from_chars<T>("22.23e5" , fmt, 7, errc{}, T{2223000}); |
| 876 | test_from_chars<T>(".24e5" , fmt, 5, errc{}, T{24000}); |
| 877 | test_from_chars<T>("33e+5" , fmt, 5, errc{}, T{3300000}); |
| 878 | test_from_chars<T>("33e-5" , fmt, 5, errc{}, static_cast<T>(.00033)); // avoid truncation warning |
| 879 | test_from_chars<T>("4E7" , fmt, 3, errc{}, T{40000000}); |
| 880 | test_from_chars<T>("-00123abc" , fmt, 6, errc{}, T{-123}); |
| 881 | test_from_chars<T>(".0045000" , fmt, 8, errc{}, static_cast<T>(.0045)); // avoid truncation warning |
| 882 | test_from_chars<T>("000" , fmt, 3, errc{}, T{0}); |
| 883 | test_from_chars<T>("0e9999" , fmt, 6, errc{}, T{0}); |
| 884 | test_from_chars<T>("0e-9999" , fmt, 7, errc{}, T{0}); |
| 885 | test_from_chars<T>("-000" , fmt, 4, errc{}, T{-0.0}); |
| 886 | test_from_chars<T>("-0e9999" , fmt, 7, errc{}, T{-0.0}); |
| 887 | test_from_chars<T>("-0e-9999" , fmt, 8, errc{}, T{-0.0}); |
| 888 | test_from_chars<T>("1e9999" , fmt, 6, errc::result_out_of_range, inf); |
| 889 | test_from_chars<T>("-1e9999" , fmt, 7, errc::result_out_of_range, -inf); |
| 890 | test_from_chars<T>("1e-9999" , fmt, 7, errc::result_out_of_range, T{0}); |
| 891 | test_from_chars<T>("-1e-9999" , fmt, 8, errc::result_out_of_range, T{-0.0}); |
| 892 | test_from_chars<T>("1" + string(6000, '0'), fmt, 6001, errc::result_out_of_range, inf); |
| 893 | test_from_chars<T>("-1" + string(6000, '0'), fmt, 6002, errc::result_out_of_range, -inf); |
| 894 | test_from_chars<T>("." + string(6000, '0') + "1" , fmt, 6002, errc::result_out_of_range, T{0}); |
| 895 | test_from_chars<T>("-." + string(6000, '0') + "1" , fmt, 6003, errc::result_out_of_range, T{-0.0}); |
| 896 | test_from_chars<T>("1" + string(500, '0'), fmt, 501, errc::result_out_of_range, inf); |
| 897 | test_from_chars<T>("-1" + string(500, '0'), fmt, 502, errc::result_out_of_range, -inf); |
| 898 | test_from_chars<T>("." + string(500, '0') + "1" , fmt, 502, errc::result_out_of_range, T{0}); |
| 899 | test_from_chars<T>("-." + string(500, '0') + "1" , fmt, 503, errc::result_out_of_range, T{-0.0}); |
| 900 | break; |
| 901 | case chars_format::scientific: |
| 902 | test_from_chars<T>("1729" , fmt, 0, inv_arg); |
| 903 | test_from_chars<T>("1729e3" , fmt, 6, errc{}, T{1729000}); |
| 904 | break; |
| 905 | case chars_format::fixed: |
| 906 | test_from_chars<T>("1729" , fmt, 4, errc{}, T{1729}); |
| 907 | test_from_chars<T>("1729e3" , fmt, 4, errc{}, T{1729}); |
| 908 | break; |
| 909 | case chars_format::hex: |
| 910 | test_from_chars<T>("0x123" , fmt, 1, errc{}, T{0}); |
| 911 | test_from_chars<T>("a0" , fmt, 2, errc{}, T{160}); |
| 912 | test_from_chars<T>("a1." , fmt, 3, errc{}, T{161}); |
| 913 | test_from_chars<T>("a2.a3" , fmt, 5, errc{}, T{162.63671875}); |
| 914 | test_from_chars<T>(".a4" , fmt, 3, errc{}, T{0.640625}); |
| 915 | test_from_chars<T>("a0p5" , fmt, 4, errc{}, T{5120}); |
| 916 | test_from_chars<T>("a1.p5" , fmt, 5, errc{}, T{5152}); |
| 917 | test_from_chars<T>("a2.a3p5" , fmt, 7, errc{}, T{5204.375}); |
| 918 | test_from_chars<T>(".a4p5" , fmt, 5, errc{}, T{20.5}); |
| 919 | test_from_chars<T>("a0p+5" , fmt, 5, errc{}, T{5120}); |
| 920 | test_from_chars<T>("a0p-5" , fmt, 5, errc{}, T{5}); |
| 921 | test_from_chars<T>("ABCDEFP3" , fmt, 8, errc{}, T{90075000}); |
| 922 | test_from_chars<T>("-00cdrom" , fmt, 5, errc{}, T{-205}); |
| 923 | test_from_chars<T>(".00ef000" , fmt, 8, errc{}, T{0.0036468505859375}); |
| 924 | test_from_chars<T>("000" , fmt, 3, errc{}, T{0}); |
| 925 | test_from_chars<T>("0p9999" , fmt, 6, errc{}, T{0}); |
| 926 | test_from_chars<T>("0p-9999" , fmt, 7, errc{}, T{0}); |
| 927 | test_from_chars<T>("-000" , fmt, 4, errc{}, T{-0.0}); |
| 928 | test_from_chars<T>("-0p9999" , fmt, 7, errc{}, T{-0.0}); |
| 929 | test_from_chars<T>("-0p-9999" , fmt, 8, errc{}, T{-0.0}); |
| 930 | test_from_chars<T>("1p9999" , fmt, 6, errc::result_out_of_range, inf); |
| 931 | test_from_chars<T>("-1p9999" , fmt, 7, errc::result_out_of_range, -inf); |
| 932 | test_from_chars<T>("1p-9999" , fmt, 7, errc::result_out_of_range, T{0}); |
| 933 | test_from_chars<T>("-1p-9999" , fmt, 8, errc::result_out_of_range, T{-0.0}); |
| 934 | test_from_chars<T>("1" + string(2000, '0'), fmt, 2001, errc::result_out_of_range, inf); |
| 935 | test_from_chars<T>("-1" + string(2000, '0'), fmt, 2002, errc::result_out_of_range, -inf); |
| 936 | test_from_chars<T>("." + string(2000, '0') + "1" , fmt, 2002, errc::result_out_of_range, T{0}); |
| 937 | test_from_chars<T>("-." + string(2000, '0') + "1" , fmt, 2003, errc::result_out_of_range, T{-0.0}); |
| 938 | test_from_chars<T>("1" + string(300, '0'), fmt, 301, errc::result_out_of_range, inf); |
| 939 | test_from_chars<T>("-1" + string(300, '0'), fmt, 302, errc::result_out_of_range, -inf); |
| 940 | test_from_chars<T>("." + string(300, '0') + "1" , fmt, 302, errc::result_out_of_range, T{0}); |
| 941 | test_from_chars<T>("-." + string(300, '0') + "1" , fmt, 303, errc::result_out_of_range, T{-0.0}); |
| 942 | break; |
| 943 | } |
| 944 | } |
| 945 | #endif // TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 946 | |
| 947 | template <typename T> |
| 948 | void test_floating_to_chars( |
| 949 | const T value, const optional<chars_format> opt_fmt, const optional<int> opt_precision, const string_view correct) { |
| 950 | |
| 951 | test_common_to_chars(value, opt_fmt, opt_precision, correct); |
| 952 | } |
| 953 | |
| 954 | void all_floating_tests(mt19937_64& mt64) { |
| 955 | test_floating_prefixes(mt64); |
| 956 | |
| 957 | #ifdef TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 958 | for (const auto& fmt : {chars_format::general, chars_format::scientific, chars_format::fixed, chars_format::hex}) { |
| 959 | test_floating_from_chars<float>(fmt); |
| 960 | test_floating_from_chars<double>(fmt); |
| 961 | } |
| 962 | // Test rounding. |
| 963 | |
| 964 | // See float_from_chars_test_cases.hpp in this directory. |
| 965 | for (const auto& t : float_from_chars_test_cases) { |
| 966 | test_from_chars<float>(t.input, t.fmt, t.correct_idx, t.correct_ec, t.correct_value); |
| 967 | } |
| 968 | |
| 969 | // See double_from_chars_test_cases.hpp in this directory. |
| 970 | for (const auto& t : double_from_chars_test_cases) { |
| 971 | test_from_chars<double>(t.input, t.fmt, t.correct_idx, t.correct_ec, t.correct_value); |
| 972 | } |
| 973 | |
| 974 | { |
| 975 | // See LWG-2403. This number (exactly 0x1.fffffd00000004 in infinite precision) behaves differently |
| 976 | // when parsed as double and converted to float, versus being parsed as float directly. |
| 977 | const char* const lwg_2403 = "1.999999821186065729339276231257827021181583404541015625" ; |
| 978 | constexpr float correct_float = 0x1.fffffep0f; |
| 979 | constexpr double correct_double = 0x1.fffffdp0; |
| 980 | constexpr float twice_rounded_float = 0x1.fffffcp0f; |
| 981 | |
| 982 | test_from_chars<float>(lwg_2403, chars_format::general, 56, errc{}, correct_float); |
| 983 | test_from_chars<double>(lwg_2403, chars_format::general, 56, errc{}, correct_double); |
| 984 | static_assert(static_cast<float>(correct_double) == twice_rounded_float); |
| 985 | } |
| 986 | |
| 987 | // See floating_point_test_cases.hpp. |
| 988 | for (const auto& p : floating_point_test_cases_float) { |
| 989 | test_from_chars<float>(p.first, chars_format::general, strlen(p.first), errc{}, _Bit_cast<float>(p.second)); |
| 990 | } |
| 991 | |
| 992 | for (const auto& p : floating_point_test_cases_double) { |
| 993 | test_from_chars<double>(p.first, chars_format::general, strlen(p.first), errc{}, _Bit_cast<double>(p.second)); |
| 994 | } |
| 995 | #endif // TEST_HAS_FROM_CHARS_FLOATING_POINT |
| 996 | |
| 997 | // See float_to_chars_test_cases.hpp in this directory. |
| 998 | for (const auto& t : float_to_chars_test_cases) { |
| 999 | if (t.fmt == chars_format{}) { |
| 1000 | test_floating_to_chars(value: t.value, opt_fmt: nullopt, opt_precision: nullopt, correct: t.correct); |
| 1001 | } else { |
| 1002 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: nullopt, correct: t.correct); |
| 1003 | } |
| 1004 | } |
| 1005 | |
| 1006 | // See double_to_chars_test_cases.hpp in this directory. |
| 1007 | for (const auto& t : double_to_chars_test_cases) { |
| 1008 | if (t.fmt == chars_format{}) { |
| 1009 | test_floating_to_chars(value: t.value, opt_fmt: nullopt, opt_precision: nullopt, correct: t.correct); |
| 1010 | } else { |
| 1011 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: nullopt, correct: t.correct); |
| 1012 | } |
| 1013 | } |
| 1014 | |
| 1015 | // See corresponding headers in this directory. |
| 1016 | for (const auto& t : float_hex_precision_to_chars_test_cases) { |
| 1017 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1018 | } |
| 1019 | for (const auto& t : float_fixed_precision_to_chars_test_cases) { |
| 1020 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1021 | } |
| 1022 | for (const auto& t : float_scientific_precision_to_chars_test_cases) { |
| 1023 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1024 | } |
| 1025 | for (const auto& t : float_general_precision_to_chars_test_cases) { |
| 1026 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1027 | } |
| 1028 | for (const auto& t : double_hex_precision_to_chars_test_cases) { |
| 1029 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1030 | } |
| 1031 | for (const auto& t : double_fixed_precision_to_chars_test_cases_1) { |
| 1032 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1033 | } |
| 1034 | for (const auto& t : double_fixed_precision_to_chars_test_cases_2) { |
| 1035 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1036 | } |
| 1037 | for (const auto& t : double_fixed_precision_to_chars_test_cases_3) { |
| 1038 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1039 | } |
| 1040 | for (const auto& t : double_fixed_precision_to_chars_test_cases_4) { |
| 1041 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1042 | } |
| 1043 | for (const auto& t : double_scientific_precision_to_chars_test_cases_1) { |
| 1044 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1045 | } |
| 1046 | for (const auto& t : double_scientific_precision_to_chars_test_cases_2) { |
| 1047 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1048 | } |
| 1049 | for (const auto& t : double_scientific_precision_to_chars_test_cases_3) { |
| 1050 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1051 | } |
| 1052 | for (const auto& t : double_scientific_precision_to_chars_test_cases_4) { |
| 1053 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1054 | } |
| 1055 | for (const auto& t : double_general_precision_to_chars_test_cases) { |
| 1056 | test_floating_to_chars(value: t.value, opt_fmt: t.fmt, opt_precision: t.precision, correct: t.correct); |
| 1057 | } |
| 1058 | } |
| 1059 | |
| 1060 | int main(int argc, char** argv) { |
| 1061 | const auto start = chrono::steady_clock::now(); |
| 1062 | |
| 1063 | mt19937_64 mt64; |
| 1064 | |
| 1065 | initialize_randomness(mt64, argc, argv); |
| 1066 | |
| 1067 | all_integer_tests(); |
| 1068 | |
| 1069 | all_floating_tests(mt64); |
| 1070 | |
| 1071 | const auto finish = chrono::steady_clock::now(); |
| 1072 | const long long ms = chrono::duration_cast<chrono::milliseconds>(d: finish - start).count(); |
| 1073 | |
| 1074 | puts(s: "PASS" ); |
| 1075 | printf(format: "Randomized test cases: %zu\n" , static_cast<std::size_t>(PrefixesToTest * Fractions)); |
| 1076 | printf(format: "Total time: %lld ms\n" , ms); |
| 1077 | |
| 1078 | if (ms < 3'000) { |
| 1079 | puts(s: "That was fast. Consider tuning PrefixesToTest and FractionBits to test more cases." ); |
| 1080 | } else if (ms > 30'000) { |
| 1081 | puts(s: "That was slow. Consider tuning PrefixesToTest and FractionBits to test fewer cases." ); |
| 1082 | } |
| 1083 | } |
| 1084 | |