mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
340 lines
6.4 KiB
V
340 lines
6.4 KiB
V
module strconv
|
||
|
||
// import math
|
||
|
||
/*
|
||
f32/f64 to string utilities
|
||
|
||
Copyright (c) 2019-2023 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 the f32/f64 to string utilities functions
|
||
|
||
These functions are based on the work of:
|
||
Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN
|
||
Conference on Programming Language Design and ImplementationJune 2018
|
||
Pages 270–282 https://doi.org/10.1145/3192366.3192369
|
||
|
||
inspired by the Go version here:
|
||
https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea
|
||
*/
|
||
|
||
/*
|
||
f64 to string with string format
|
||
*/
|
||
|
||
// TODO: Investigate precision issues
|
||
// f32_to_str_l returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot.
|
||
//
|
||
// Example: assert strconv.f32_to_str_l(34.1234567) == '34.12346'
|
||
[manualfree]
|
||
pub fn f32_to_str_l(f f32) string {
|
||
s := f32_to_str(f, 6)
|
||
res := fxx_to_str_l_parse(s)
|
||
unsafe { s.free() }
|
||
return res
|
||
}
|
||
|
||
// f32_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 6 digits after the dot.
|
||
// If the decimal digits after the dot are zero, a '.0' is appended for clarity.
|
||
//
|
||
// Example: assert strconv.f32_to_str_l_with_dot(34.) == '34.0'
|
||
[manualfree]
|
||
pub fn f32_to_str_l_with_dot(f f32) string {
|
||
s := f32_to_str(f, 6)
|
||
res := fxx_to_str_l_parse_with_dot(s)
|
||
unsafe { s.free() }
|
||
return res
|
||
}
|
||
|
||
// f64_to_str_l returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot.
|
||
//
|
||
// Example: assert strconv.f64_to_str_l(123.1234567891011121) == '123.12345678910111'
|
||
[manualfree]
|
||
pub fn f64_to_str_l(f f64) string {
|
||
s := f64_to_str(f, 18)
|
||
res := fxx_to_str_l_parse(s)
|
||
unsafe { s.free() }
|
||
return res
|
||
}
|
||
|
||
// f64_to_str_l_with_dot returns `f` as a `string` in decimal notation with a maximum of 18 digits after the dot.
|
||
// If the decimal digits after the dot are zero, a '.0' is appended for clarity.
|
||
//
|
||
// Example: assert strconv.f64_to_str_l_with_dot (34.) == '34.0'
|
||
[manualfree]
|
||
pub fn f64_to_str_l_with_dot(f f64) string {
|
||
s := f64_to_str(f, 18)
|
||
res := fxx_to_str_l_parse_with_dot(s)
|
||
unsafe { s.free() }
|
||
return res
|
||
}
|
||
|
||
// fxx_to_str_l_parse returns a `string` in decimal notation converted from a
|
||
// floating-point `string` in scientific notation.
|
||
//
|
||
// Example: assert strconv.fxx_to_str_l_parse('34.22e+00') == '34.22'
|
||
[direct_array_access; manualfree]
|
||
pub fn fxx_to_str_l_parse(s string) string {
|
||
// check for +inf -inf Nan
|
||
if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
|
||
return s.clone()
|
||
}
|
||
|
||
m_sgn_flag := false
|
||
mut sgn := 1
|
||
mut b := [26]u8{}
|
||
mut d_pos := 1
|
||
mut i := 0
|
||
mut i1 := 0
|
||
mut exp := 0
|
||
mut exp_sgn := 1
|
||
|
||
// get sign and decimal parts
|
||
for c in s {
|
||
if c == `-` {
|
||
sgn = -1
|
||
i++
|
||
} else if c == `+` {
|
||
sgn = 1
|
||
i++
|
||
} else if c >= `0` && c <= `9` {
|
||
b[i1] = c
|
||
i1++
|
||
i++
|
||
} else if c == `.` {
|
||
if sgn > 0 {
|
||
d_pos = i
|
||
} else {
|
||
d_pos = i - 1
|
||
}
|
||
i++
|
||
} else if c == `e` {
|
||
i++
|
||
break
|
||
} else {
|
||
return 'Float conversion error!!'
|
||
}
|
||
}
|
||
b[i1] = 0
|
||
|
||
// get exponent
|
||
if s[i] == `-` {
|
||
exp_sgn = -1
|
||
i++
|
||
} else if s[i] == `+` {
|
||
exp_sgn = 1
|
||
i++
|
||
}
|
||
|
||
mut c := i
|
||
for c < s.len {
|
||
exp = exp * 10 + int(s[c] - `0`)
|
||
c++
|
||
}
|
||
|
||
// allocate exp+32 chars for the return string
|
||
mut res := []u8{len: exp + 32, init: 0}
|
||
mut r_i := 0 // result string buffer index
|
||
|
||
// println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
|
||
|
||
if sgn == 1 {
|
||
if m_sgn_flag {
|
||
res[r_i] = `+`
|
||
r_i++
|
||
}
|
||
} else {
|
||
res[r_i] = `-`
|
||
r_i++
|
||
}
|
||
|
||
i = 0
|
||
if exp_sgn >= 0 {
|
||
for b[i] != 0 {
|
||
res[r_i] = b[i]
|
||
r_i++
|
||
i++
|
||
if i >= d_pos && exp >= 0 {
|
||
if exp == 0 {
|
||
res[r_i] = `.`
|
||
r_i++
|
||
}
|
||
exp--
|
||
}
|
||
}
|
||
for exp >= 0 {
|
||
res[r_i] = `0`
|
||
r_i++
|
||
exp--
|
||
}
|
||
} else {
|
||
mut dot_p := true
|
||
for exp > 0 {
|
||
res[r_i] = `0`
|
||
r_i++
|
||
exp--
|
||
if dot_p {
|
||
res[r_i] = `.`
|
||
r_i++
|
||
dot_p = false
|
||
}
|
||
}
|
||
for b[i] != 0 {
|
||
res[r_i] = b[i]
|
||
r_i++
|
||
i++
|
||
}
|
||
}
|
||
|
||
// Add a zero after the dot from the numbers like 2.
|
||
if r_i > 1 && res[r_i - 1] == `.` {
|
||
res[r_i] = `0`
|
||
r_i++
|
||
} else if `.` !in res {
|
||
// If there is no dot, add it with a zero
|
||
res[r_i] = `.`
|
||
r_i++
|
||
res[r_i] = `0`
|
||
r_i++
|
||
}
|
||
|
||
res[r_i] = 0
|
||
return unsafe { tos(res.data, r_i) }
|
||
}
|
||
|
||
// fxx_to_str_l_parse_with_dot returns a `string` in decimal notation converted from a
|
||
// floating-point `string` in scientific notation.
|
||
// If the decimal digits after the dot are zero, a '.0' is appended for clarity.
|
||
//
|
||
// Example: assert strconv.fxx_to_str_l_parse_with_dot ('34.e+01') == '340.0'
|
||
[direct_array_access; manualfree]
|
||
pub fn fxx_to_str_l_parse_with_dot(s string) string {
|
||
// check for +inf -inf Nan
|
||
if s.len > 2 && (s[0] == `n` || s[1] == `i`) {
|
||
return s.clone()
|
||
}
|
||
|
||
m_sgn_flag := false
|
||
mut sgn := 1
|
||
mut b := [26]u8{}
|
||
mut d_pos := 1
|
||
mut i := 0
|
||
mut i1 := 0
|
||
mut exp := 0
|
||
mut exp_sgn := 1
|
||
|
||
// get sign and decimal parts
|
||
for c in s {
|
||
if c == `-` {
|
||
sgn = -1
|
||
i++
|
||
} else if c == `+` {
|
||
sgn = 1
|
||
i++
|
||
} else if c >= `0` && c <= `9` {
|
||
b[i1] = c
|
||
i1++
|
||
i++
|
||
} else if c == `.` {
|
||
if sgn > 0 {
|
||
d_pos = i
|
||
} else {
|
||
d_pos = i - 1
|
||
}
|
||
i++
|
||
} else if c == `e` {
|
||
i++
|
||
break
|
||
} else {
|
||
return 'Float conversion error!!'
|
||
}
|
||
}
|
||
b[i1] = 0
|
||
|
||
// get exponent
|
||
if s[i] == `-` {
|
||
exp_sgn = -1
|
||
i++
|
||
} else if s[i] == `+` {
|
||
exp_sgn = 1
|
||
i++
|
||
}
|
||
|
||
mut c := i
|
||
for c < s.len {
|
||
exp = exp * 10 + int(s[c] - `0`)
|
||
c++
|
||
}
|
||
|
||
// allocate exp+32 chars for the return string
|
||
mut res := []u8{len: exp + 32, init: 0}
|
||
mut r_i := 0 // result string buffer index
|
||
|
||
// println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}")
|
||
|
||
if sgn == 1 {
|
||
if m_sgn_flag {
|
||
res[r_i] = `+`
|
||
r_i++
|
||
}
|
||
} else {
|
||
res[r_i] = `-`
|
||
r_i++
|
||
}
|
||
|
||
i = 0
|
||
if exp_sgn >= 0 {
|
||
for b[i] != 0 {
|
||
res[r_i] = b[i]
|
||
r_i++
|
||
i++
|
||
if i >= d_pos && exp >= 0 {
|
||
if exp == 0 {
|
||
res[r_i] = `.`
|
||
r_i++
|
||
}
|
||
exp--
|
||
}
|
||
}
|
||
for exp >= 0 {
|
||
res[r_i] = `0`
|
||
r_i++
|
||
exp--
|
||
}
|
||
} else {
|
||
mut dot_p := true
|
||
for exp > 0 {
|
||
res[r_i] = `0`
|
||
r_i++
|
||
exp--
|
||
if dot_p {
|
||
res[r_i] = `.`
|
||
r_i++
|
||
dot_p = false
|
||
}
|
||
}
|
||
for b[i] != 0 {
|
||
res[r_i] = b[i]
|
||
r_i++
|
||
i++
|
||
}
|
||
}
|
||
|
||
// Add a zero after the dot from the numbers like 2.
|
||
if r_i > 1 && res[r_i - 1] == `.` {
|
||
res[r_i] = `0`
|
||
r_i++
|
||
} else if `.` !in res {
|
||
// If there is no dot, add it with a zero
|
||
res[r_i] = `.`
|
||
r_i++
|
||
res[r_i] = `0`
|
||
r_i++
|
||
}
|
||
|
||
res[r_i] = 0
|
||
return unsafe { tos(res.data, r_i) }
|
||
}
|