strconv: cleanup atof.c.v - use a ParserState enum, clarify comments

pull/13967/head
Delyan Angelov 2022-04-11 12:01:47 +03:00
parent e4dfffd70b
commit 843ce43077
No known key found for this signature in database
GPG Key ID: 66886C0F12D595ED
1 changed files with 75 additions and 93 deletions

View File

@ -1,26 +1,47 @@
module strconv
/*
atof util
// Copyright (c) 2019-2022 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 converting a string to 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.
Copyright (c) 2019-2022 Dario Deledda. All rights reserved.
Use of this source code is governed by an MIT license
that can be found in the LICENSE file.
// f32 constants
pub const (
single_plus_zero = u32(0x0000_0000)
single_minus_zero = u32(0x8000_0000)
single_plus_infinity = u32(0x7F80_0000)
single_minus_infinity = u32(0xFF80_0000)
)
This file contains utilities for convert a string in a f64 variable
IEEE 754 standard is used
// f64 constants
pub const (
digits = 18
double_plus_zero = u64(0x0000000000000000)
double_minus_zero = u64(0x8000000000000000)
double_plus_infinity = u64(0x7FF0000000000000)
double_minus_infinity = u64(0xFFF0000000000000)
)
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
*/
// char constants
pub const (
c_dpoint = `.`
c_plus = `+`
c_minus = `-`
c_zero = `0`
c_nine = `9`
c_ten = u32(10)
)
// right logical shift 96 bit
fn lsr96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) {
@ -78,48 +99,7 @@ fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) {
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
parser_invalid_number = 5 // invalid number, used for '#@%^' for example
//
// 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 functions
// 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
}
@ -132,14 +112,21 @@ fn is_exp(x byte) bool {
return (x == `E` || x == `e`) == true
}
/*
String parser
NOTE: #TOFIX need one char after the last char of the number
*/
// Possible parser return values.
enum ParserState {
ok // parser finished OK
pzero // no digits or number is smaller than +-2^-1022
mzero // number is negative, module smaller
pinf // number is higher than +HUGE_VAL
minf // number is lower than -HUGE_VAL
invalid_number // invalid number, used for '#@%^' for example
}
fn parser(s string) (int, PrepNumber) {
// parser tries to parse the given string into a number
// NOTE: #TOFIX need one char after the last char of the number
fn parser(s string) (ParserState, PrepNumber) {
mut digx := 0
mut result := strconv.parser_ok
mut result := ParserState.ok
mut expneg := false
mut expexp := 0
mut i := 0
@ -216,45 +203,45 @@ fn parser(s string) (int, PrepNumber) {
pn.exponent += expexp
if pn.mantissa == 0 {
if pn.negative {
result = strconv.parser_mzero
result = .mzero
} else {
result = strconv.parser_pzero
result = .pzero
}
} else if pn.exponent > 309 {
if pn.negative {
result = strconv.parser_minf
result = .minf
} else {
result = strconv.parser_pinf
result = .pinf
}
} else if pn.exponent < -328 {
if pn.negative {
result = strconv.parser_mzero
result = .mzero
} else {
result = strconv.parser_pzero
result = .pzero
}
}
if i == 0 && s.len > 0 {
return strconv.parser_invalid_number, pn
return ParserState.invalid_number, pn
}
return result, pn
}
/*
Converter to the bit form of the f64 number
*/
// converter return a u64 with the bit image of the f64 number
// converter returns 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
// s0,s1,s2 are the parts of a 96-bit precision integer
mut s2 := u32(0)
mut s1 := u32(0)
mut s0 := u32(0)
mut q2 := u32(0) // 96-bit precision integer
// q0,q1,q2 are the parts of a 96-bit precision integer
mut q2 := u32(0)
mut q1 := u32(0)
mut q0 := u32(0)
mut r2 := u32(0) // 96-bit precision integer
// r0,r1,r2 are the parts of a 96-bit precision integer
mut r2 := u32(0)
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
@ -404,35 +391,30 @@ fn converter(mut pn PrepNumber) u64 {
return result
}
// Public functions
// atof64 return a f64 from a string doing a parsing operation
// atof64 parses the string `s`, and if possible, converts it into a f64 number
pub fn atof64(s string) ?f64 {
if s.len == 0 {
return error('expected a number found an empty string')
}
mut pn := PrepNumber{}
mut res_parsing := 0
mut res := Float64u{}
res_parsing, pn = parser(s)
mut res_parsing, mut pn := parser(s)
match res_parsing {
strconv.parser_ok {
.ok {
res.u = converter(mut pn)
}
strconv.parser_pzero {
.pzero {
res.u = strconv.double_plus_zero
}
strconv.parser_mzero {
.mzero {
res.u = strconv.double_minus_zero
}
strconv.parser_pinf {
.pinf {
res.u = strconv.double_plus_infinity
}
strconv.parser_minf {
.minf {
res.u = strconv.double_minus_infinity
}
else {
.invalid_number {
return error('not a number')
}
}