package strconv import "base:intrinsics" /* Scans a hexadecimal floating-point number at the start of `s`. `[+-] 0x hexdigits [. hexdigits] (p|P) [+-] digits` A `_` between digits is skipped **Returns** - mantissa, exp: The value is `mantissa * 2^exp`. If `trunc` is true, the significant hex digits after the first 16 were dropped, and at least one of them was not zero. - neg: The number has a minus sign. - nr: The number of bytes in the number. - ok: `false` if `s` does not start with a hexadecimal float. */ scan_hex_float :: proc "contextless" (s: string) -> (mantissa: u64, exp: int, neg, trunc: bool, nr: int, ok: bool) #no_bounds_check { MAX_HEX_DIGITS :: 16 n := len(s) if n == 0 { return } neg = s[0] == '-' i := int(neg || s[0] == '+') // "0x" is a hex prefix only if another byte follows it. if !(i+2 < n && s[i] == '0' && lower(s[i+1]) == 'x') { return } i += 2 nd := 0 // number of significant hex digits in the mantissa saw_digits := false // Integer part. Leading zeros do not count as significant digits. for i < n && (s[i] == '0' || s[i] == '_') { saw_digits ||= s[i] == '0' i += 1 } for i+8 <= n && nd+8 <= MAX_HEX_DIGITS { v := read8_to_u64(s, i) if !is_eight_hex_digits(v) { break } mantissa = mantissa<<32 | parse_eight_hex_digits(v) nd += 8 i += 8 saw_digits = true } for i < n { d := hex_digit_table[s[i]] if d >= 16 { if s[i] != '_' { break } } else if nd < MAX_HEX_DIGITS { mantissa = mantissa<<4 | u64(d) nd += 1 saw_digits = true } else { exp += 4 // dropped digit trunc ||= d != 0 } i += 1 } // Fraction part if i < n && s[i] == '.' { i += 1 if mantissa == 0 { // Leading zeros of the fraction change only the exponent for i < n && (s[i] == '0' || s[i] == '_') { if s[i] == '0' { exp -= 4 saw_digits = true } i += 1 } } for i+8 <= n && nd+8 <= MAX_HEX_DIGITS { v := read8_to_u64(s, i) if !is_eight_hex_digits(v) { break } mantissa = mantissa<<32 | parse_eight_hex_digits(v) nd += 8 exp -= 32 i += 8 saw_digits = true } if i+4 <= n && nd+4 <= MAX_HEX_DIGITS { // Four digits: pad them with "0000" and use the 8-digit code. v := u64(read4_to_u32(s, i)) | 0x3030_3030 << 32 if is_eight_hex_digits(v) { mantissa = mantissa<<16 | parse_eight_hex_digits(v) >> 16 nd += 4 exp -= 16 i += 4 saw_digits = true } } for i < n { d := hex_digit_table[s[i]] if d >= 16 { if s[i] != '_' { break } } else if mantissa == 0 && d == 0 { exp -= 4 // a leading zero of the fraction saw_digits = true } else if nd < MAX_HEX_DIGITS { mantissa = mantissa<<4 | u64(d) nd += 1 exp -= 4 saw_digits = true } else { trunc ||= d != 0 // dropped digit saw_digits = true } i += 1 } } if !saw_digits { return } // The binary exponent is required. The first byte after `p` and the sign must be a digit. if !(i < n && lower(s[i]) == 'p') { return } i += 1 exp_neg := false if i < n && (s[i] == '+' || s[i] == '-') { exp_neg = s[i] == '-' i += 1 } if i >= n || s[i] - '0' > 9 { return } x := 0 for i < n && (s[i] - '0' <= 9 || s[i] == '_') { if s[i] != '_' && x < 100_000 { // larger exponents overflow or underflow anyway x = x*10 + int(s[i] - '0') } i += 1 } exp += -x if exp_neg else x if mantissa == 0 { exp = 0 trunc = false } nr, ok = i, true return } /* Converts `mantissa * 2^exp` to the bits of the float type that `info` describes, rounded to nearest, ties to even. `trunc` means that the exact value is a little larger than `mantissa * 2^exp` (nonzero digits were dropped). **Returns** - float_bits: The bits of the float, with the sign. - ok: `false` if the value overflows. Then `float_bits` is infinity. */ hex_float_bits :: proc "contextless" (mantissa: u64, exp: int, neg, trunc: bool, info: ^Float_Info) -> (float_bits: u64, ok: bool) { M := int(info.mantbits) sign := u64(neg) << info.mantbits << info.expbits if mantissa == 0 { return sign, true } // Normalize: the top bit of m is set, so the value is in [2^(e+63), 2^(e+64)). lz := intrinsics.count_leading_zeros(mantissa) m := mantissa << lz e := exp - int(lz) biased := e + 63 - info.bias // the biased exponent of a normal result shift := 63 - M // keep M+1 bits for a normal result if biased < 1 { // Subnormal: keep fewer bits. shift += 1 - biased biased = 0 } // Round to nearest, ties to even. `trunc` is a sticky bit below all bits of m. kept, rest, half: u64 switch { case shift < 64: kept = m >> uint(shift) rest = m & (1< half || (rest == half && (trunc || kept & 1 == 1)) { kept += 1 } if biased == 0 { // Subnormal return sign | kept, true } if kept == 1 << uint(M+1) { kept >>= 1 biased += 1 } if biased >= 1< bool { ONES :: 0x0101_0101_0101_0101 HIGH :: 0x8080_8080_8080_8080 in_range :: #force_inline proc "contextless" (v: u64, $lo, $hi: u64) -> u64 { return (v + (0x80 - lo)*ONES) &~ (v + (0x7f - hi)*ONES) } digit := in_range(v, '0', '9') letter := in_range(v | 0x20*ONES, 'a', 'f') return (digit | letter) &~ v & HIGH == HIGH } parse_eight_hex_digits :: #force_inline proc "contextless" (v: u64) -> u64 { x := (v & 0x0f0f_0f0f_0f0f_0f0f) + ((v >> 6) & 0x0101_0101_0101_0101) * 9 x = ((x << 4) + (x >> 8)) & 0x00ff_00ff_00ff_00ff x = ((x << 8) + (x >> 16)) & 0x0000_ffff_0000_ffff return ((x << 16) + (x >> 32)) & 0xffff_ffff }