1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

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

This commit is contained in:
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

View File

@ -1,26 +1,47 @@
module strconv module strconv
/* // Copyright (c) 2019-2022 Dario Deledda. All rights reserved.
atof util // 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. // f32 constants
Use of this source code is governed by an MIT license pub const (
that can be found in the LICENSE file. 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 // f64 constants
IEEE 754 standard is used 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: // char constants
- limited to 18 significant digits pub const (
c_dpoint = `.`
The code is inspired by: c_plus = `+`
Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl c_minus = `-`
URL: http://krashan.ppa.pl/articles/stringtofloat/ c_zero = `0`
Original license: MIT c_nine = `9`
c_ten = u32(10)
96 bit operation utilities )
Note: when u128 will be available these function can be refactored
*/
// right logical shift 96 bit // right logical shift 96 bit
fn lsr96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { 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 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 // Utility functions
// NOTE: Modify these if working with non-ASCII encoding
fn is_digit(x byte) bool { fn is_digit(x byte) bool {
return (x >= strconv.c_zero && x <= strconv.c_nine) == true 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 return (x == `E` || x == `e`) == true
} }
/* // Possible parser return values.
String parser enum ParserState {
NOTE: #TOFIX need one char after the last char of the number 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 digx := 0
mut result := strconv.parser_ok mut result := ParserState.ok
mut expneg := false mut expneg := false
mut expexp := 0 mut expexp := 0
mut i := 0 mut i := 0
@ -216,45 +203,45 @@ fn parser(s string) (int, PrepNumber) {
pn.exponent += expexp pn.exponent += expexp
if pn.mantissa == 0 { if pn.mantissa == 0 {
if pn.negative { if pn.negative {
result = strconv.parser_mzero result = .mzero
} else { } else {
result = strconv.parser_pzero result = .pzero
} }
} else if pn.exponent > 309 { } else if pn.exponent > 309 {
if pn.negative { if pn.negative {
result = strconv.parser_minf result = .minf
} else { } else {
result = strconv.parser_pinf result = .pinf
} }
} else if pn.exponent < -328 { } else if pn.exponent < -328 {
if pn.negative { if pn.negative {
result = strconv.parser_mzero result = .mzero
} else { } else {
result = strconv.parser_pzero result = .pzero
} }
} }
if i == 0 && s.len > 0 { if i == 0 && s.len > 0 {
return strconv.parser_invalid_number, pn return ParserState.invalid_number, pn
} }
return result, pn return result, pn
} }
/* // converter returns a u64 with the bit image of the f64 number
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 { fn converter(mut pn PrepNumber) u64 {
mut binexp := 92 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 s1 := u32(0)
mut s0 := 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 q1 := u32(0)
mut q0 := 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 r1 := u32(0)
mut r0 := u32(0) mut r0 := u32(0)
//
mask28 := u32(u64(0xF) << 28) mask28 := u32(u64(0xF) << 28)
mut result := u64(0) mut result := u64(0)
// working on 3 u32 to have 96 bit precision // working on 3 u32 to have 96 bit precision
@ -404,35 +391,30 @@ fn converter(mut pn PrepNumber) u64 {
return result return result
} }
// Public functions // atof64 parses the string `s`, and if possible, converts it into a f64 number
// atof64 return a f64 from a string doing a parsing operation
pub fn atof64(s string) ?f64 { pub fn atof64(s string) ?f64 {
if s.len == 0 { if s.len == 0 {
return error('expected a number found an empty string') return error('expected a number found an empty string')
} }
mut pn := PrepNumber{}
mut res_parsing := 0
mut res := Float64u{} mut res := Float64u{}
mut res_parsing, mut pn := parser(s)
res_parsing, pn = parser(s)
match res_parsing { match res_parsing {
strconv.parser_ok { .ok {
res.u = converter(mut pn) res.u = converter(mut pn)
} }
strconv.parser_pzero { .pzero {
res.u = strconv.double_plus_zero res.u = strconv.double_plus_zero
} }
strconv.parser_mzero { .mzero {
res.u = strconv.double_minus_zero res.u = strconv.double_minus_zero
} }
strconv.parser_pinf { .pinf {
res.u = strconv.double_plus_infinity res.u = strconv.double_plus_infinity
} }
strconv.parser_minf { .minf {
res.u = strconv.double_minus_infinity res.u = strconv.double_minus_infinity
} }
else { .invalid_number {
return error('not a number') return error('not a number')
} }
} }