diff options
Diffstat (limited to 'v_windows/v/old/vlib/strconv/atof.v')
-rw-r--r-- | v_windows/v/old/vlib/strconv/atof.v | 441 |
1 files changed, 441 insertions, 0 deletions
diff --git a/v_windows/v/old/vlib/strconv/atof.v b/v_windows/v/old/vlib/strconv/atof.v new file mode 100644 index 0000000..dd994bd --- /dev/null +++ b/v_windows/v/old/vlib/strconv/atof.v @@ -0,0 +1,441 @@ +module strconv + +/* +atof util + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains utilities for convert a string in a f64 variable +IEEE 754 standard is used + +Know limitation: +- limited to 18 significant digits + +The code is inspired by: +Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl +URL: http://krashan.ppa.pl/articles/stringtofloat/ +Original license: MIT + +96 bit operation utilities +Note: when u128 will be available these function can be refactored +*/ + +// right logical shift 96 bit +fn lsr96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + r0 = (s0 >> 1) | ((s1 & u32(1)) << 31) + r1 = (s1 >> 1) | ((s2 & u32(1)) << 31) + r2 = s2 >> 1 + return r2, r1, r0 +} + +// left logical shift 96 bit +fn lsl96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + r2 = (s2 << 1) | ((s1 & (u32(1) << 31)) >> 31) + r1 = (s1 << 1) | ((s0 & (u32(1) << 31)) >> 31) + r0 = s0 << 1 + return r2, r1, r0 +} + +// sum on 96 bit +fn add96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { + mut w := u64(0) + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + w = u64(s0) + u64(d0) + r0 = u32(w) + w >>= 32 + w += u64(s1) + u64(d1) + r1 = u32(w) + w >>= 32 + w += u64(s2) + u64(d2) + r2 = u32(w) + return r2, r1, r0 +} + +// subtraction on 96 bit +fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { + mut w := u64(0) + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + w = u64(s0) - u64(d0) + r0 = u32(w) + w >>= 32 + w += u64(s1) - u64(d1) + r1 = u32(w) + w >>= 32 + w += u64(s2) - u64(d2) + r2 = u32(w) + return r2, r1, r0 +} + +/* +Constants +*/ + +pub const ( + // + // f32 constants + // + single_plus_zero = u32(0x0000_0000) + single_minus_zero = u32(0x8000_0000) + single_plus_infinity = u32(0x7F80_0000) + single_minus_infinity = u32(0xFF80_0000) + // + // f64 constants + // + digits = 18 + double_plus_zero = u64(0x0000000000000000) + double_minus_zero = u64(0x8000000000000000) + double_plus_infinity = u64(0x7FF0000000000000) + double_minus_infinity = u64(0xFFF0000000000000) + // + // Possible parser return values. + // + parser_ok = 0 // parser finished OK + parser_pzero = 1 // no digits or number is smaller than +-2^-1022 + parser_mzero = 2 // number is negative, module smaller + parser_pinf = 3 // number is higher than +HUGE_VAL + parser_minf = 4 // number is lower than -HUGE_VAL + // + // char constants + // Note: Modify these if working with non-ASCII encoding + // + c_dpoint = `.` + c_plus = `+` + c_minus = `-` + c_zero = `0` + c_nine = `9` + c_ten = u32(10) +) + +/* +Utility +*/ + +// NOTE: Modify these if working with non-ASCII encoding +fn is_digit(x byte) bool { + return (x >= strconv.c_zero && x <= strconv.c_nine) == true +} + +fn is_space(x byte) bool { + return (x == `\t` || x == `\n` || x == `\v` || x == `\f` || x == `\r` || x == ` `) +} + +fn is_exp(x byte) bool { + return (x == `E` || x == `e`) == true +} + +/* +Support struct +*/ + +/* +String parser +NOTE: #TOFIX need one char after the last char of the number +*/ + +fn parser(s string) (int, PrepNumber) { + mut digx := 0 + mut result := strconv.parser_ok + mut expneg := false + mut expexp := 0 + mut i := 0 + mut pn := PrepNumber{} + + // skip spaces + for i < s.len && s[i].is_space() { + i++ + } + + // check negatives + if s[i] == `-` { + pn.negative = true + i++ + } + + // positive sign ignore it + if s[i] == `+` { + i++ + } + + // read mantissa + for i < s.len && s[i].is_digit() { + // println("$i => ${s[i]}") + if digx < strconv.digits { + pn.mantissa *= 10 + pn.mantissa += u64(s[i] - strconv.c_zero) + digx++ + } else if pn.exponent < 2147483647 { + pn.exponent++ + } + i++ + } + + // read mantissa decimals + if (i < s.len) && (s[i] == `.`) { + i++ + for i < s.len && s[i].is_digit() { + if digx < strconv.digits { + pn.mantissa *= 10 + pn.mantissa += u64(s[i] - strconv.c_zero) + pn.exponent-- + digx++ + } + i++ + } + } + + // read exponent + if (i < s.len) && ((s[i] == `e`) || (s[i] == `E`)) { + i++ + if i < s.len { + // esponent sign + if s[i] == strconv.c_plus { + i++ + } else if s[i] == strconv.c_minus { + expneg = true + i++ + } + + for i < s.len && s[i].is_digit() { + if expexp < 214748364 { + expexp *= 10 + expexp += int(s[i] - strconv.c_zero) + } + i++ + } + } + } + + if expneg { + expexp = -expexp + } + pn.exponent += expexp + if pn.mantissa == 0 { + if pn.negative { + result = strconv.parser_mzero + } else { + result = strconv.parser_pzero + } + } else if pn.exponent > 309 { + if pn.negative { + result = strconv.parser_minf + } else { + result = strconv.parser_pinf + } + } else if pn.exponent < -328 { + if pn.negative { + result = strconv.parser_mzero + } else { + result = strconv.parser_pzero + } + } + return result, pn +} + +/* +Converter to the bit form of the f64 number +*/ + +// converter return a u64 with the bit image of the f64 number +fn converter(mut pn PrepNumber) u64 { + mut binexp := 92 + mut s2 := u32(0) // 96-bit precision integer + mut s1 := u32(0) + mut s0 := u32(0) + mut q2 := u32(0) // 96-bit precision integer + mut q1 := u32(0) + mut q0 := u32(0) + mut r2 := u32(0) // 96-bit precision integer + mut r1 := u32(0) + mut r0 := u32(0) + mask28 := u32(u64(0xF) << 28) + mut result := u64(0) + // working on 3 u32 to have 96 bit precision + s0 = u32(pn.mantissa & u64(0x00000000FFFFFFFF)) + s1 = u32(pn.mantissa >> 32) + s2 = u32(0) + // so we take the decimal exponent off + for pn.exponent > 0 { + q2, q1, q0 = lsl96(s2, s1, s0) // q = s * 2 + r2, r1, r0 = lsl96(q2, q1, q0) // r = s * 4 <=> q * 2 + s2, s1, s0 = lsl96(r2, r1, r0) // s = s * 8 <=> r * 2 + s2, s1, s0 = add96(s2, s1, s0, q2, q1, q0) // s = (s * 8) + (s * 2) <=> s*10 + pn.exponent-- + for (s2 & mask28) != 0 { + q2, q1, q0 = lsr96(s2, s1, s0) + binexp++ + s2 = q2 + s1 = q1 + s0 = q0 + } + } + for pn.exponent < 0 { + for !((s2 & (u32(1) << 31)) != 0) { + q2, q1, q0 = lsl96(s2, s1, s0) + binexp-- + s2 = q2 + s1 = q1 + s0 = q0 + } + q2 = s2 / strconv.c_ten + r1 = s2 % strconv.c_ten + r2 = (s1 >> 8) | (r1 << 24) + q1 = r2 / strconv.c_ten + r1 = r2 % strconv.c_ten + r2 = ((s1 & u32(0xFF)) << 16) | (s0 >> 16) | (r1 << 24) + r0 = r2 / strconv.c_ten + r1 = r2 % strconv.c_ten + q1 = (q1 << 8) | ((r0 & u32(0x00FF0000)) >> 16) + q0 = r0 << 16 + r2 = (s0 & u32(0xFFFF)) | (r1 << 16) + q0 |= r2 / strconv.c_ten + s2 = q2 + s1 = q1 + s0 = q0 + pn.exponent++ + } + // C.printf("mantissa before normalization: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) + // normalization, the 28 bit in s2 must the leftest one in the variable + if s2 != 0 || s1 != 0 || s0 != 0 { + for (s2 & mask28) == 0 { + q2, q1, q0 = lsl96(s2, s1, s0) + binexp-- + s2 = q2 + s1 = q1 + s0 = q0 + } + } + // rounding if needed + /* + * "round half to even" algorithm + * Example for f32, just a reminder + * + * If bit 54 is 0, round down + * If bit 54 is 1 + * If any bit beyond bit 54 is 1, round up + * If all bits beyond bit 54 are 0 (meaning the number is halfway between two floating-point numbers) + * If bit 53 is 0, round down + * If bit 53 is 1, round up + */ + /* + test case 1 complete + s2=0x1FFFFFFF + s1=0xFFFFFF80 + s0=0x0 + */ + + /* + test case 1 check_round_bit + s2=0x18888888 + s1=0x88888880 + s0=0x0 + */ + + /* + test case check_round_bit + normalization + s2=0x18888888 + s1=0x88888F80 + s0=0x0 + */ + + // C.printf("mantissa before rounding: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) + // s1 => 0xFFFFFFxx only F are rapresented + nbit := 7 + check_round_bit := u32(1) << u32(nbit) + check_round_mask := u32(0xFFFFFFFF) << u32(nbit) + if (s1 & check_round_bit) != 0 { + // C.printf("need round!! cehck mask: %08x\n", s1 & ~check_round_mask ) + if (s1 & ~check_round_mask) != 0 { + // C.printf("Add 1!\n") + s2, s1, s0 = add96(s2, s1, s0, 0, check_round_bit, 0) + } else { + // C.printf("All 0!\n") + if (s1 & (check_round_bit << u32(1))) != 0 { + // C.printf("Add 1 form -1 bit control!\n") + s2, s1, s0 = add96(s2, s1, s0, 0, check_round_bit, 0) + } + } + s1 = s1 & check_round_mask + s0 = u32(0) + // recheck normalization + if s2 & (mask28 << u32(1)) != 0 { + // C.printf("Renormalize!!") + q2, q1, q0 = lsr96(s2, s1, s0) + binexp-- + s2 = q2 + s1 = q1 + s0 = q0 + } + } + // tmp := ( u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8) + // C.printf("mantissa after rounding : %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) + // C.printf("Tmp result: %016x\n",tmp) + // end rounding + // offset the binary exponent IEEE 754 + binexp += 1023 + if binexp > 2046 { + if pn.negative { + result = strconv.double_minus_infinity + } else { + result = strconv.double_plus_infinity + } + } else if binexp < 1 { + if pn.negative { + result = strconv.double_minus_zero + } else { + result = strconv.double_plus_zero + } + } else if s2 != 0 { + mut q := u64(0) + binexs2 := u64(binexp) << 52 + q = (u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8) | binexs2 + if pn.negative { + q |= (u64(1) << 63) + } + result = q + } + return result +} + +/* +Public functions +*/ + +// atof64 return a f64 from a string doing a parsing operation +pub fn atof64(s string) f64 { + mut pn := PrepNumber{} + mut res_parsing := 0 + mut res := Float64u{} + + res_parsing, pn = parser(s) + match res_parsing { + strconv.parser_ok { + res.u = converter(mut pn) + } + strconv.parser_pzero { + res.u = strconv.double_plus_zero + } + strconv.parser_mzero { + res.u = strconv.double_minus_zero + } + strconv.parser_pinf { + res.u = strconv.double_plus_infinity + } + strconv.parser_minf { + res.u = strconv.double_minus_infinity + } + else {} + } + return unsafe { res.f } +} |