ftos_test.ha (9514B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use io; 5 use math; 6 use memio; 7 8 @test fn ftosf() void = { 9 // These tests should pass for both f32 and f64. 10 const tcs: [](f64, ffmt, (void | uint), fflags, str) = [ 11 // First test special values 12 (1.0 / 0.0, ffmt::G, void, 0, "infinity"), 13 (1.0 / 0.0, ffmt::G, void, fflags::SHOW_POS, "+infinity"), 14 (-1.0 / 0.0, ffmt::F, void, 0, "-infinity"), 15 (-1.0 / 0.0, ffmt::E, void, fflags::UPPERCASE, "-INFINITY"), 16 (0.0 / 0.0, ffmt::G, void, 0, "nan"), 17 (0.0 / 0.0, ffmt::E, void, fflags::UPPERCASE, "NAN"), 18 19 // Then a million tests for zero. 20 (0.0, ffmt::E, void, 0, "0e0"), 21 (0.0, ffmt::E, void, fflags::SHOW_TWO_EXP_DIGITS, "0e00"), 22 (0.0, ffmt::E, void, fflags::UPPER_EXP, "0E0"), 23 (0.0, ffmt::E, void, fflags::SHOW_POS_EXP, "0e+0"), 24 (0.0, ffmt::E, void, fflags::SHOW_POINT, "0.0e0"), 25 (0.0, ffmt::F, void, 0, "0"), 26 (0.0, ffmt::F, void, fflags::SHOW_POINT, "0.0"), 27 (0.0, ffmt::G, void, 0, "0"), 28 (-0.0, ffmt::G, void, fflags::SHOW_POS, "-0"), 29 (0.0, ffmt::G, void, fflags::SHOW_POS, "+0"), 30 (0.0, ffmt::G, void, fflags::SHOW_POINT, "0.0"), 31 32 // ... e and f do not cut trailing zeros 33 (0.0, ffmt::E, 0, 0, "0e0"), 34 (0.0, ffmt::E, 1, 0, "0.0e0"), 35 (0.0, ffmt::E, 2, 0, "0.00e0"), 36 (0.0, ffmt::F, 0, 0, "0"), 37 (0.0, ffmt::F, 1, 0, "0.0"), 38 (0.0, ffmt::F, 2, 0, "0.00"), 39 // ... g cuts trailing zeros 40 (0.0, ffmt::G, 0, 0, "0"), 41 (0.0, ffmt::G, 1, 0, "0"), 42 (0.0, ffmt::G, 2, 0, "0"), 43 44 // ... SHOW_POINT only changes precision 0 45 (0.0, ffmt::E, 0, fflags::SHOW_POINT, "0.0e0"), 46 (0.0, ffmt::E, 1, fflags::SHOW_POINT, "0.0e0"), 47 (0.0, ffmt::E, 2, fflags::SHOW_POINT, "0.00e0"), 48 (0.0, ffmt::F, 0, fflags::SHOW_POINT, "0.0"), 49 (0.0, ffmt::F, 1, fflags::SHOW_POINT, "0.0"), 50 (0.0, ffmt::F, 2, fflags::SHOW_POINT, "0.00"), 51 // ... g with SHOW_POINT only has the one extra 0 52 (0.0, ffmt::G, 0, fflags::SHOW_POINT, "0.0"), 53 (0.0, ffmt::G, 1, fflags::SHOW_POINT, "0.0"), 54 (0.0, ffmt::G, 2, fflags::SHOW_POINT, "0.0"), 55 56 // Now we can test actual numbers. 57 (10.0, ffmt::F, void, 0, "10"), 58 (1.0, ffmt::F, void, 0, "1"), 59 (1.1, ffmt::F, void, 0, "1.1"), 60 (13.37, ffmt::G, void, 0, "13.37"), 61 (0.3, ffmt::F, void, 0, "0.3"), 62 (0.0031415, ffmt::F, void, 0, "0.0031415"), 63 (-6345.972, ffmt::F, void, 0, "-6345.972"), 64 (1.414, ffmt::F, void, 0, "1.414"), 65 (1000000.0e9, ffmt::F, void, 0, "1000000000000000"), 66 (10.0, ffmt::E, void, 0, "1e1"), 67 (10.0, ffmt::E, void, fflags::SHOW_TWO_EXP_DIGITS, "1e01"), 68 (10.0, ffmt::E, void, fflags::UPPER_EXP, "1E1"), 69 (10.0, ffmt::E, void, fflags::SHOW_POS_EXP, "1e+1"), 70 (0.1, ffmt::E, void, fflags::SHOW_POS_EXP, "1e-1"), 71 (1.0, ffmt::E, void, 0, "1e0"), 72 (0.3, ffmt::E, void, 0, "3e-1"), 73 (0.0031415, ffmt::E, void, 0, "3.1415e-3"), 74 (0.12345, ffmt::E, void, 0, "1.2345e-1"), 75 76 // ... g is shortest 77 (12345.0, ffmt::G, void, 0, "12345"), 78 (10000.0, ffmt::G, void, 0, "1e4"), 79 (11000.0, ffmt::G, void, 0, "1.1e4"), 80 (1000.0, ffmt::G, void, 0, "1e3"), 81 (1100.0, ffmt::G, void, 0, "1100"), 82 (100.0, ffmt::G, void, 0, "100"), 83 (10.0, ffmt::G, void, 0, "10"), 84 (1.0, ffmt::G, void, 0, "1"), 85 (0.1, ffmt::G, void, 0, "0.1"), 86 (0.01, ffmt::G, void, 0, "0.01"), 87 (0.011, ffmt::G, void, 0, "0.011"), 88 (0.001, ffmt::G, void, 0, "1e-3"), // one shorter than f 89 (0.0011, ffmt::G, void, 0, "1.1e-3"), // same length as f 90 (0.0001, ffmt::G, void, 0, "1e-4"), 91 92 // ... fixed precision stuff 93 (0.5, ffmt::F, 0, 0, "0"), 94 (1.0 / 3.0, ffmt::F, 2, 0, "0.33"), 95 (1.0 / 3.0, ffmt::F, 1, 0, "0.3"), 96 (1.0 / 3.0, ffmt::F, 0, 0, "0"), 97 (1.0 / 3.0, ffmt::F, 0, fflags::SHOW_POINT, "0.3"), 98 (2.0 / 3.0, ffmt::F, 2, 0, "0.67"), 99 (2.0 / 3.0, ffmt::F, 1, 0, "0.7"), 100 (2.0 / 3.0, ffmt::F, 0, 0, "1"), 101 (2.0 / 3.0, ffmt::F, 0, fflags::SHOW_POINT, "0.7"), 102 (2.0 / 30.0, ffmt::F, 5, 0, "0.06667"), 103 (2.0 / 30.0, ffmt::F, 2, 0, "0.07"), 104 (2.0 / 30.0, ffmt::F, 1, 0, "0.1"), 105 (2.0 / 30.0, ffmt::F, 1, fflags::SHOW_POINT, "0.1"), 106 (200.0 / 3.0, ffmt::F, 4, 0, "66.6667"), 107 (100.0 / 3.0, ffmt::F, 4, 0, "33.3333"), 108 (100.0 / 3.0, ffmt::F, 0, 0, "33"), 109 (100.0 / 3.0, ffmt::F, 0, fflags::SHOW_POINT, "33.3"), 110 (0.00001, ffmt::F, 1, 0, "0.0"), 111 (0.001, ffmt::F, 2, 0, "0.00"), 112 (0.006, ffmt::F, 2, 0, "0.01"), 113 (0.001, ffmt::F, 6, 0, "0.001000"), 114 (1.0, ffmt::F, 6, 0, "1.000000"), 115 (100.0, ffmt::F, 6, 0, "100.000000"), 116 117 // ... scientific notation stuff 118 (460.0, ffmt::E, 2, 0, "4.60e2"), 119 (1.0 / 3.0, ffmt::E, 2, 0, "3.33e-1"), 120 (1.0 / 3.0, ffmt::E, 1, 0, "3.3e-1"), 121 (1.0 / 3.0, ffmt::E, 0, 0, "3e-1"), 122 (1.0 / 3.0, ffmt::E, 0, fflags::SHOW_POINT, "3.3e-1"), 123 (2.0 / 3.0, ffmt::E, 2, 0, "6.67e-1"), 124 (2.0 / 3.0, ffmt::E, 1, 0, "6.7e-1"), 125 (2.0 / 3.0, ffmt::E, 0, 0, "7e-1"), 126 (2.0 / 3.0, ffmt::E, 0, fflags::SHOW_POINT, "6.7e-1"), 127 (2.0 / 30.0, ffmt::E, 5, 0, "6.66667e-2"), 128 (2.0 / 30.0, ffmt::E, 2, 0, "6.67e-2"), 129 (2.0 / 30.0, ffmt::E, 0, 0, "7e-2"), 130 (2.0 / 30.0, ffmt::E, 0, fflags::SHOW_POINT, "6.7e-2"), 131 (200.0 / 3.0, ffmt::E, 5, 0, "6.66667e1"), 132 (100.0 / 3.0, ffmt::E, 5, 0, "3.33333e1"), 133 (100.0 / 3.0, ffmt::E, 0, 0, "3e1"), 134 (100.0 / 3.0, ffmt::E, 0, fflags::SHOW_POINT, "3.3e1"), 135 (0.001, ffmt::E, 2, 0, "1.00e-3"), 136 (1.0, ffmt::E, 6, 0, "1.000000e0"), 137 (100.0, ffmt::E, 6, 0, "1.000000e2"), 138 139 // ... and G. The behavior with SHOW_POINT is gnarly. 140 (1.0 / 3.0, ffmt::G, 2, 0, "0.33"), 141 (1.0 / 3.0, ffmt::G, 0, 0, "0.3"), 142 (0.01, ffmt::G, void, fflags::SHOW_POINT, "0.01"), 143 (1.0, ffmt::G, void, fflags::SHOW_POINT, "1.0"), 144 (0.0001, ffmt::G, 0, 0, "1e-4"), 145 (0.001, ffmt::G, 0, 0, "1e-3"), 146 (0.00123, ffmt::G, 2, 0, "1.2e-3"), 147 (0.01, ffmt::G, 5, 0, "0.01"), // trim trailing zeros 148 (0.1, ffmt::G, 5, 0, "0.1"), 149 (1.0, ffmt::G, 0, 0, "1"), 150 (10.0, ffmt::G, 0, 0, "10"), 151 (120.0, ffmt::G, 2, 0, "120"), 152 (12000.0, ffmt::G, 2, 0, "1.2e4"), 153 (0.0001, ffmt::G, 0, fflags::SHOW_POINT, "1.0e-4"), 154 (0.001, ffmt::G, 0, fflags::SHOW_POINT, "1.0e-3"), 155 (0.01, ffmt::G, 0, fflags::SHOW_POINT, "0.01"), 156 (0.1, ffmt::G, 0, fflags::SHOW_POINT, "0.1"), 157 (1.0, ffmt::G, 0, fflags::SHOW_POINT, "1.0"), 158 (10.0, ffmt::G, 0, fflags::SHOW_POINT, "10.0"), 159 (100.0, ffmt::G, 0, fflags::SHOW_POINT, "100.0"), 160 (1000.0, ffmt::G, 0, fflags::SHOW_POINT, "1.0e3"), 161 (0.0123, ffmt::G, 2, fflags::SHOW_POINT, "0.012"), 162 (0.0123, ffmt::G, 5, fflags::SHOW_POINT, "0.0123"), 163 ]; 164 const stream = memio::dynamic(); 165 defer io::close(&stream)!; 166 for (let i = 0z; i < len(tcs); i += 1) { 167 const z64 = fftosf(&stream, tcs[i].0, tcs[i].1, 168 tcs[i].2, tcs[i].3)!; 169 const res64 = memio::string(&stream)!; 170 assert(len(res64) == z64); 171 assert(res64 == tcs[i].4, res64); 172 if (tcs[i].2 is void) { 173 // void precision should guarantee that it parses back 174 // to the original number. 175 const back = stof64(res64)!; 176 assert(math::isnan(back) == math::isnan(tcs[i].0)); 177 if (!math::isnan(back)) assert(back == tcs[i].0); 178 }; 179 180 memio::reset(&stream); 181 const z32 = fftosf(&stream, tcs[i].0: f32, tcs[i].1, 182 tcs[i].2, tcs[i].3)!; 183 const res32 = memio::string(&stream)!; 184 assert(len(res32) == z32); 185 assert(res32 == tcs[i].4); 186 if (tcs[i].2 is void) { 187 const back = stof32(res32)!; 188 assert(math::isnan(back) == math::isnan(tcs[i].0)); 189 if (!math::isnan(back)) assert(back == tcs[i].0: f32); 190 }; 191 memio::reset(&stream); 192 }; 193 // These tests will only pass for f64 194 const tcsf64: [](f64, ffmt, (void | uint), fflags, str) = [ 195 (9007199254740991.0, ffmt::F, void, 0, "9007199254740991"), 196 (90071992547409915.0, ffmt::F, void, 0, "90071992547409920"), 197 (90071992547409925.0, ffmt::F, void, 0, "90071992547409920"), 198 (math::F64_MIN, ffmt::E, void, 0, "5e-324"), 199 (math::F64_MIN, ffmt::E, void, fflags::SHOW_TWO_EXP_DIGITS, "5e-324"), 200 (-math::F64_MIN, ffmt::E, void, 0, "-5e-324"), 201 (math::F64_MIN_NORMAL, ffmt::E, void, 0, "2.2250738585072014e-308"), 202 (math::F64_MAX_NORMAL, ffmt::E, void, 0, "1.7976931348623157e308"), 203 (math::F64_MIN, ffmt::E, 2, 0, "4.94e-324"), 204 (math::F64_MIN_NORMAL, ffmt::E, 0, 0, "2e-308"), 205 (math::F64_MAX_NORMAL, ffmt::E, 3, 0, "1.798e308"), 206 ]; 207 for (let i = 0z; i < len(tcsf64); i += 1) { 208 const z64 = fftosf(&stream, tcsf64[i].0, tcsf64[i].1, 209 tcsf64[i].2, tcsf64[i].3)!; 210 const res64 = memio::string(&stream)!; 211 assert(len(res64) == z64); 212 assert(res64 == tcsf64[i].4); 213 memio::reset(&stream); 214 }; 215 // These tests will only pass for f32 216 const tcsf32: [](f32, ffmt, (void | uint), fflags, str) = [ 217 (math::F32_MIN, ffmt::G, void, 0, "1e-45"), 218 (math::F32_MIN_NORMAL, ffmt::G, void, 0, "1.1754944e-38"), 219 (math::F32_MAX_NORMAL, ffmt::G, void, 0, "3.4028235e38"), 220 ]; 221 for (let i = 0z; i < len(tcsf32); i += 1) { 222 const z32 = fftosf(&stream, tcsf32[i].0, tcsf32[i].1, 223 tcsf32[i].2, tcsf32[i].3)!; 224 const res32 = memio::string(&stream)!; 225 assert(len(res32) == z32); 226 assert(res32 == tcsf32[i].4); 227 memio::reset(&stream); 228 }; 229 // Just make sure we can generate big numbers without breaking anything. 230 const tcslen: [](f64, ffmt, (void | uint), fflags, size) = [ 231 (9007199254740991.0, ffmt::F, void, 0, 16), 232 (-math::F64_MIN, ffmt::E, 100, 0, 108), 233 (1.0, ffmt::F, 1000, 0, 1002), 234 (2.22507385850720088902458687609E-308, ffmt::F, 1000, 0, 1002), 235 ]; 236 for (let i = 0z; i < len(tcslen); i += 1) { 237 const z64 = fftosf(&stream, tcslen[i].0, tcslen[i].1, 238 tcslen[i].2, tcslen[i].3)!; 239 const res64 = memio::string(&stream)!; 240 assert(len(res64) == z64); 241 assert(len(res64) == tcslen[i].4); 242 memio::reset(&stream); 243 }; 244 assert(f64tos(13.37) == "13.37"); 245 };