hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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 };