| 1 | module strconv |
| 2 | |
| 3 | /*============================================================================= |
| 4 | |
| 5 | f32 to string |
| 6 | |
| 7 | Copyright (c) 2019-2024 Dario Deledda. All rights reserved. |
| 8 | Use of this source code is governed by an MIT license |
| 9 | that can be found in the LICENSE file. |
| 10 | |
| 11 | This file contains the f32 to string functions |
| 12 | |
| 13 | These functions are based on the work of: |
| 14 | Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN |
| 15 | Conference on Programming Language Design and ImplementationJune 2018 |
| 16 | Pages 270–282 https://doi.org/10.1145/3192366.3192369 |
| 17 | |
| 18 | inspired by the Go version here: |
| 19 | https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea |
| 20 | |
| 21 | =============================================================================*/ |
| 22 | |
| 23 | // pow of ten table used by n_digit reduction |
| 24 | const ten_pow_table_32 = [ |
| 25 | u32(1), |
| 26 | u32(10), |
| 27 | u32(100), |
| 28 | u32(1000), |
| 29 | u32(10000), |
| 30 | u32(100000), |
| 31 | u32(1000000), |
| 32 | u32(10000000), |
| 33 | u32(100000000), |
| 34 | u32(1000000000), |
| 35 | ]! |
| 36 | |
| 37 | //============================================================================= |
| 38 | // Conversion Functions |
| 39 | //============================================================================= |
| 40 | const mantbits32 = u32(23) |
| 41 | const expbits32 = u32(8) |
| 42 | const bias32 = 127 // f32 exponent bias |
| 43 | |
| 44 | const maxexp32 = 255 |
| 45 | |
| 46 | // max 46 char |
| 47 | // -3.40282346638528859811704183484516925440e+38 |
| 48 | @[direct_array_access] |
| 49 | pub fn (d Dec32) get_string_32(neg bool, i_n_digit int, i_pad_digit int) string { |
| 50 | n_digit := i_n_digit + 1 |
| 51 | pad_digit := i_pad_digit + 1 |
| 52 | mut out := d.m |
| 53 | // mut out_len := decimal_len_32(out) |
| 54 | mut out_len := dec_digits(out) |
| 55 | out_len_original := out_len |
| 56 | |
| 57 | mut fw_zeros := 0 |
| 58 | if pad_digit > out_len { |
| 59 | fw_zeros = pad_digit - out_len |
| 60 | } |
| 61 | |
| 62 | mut buf := []u8{len: int(out_len + 5 + 1 + 1)} // sign + mant_len + . + e + e_sign + exp_len(2) + \0} |
| 63 | mut i := 0 |
| 64 | |
| 65 | if neg { |
| 66 | if buf.data != 0 { |
| 67 | // The buf.data != 0 check here, is needed for clean compilation |
| 68 | // with `-cc gcc -cstrict -prod`. Without it, gcc produces: |
| 69 | // error: potential null pointer dereference |
| 70 | buf[i] = `-` |
| 71 | } |
| 72 | i++ |
| 73 | } |
| 74 | |
| 75 | mut disp := 0 |
| 76 | if out_len <= 1 { |
| 77 | disp = 1 |
| 78 | } |
| 79 | |
| 80 | if n_digit < out_len { |
| 81 | // println("orig: ${out_len_original}") |
| 82 | out += ten_pow_table_32[out_len - n_digit - 1] * 5 // round to up |
| 83 | out /= ten_pow_table_32[out_len - n_digit] |
| 84 | out_len = n_digit |
| 85 | } |
| 86 | |
| 87 | y := i + out_len |
| 88 | mut x := 0 |
| 89 | for x < (out_len - disp - 1) { |
| 90 | buf[y - x] = `0` + u8(out % 10) |
| 91 | out /= 10 |
| 92 | i++ |
| 93 | x++ |
| 94 | } |
| 95 | |
| 96 | // no decimal digits needed, end here |
| 97 | if i_n_digit == 0 { |
| 98 | unsafe { |
| 99 | buf[i] = 0 |
| 100 | return tos(memdup(&buf[0], i + 1), i) |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | if out_len > 1 || fw_zeros > 0 { |
| 105 | buf[y - x] = `.` |
| 106 | i++ |
| 107 | } |
| 108 | x++ |
| 109 | |
| 110 | if y - x >= 0 { |
| 111 | buf[y - x] = `0` + u8(out % 10) |
| 112 | i++ |
| 113 | } |
| 114 | |
| 115 | for fw_zeros > 0 { |
| 116 | buf[i] = `0` |
| 117 | i++ |
| 118 | fw_zeros-- |
| 119 | } |
| 120 | |
| 121 | buf[i] = `e` |
| 122 | i++ |
| 123 | |
| 124 | mut exp := d.e + out_len_original - 1 |
| 125 | if exp < 0 { |
| 126 | buf[i] = `-` |
| 127 | i++ |
| 128 | exp = -exp |
| 129 | } else { |
| 130 | buf[i] = `+` |
| 131 | i++ |
| 132 | } |
| 133 | |
| 134 | // Always print two digits to match strconv's formatting. |
| 135 | d1 := exp % 10 |
| 136 | d0 := exp / 10 |
| 137 | buf[i] = `0` + u8(d0) |
| 138 | i++ |
| 139 | buf[i] = `0` + u8(d1) |
| 140 | i++ |
| 141 | buf[i] = 0 |
| 142 | |
| 143 | return unsafe { |
| 144 | tos(memdup(&buf[0], i + 1), i) |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | fn f32_to_decimal_exact_int(i_mant u32, exp u32) (Dec32, bool) { |
| 149 | mut d := Dec32{} |
| 150 | e := exp - bias32 |
| 151 | if e > mantbits32 { |
| 152 | return d, false |
| 153 | } |
| 154 | shift := mantbits32 - e |
| 155 | mant := i_mant | 0x0080_0000 // implicit 1 |
| 156 | // mant := i_mant | (1 << mantbits32) // implicit 1 |
| 157 | d.m = mant >> shift |
| 158 | if (d.m << shift) != mant { |
| 159 | return d, false |
| 160 | } |
| 161 | for (d.m % 10) == 0 { |
| 162 | d.m /= 10 |
| 163 | d.e++ |
| 164 | } |
| 165 | return d, true |
| 166 | } |
| 167 | |
| 168 | fn f32_to_decimal(mant u32, exp u32) Dec32 { |
| 169 | mut e2 := 0 |
| 170 | mut m2 := u32(0) |
| 171 | if exp == 0 { |
| 172 | // We subtract 2 so that the bounds computation has |
| 173 | // 2 additional bits. |
| 174 | e2 = 1 - bias32 - int(mantbits32) - 2 |
| 175 | m2 = mant |
| 176 | } else { |
| 177 | e2 = int(exp) - bias32 - int(mantbits32) - 2 |
| 178 | m2 = (u32(1) << mantbits32) | mant |
| 179 | } |
| 180 | even := (m2 & 1) == 0 |
| 181 | accept_bounds := even |
| 182 | |
| 183 | // Step 2: Determine the interval of valid decimal representations. |
| 184 | mv := u32(4 * m2) |
| 185 | mp := u32(4 * m2 + 2) |
| 186 | mm_shift := bool_to_u32(mant != 0 || exp <= 1) |
| 187 | mm := u32(4 * m2 - 1 - mm_shift) |
| 188 | |
| 189 | mut vr := u32(0) |
| 190 | mut vp := u32(0) |
| 191 | mut vm := u32(0) |
| 192 | mut e10 := 0 |
| 193 | mut vm_is_trailing_zeros := false |
| 194 | mut vr_is_trailing_zeros := false |
| 195 | mut last_removed_digit := u8(0) |
| 196 | |
| 197 | if e2 >= 0 { |
| 198 | q := log10_pow2(e2) |
| 199 | e10 = int(q) |
| 200 | k := pow5_inv_num_bits_32 + pow5_bits(int(q)) - 1 |
| 201 | i := -e2 + int(q) + k |
| 202 | |
| 203 | vr = mul_pow5_invdiv_pow2(mv, q, i) |
| 204 | vp = mul_pow5_invdiv_pow2(mp, q, i) |
| 205 | vm = mul_pow5_invdiv_pow2(mm, q, i) |
| 206 | if q != 0 && (vp - 1) / 10 <= vm / 10 { |
| 207 | // We need to know one removed digit even if we are not |
| 208 | // going to loop below. We could use q = X - 1 above, |
| 209 | // except that would require 33 bits for the result, and |
| 210 | // we've found that 32-bit arithmetic is faster even on |
| 211 | // 64-bit machines. |
| 212 | l := pow5_inv_num_bits_32 + pow5_bits(int(q - 1)) - 1 |
| 213 | last_removed_digit = u8(mul_pow5_invdiv_pow2(mv, q - 1, -e2 + int(q - 1) + l) % 10) |
| 214 | } |
| 215 | if q <= 9 { |
| 216 | // The largest power of 5 that fits in 24 bits is 5^10, |
| 217 | // but q <= 9 seems to be safe as well. Only one of mp, |
| 218 | // mv, and mm can be a multiple of 5, if any. |
| 219 | if mv % 5 == 0 { |
| 220 | vr_is_trailing_zeros = multiple_of_power_of_five_32(mv, q) |
| 221 | } else if accept_bounds { |
| 222 | vm_is_trailing_zeros = multiple_of_power_of_five_32(mm, q) |
| 223 | } else if multiple_of_power_of_five_32(mp, q) { |
| 224 | vp-- |
| 225 | } |
| 226 | } |
| 227 | } else { |
| 228 | q := log10_pow5(-e2) |
| 229 | e10 = int(q) + e2 |
| 230 | i := -e2 - int(q) |
| 231 | k := pow5_bits(i) - pow5_num_bits_32 |
| 232 | mut j := int(q) - k |
| 233 | vr = mul_pow5_div_pow2(mv, u32(i), j) |
| 234 | vp = mul_pow5_div_pow2(mp, u32(i), j) |
| 235 | vm = mul_pow5_div_pow2(mm, u32(i), j) |
| 236 | if q != 0 && ((vp - 1) / 10) <= vm / 10 { |
| 237 | j = int(q) - 1 - (pow5_bits(i + 1) - pow5_num_bits_32) |
| 238 | last_removed_digit = u8(mul_pow5_div_pow2(mv, u32(i + 1), j) % 10) |
| 239 | } |
| 240 | if q <= 1 { |
| 241 | // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at |
| 242 | // least q trailing 0 bits. mv = 4 * m2, so it always |
| 243 | // has at least two trailing 0 bits. |
| 244 | vr_is_trailing_zeros = true |
| 245 | if accept_bounds { |
| 246 | // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit |
| 247 | // if mm_shift == 1. |
| 248 | vm_is_trailing_zeros = mm_shift == 1 |
| 249 | } else { |
| 250 | // mp = mv + 2, so it always has at least one |
| 251 | // trailing 0 bit. |
| 252 | vp-- |
| 253 | } |
| 254 | } else if q < 31 { |
| 255 | vr_is_trailing_zeros = multiple_of_power_of_two_32(mv, q - 1) |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | // Step 4: Find the shortest decimal representation |
| 260 | // in the interval of valid representations. |
| 261 | mut removed := 0 |
| 262 | mut out := u32(0) |
| 263 | if vm_is_trailing_zeros || vr_is_trailing_zeros { |
| 264 | // General case, which happens rarely (~4.0%). |
| 265 | for vp / 10 > vm / 10 { |
| 266 | vm_is_trailing_zeros = vm_is_trailing_zeros && (vm % 10) == 0 |
| 267 | vr_is_trailing_zeros = vr_is_trailing_zeros && last_removed_digit == 0 |
| 268 | last_removed_digit = u8(vr % 10) |
| 269 | vr /= 10 |
| 270 | vp /= 10 |
| 271 | vm /= 10 |
| 272 | removed++ |
| 273 | } |
| 274 | if vm_is_trailing_zeros { |
| 275 | for vm % 10 == 0 { |
| 276 | vr_is_trailing_zeros = vr_is_trailing_zeros && last_removed_digit == 0 |
| 277 | last_removed_digit = u8(vr % 10) |
| 278 | vr /= 10 |
| 279 | vp /= 10 |
| 280 | vm /= 10 |
| 281 | removed++ |
| 282 | } |
| 283 | } |
| 284 | if vr_is_trailing_zeros && last_removed_digit == 5 && (vr % 2) == 0 { |
| 285 | // Round even if the exact number is .....50..0. |
| 286 | last_removed_digit = 4 |
| 287 | } |
| 288 | out = vr |
| 289 | // We need to take vr + 1 if vr is outside bounds |
| 290 | // or we need to round up. |
| 291 | if (vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5 { |
| 292 | out++ |
| 293 | } |
| 294 | } else { |
| 295 | // Specialized for the common case (~96.0%). Percentages below |
| 296 | // are relative to this. Loop iterations below (approximately): |
| 297 | // 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01% |
| 298 | for vp / 10 > vm / 10 { |
| 299 | last_removed_digit = u8(vr % 10) |
| 300 | vr /= 10 |
| 301 | vp /= 10 |
| 302 | vm /= 10 |
| 303 | removed++ |
| 304 | } |
| 305 | // We need to take vr + 1 if vr is outside bounds |
| 306 | // or we need to round up. |
| 307 | out = vr + bool_to_u32(vr == vm || last_removed_digit >= 5) |
| 308 | } |
| 309 | |
| 310 | return Dec32{ |
| 311 | m: out |
| 312 | e: e10 + removed |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | //============================================================================= |
| 317 | // String Functions |
| 318 | //============================================================================= |
| 319 | |
| 320 | // f32_to_str returns a `string` in scientific notation with max `n_digit` after the dot. |
| 321 | pub fn f32_to_str(f f32, n_digit int) string { |
| 322 | mut u1 := Uf32{} |
| 323 | u1.f = f |
| 324 | u := unsafe { u1.u } |
| 325 | |
| 326 | neg := (u >> (mantbits32 + expbits32)) != 0 |
| 327 | mant := u & ((u32(1) << mantbits32) - u32(1)) |
| 328 | exp := (u >> mantbits32) & ((u32(1) << expbits32) - u32(1)) |
| 329 | |
| 330 | // println("${neg} ${mant} e ${exp-bias32}") |
| 331 | |
| 332 | // Exit early for easy cases. |
| 333 | if exp == maxexp32 || (exp == 0 && mant == 0) { |
| 334 | return get_string_special(neg, exp == 0, mant == 0) |
| 335 | } |
| 336 | |
| 337 | mut d, ok := f32_to_decimal_exact_int(mant, exp) |
| 338 | if !ok { |
| 339 | // println("with exp form") |
| 340 | d = f32_to_decimal(mant, exp) |
| 341 | } |
| 342 | |
| 343 | // println("${d.m} ${d.e}") |
| 344 | return d.get_string_32(neg, n_digit, 0) |
| 345 | } |
| 346 | |
| 347 | // f32_to_str_pad returns a `string` in scientific notation with max `n_digit` after the dot. |
| 348 | pub fn f32_to_str_pad(f f32, n_digit int) string { |
| 349 | mut u1 := Uf32{} |
| 350 | u1.f = f |
| 351 | u := unsafe { u1.u } |
| 352 | |
| 353 | neg := (u >> (mantbits32 + expbits32)) != 0 |
| 354 | mant := u & ((u32(1) << mantbits32) - u32(1)) |
| 355 | exp := (u >> mantbits32) & ((u32(1) << expbits32) - u32(1)) |
| 356 | |
| 357 | // println("${neg} ${mant} e ${exp-bias32}") |
| 358 | |
| 359 | // Exit early for easy cases. |
| 360 | if exp == maxexp32 || (exp == 0 && mant == 0) { |
| 361 | return get_string_special(neg, exp == 0, mant == 0) |
| 362 | } |
| 363 | |
| 364 | mut d, ok := f32_to_decimal_exact_int(mant, exp) |
| 365 | if !ok { |
| 366 | // println("with exp form") |
| 367 | d = f32_to_decimal(mant, exp) |
| 368 | } |
| 369 | |
| 370 | // println("${d.m} ${d.e}") |
| 371 | return d.get_string_32(neg, n_digit, n_digit) |
| 372 | } |
| 373 | |