Merge pull request #2421 from jon-lipstate/strconv_docs

Code Docs for strconv
This commit is contained in:
Jeroen van Rijn
2023-04-06 15:38:32 +02:00
committed by GitHub
6 changed files with 869 additions and 177 deletions
+120 -4
View File
@@ -8,7 +8,17 @@ Decimal :: struct {
decimal_point: int, decimal_point: int,
neg, trunc: bool, neg, trunc: bool,
} }
/*
Sets a Decimal from a given string `s`. The string is expected to represent a float. Stores parsed number in the given Decimal structure.
If parsing fails, the Decimal will be left in an undefined state.
**Inputs**
- d: Pointer to a Decimal struct where the parsed result will be stored
- s: The input string representing the floating-point number
**Returns**
- ok: A boolean indicating whether the parsing was successful
*/
set :: proc(d: ^Decimal, s: string) -> (ok: bool) { set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
d^ = {} d^ = {}
@@ -91,7 +101,16 @@ set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
return i == len(s) return i == len(s)
} }
/*
Converts a Decimal to a string representation, using the provided buffer as storage.
**Inputs**
- buf: A byte slice buffer to hold the resulting string
- a: The struct to be converted to a string
**Returns**
- A string representation of the Decimal
*/
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
digit_zero :: proc(buf: []byte) -> int { digit_zero :: proc(buf: []byte) -> int {
for _, i in buf { for _, i in buf {
@@ -100,7 +119,6 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
return len(buf) return len(buf)
} }
n := 10 + a.count + abs(a.decimal_point) n := 10 + a.count + abs(a.decimal_point)
// TODO(bill): make this work with a buffer that's not big enough // TODO(bill): make this work with a buffer that's not big enough
@@ -129,8 +147,12 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
return string(b[0:w]) return string(b[0:w])
} }
/*
Trims trailing zeros in the given Decimal, updating the count and decimal_point values as needed.
// trim trailing zeros **Inputs**
- a: Pointer to the Decimal struct to be trimmed
*/
trim :: proc(a: ^Decimal) { trim :: proc(a: ^Decimal) {
for a.count > 0 && a.digits[a.count-1] == '0' { for a.count > 0 && a.digits[a.count-1] == '0' {
a.count -= 1 a.count -= 1
@@ -139,8 +161,15 @@ trim :: proc(a: ^Decimal) {
a.decimal_point = 0 a.decimal_point = 0
} }
} }
/*
Converts a given u64 integer `idx` to its Decimal representation in the provided Decimal struct.
**Used for internal Decimal Operations.**
**Inputs**
- a: Where the result will be stored
- idx: The value to be assigned to the Decimal
*/
assign :: proc(a: ^Decimal, idx: u64) { assign :: proc(a: ^Decimal, idx: u64) {
buf: [64]byte buf: [64]byte
n := 0 n := 0
@@ -160,9 +189,15 @@ assign :: proc(a: ^Decimal, idx: u64) {
a.decimal_point = a.count a.decimal_point = a.count
trim(a) trim(a)
} }
/*
Shifts the Decimal value to the right by k positions.
**Used for internal Decimal Operations.**
**Inputs**
- a: The Decimal struct to be shifted
- k: The number of positions to shift right
*/
shift_right :: proc(a: ^Decimal, k: uint) { shift_right :: proc(a: ^Decimal, k: uint) {
r := 0 // read index r := 0 // read index
w := 0 // write index w := 0 // write index
@@ -304,7 +339,15 @@ _shift_left_offsets := [?]struct{delta: int, cutoff: string}{
{18, "173472347597680709441192448139190673828125"}, {18, "173472347597680709441192448139190673828125"},
{19, "867361737988403547205962240695953369140625"}, {19, "867361737988403547205962240695953369140625"},
} }
/*
Shifts the decimal of the input value to the left by `k` places
WARNING: asserts `k < 61`
**Inputs**
- a: The Decimal to be modified
- k: The number of places to shift the decimal to the left
*/
shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check { shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
prefix_less :: #force_inline proc "contextless" (b: []byte, s: string) -> bool #no_bounds_check { prefix_less :: #force_inline proc "contextless" (b: []byte, s: string) -> bool #no_bounds_check {
for i in 0..<len(s) { for i in 0..<len(s) {
@@ -359,7 +402,13 @@ shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check {
a.count = clamp(a.count, 0, len(a.digits)) a.count = clamp(a.count, 0, len(a.digits))
trim(a) trim(a)
} }
/*
Shifts the decimal of the input value by the specified number of places
**Inputs**
- a: The Decimal to be modified
- i: The number of places to shift the decimal (positive for left shift, negative for right shift)
*/
shift :: proc(a: ^Decimal, i: int) { shift :: proc(a: ^Decimal, i: int) {
uint_size :: 8*size_of(uint) uint_size :: 8*size_of(uint)
max_shift :: uint_size-4 max_shift :: uint_size-4
@@ -383,7 +432,15 @@ shift :: proc(a: ^Decimal, i: int) {
shift_right(a, uint(-k)) shift_right(a, uint(-k))
} }
} }
/*
Determines if the Decimal can be rounded up at the given digit index
**Inputs**
- a: The Decimal to check
- nd: The digit index to consider for rounding up
**Returns** Boolean if can be rounded up at the given index (>=5)
*/
can_round_up :: proc(a: ^Decimal, nd: int) -> bool { can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
if nd < 0 || nd >= a.count { return false } if nd < 0 || nd >= a.count { return false }
if a.digits[nd] == '5' && nd+1 == a.count { if a.digits[nd] == '5' && nd+1 == a.count {
@@ -395,7 +452,13 @@ can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
return a.digits[nd] >= '5' return a.digits[nd] >= '5'
} }
/*
Rounds the Decimal at the given digit index
**Inputs**
- a: The Decimal to be modified
- nd: The digit index to round
*/
round :: proc(a: ^Decimal, nd: int) { round :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return } if nd < 0 || nd >= a.count { return }
if can_round_up(a, nd) { if can_round_up(a, nd) {
@@ -404,7 +467,13 @@ round :: proc(a: ^Decimal, nd: int) {
round_down(a, nd) round_down(a, nd)
} }
} }
/*
Rounds the Decimal up at the given digit index
**Inputs**
- a: The Decimal to be modified
- nd: The digit index to round up
*/
round_up :: proc(a: ^Decimal, nd: int) { round_up :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return } if nd < 0 || nd >= a.count { return }
@@ -421,15 +490,62 @@ round_up :: proc(a: ^Decimal, nd: int) {
a.count = 1 a.count = 1
a.decimal_point += 1 a.decimal_point += 1
} }
/*
Rounds down the decimal value to the specified number of decimal places
**Inputs**
- a: The Decimal value to be rounded down
- nd: The number of decimal places to round down to
Example:
import "core:fmt"
import "core:strconv"
strconv_round_down_example :: proc {
d: decimal.Decimal
str := [64]u8{}
ok := decimal.set(&d, "123.456")
decimal.round_down(&d, 5)
fmt.println(decimal.decimal_to_string(str[:], &d))
}
Output:
123.45
*/
round_down :: proc(a: ^Decimal, nd: int) { round_down :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return } if nd < 0 || nd >= a.count { return }
a.count = nd a.count = nd
trim(a) trim(a)
} }
/*
Extracts the rounded integer part of a decimal value
**Inputs**
- a: A pointer to the Decimal value to extract the rounded integer part from
// Extract integer part, rounded appropriately. There are no guarantees about overflow. WARNING: There are no guarantees about overflow.
**Returns** The rounded integer part of the input decimal value
Example:
import "core:fmt"
import "core:strconv"
strconv_rounded_integer_example :: proc {
d: decimal.Decimal
ok := decimal.set(&d, "123.456")
fmt.println(decimal.rounded_integer(&d))
}
Output:
123
*/
rounded_integer :: proc(a: ^Decimal) -> u64 { rounded_integer :: proc(a: ^Decimal) -> u64 {
if a.decimal_point > 20 { if a.decimal_point > 20 {
return 0xffff_ffff_ffff_ffff return 0xffff_ffff_ffff_ffff
+52
View File
@@ -20,7 +20,29 @@ _f16_info := Float_Info{10, 5, -15}
_f32_info := Float_Info{23, 8, -127} _f32_info := Float_Info{23, 8, -127}
_f64_info := Float_Info{52, 11, -1023} _f64_info := Float_Info{52, 11, -1023}
/*
Converts a floating-point number to a string with the specified format and precision.
**Inputs**
buf: A byte slice to store the resulting string
val: The floating-point value to be converted
fmt: The formatting byte, accepted values are 'e', 'E', 'f', 'F', 'g', 'G'
precision: The number of decimal places to round to
bit_size: The size of the floating-point number in bits, valid values are 16, 32, 64
Example:
buf: [32]byte
val := 3.141592
fmt := 'f'
precision := 2
bit_size := 64
result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14"
**Returns**
- A byte slice containing the formatted string
*/
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte { generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
bits: u64 bits: u64
flt: ^Float_Info flt: ^Float_Info
@@ -95,8 +117,20 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
return format_digits(buf, shortest, neg, digs, prec, fmt) return format_digits(buf, shortest, neg, digs, prec, fmt)
} }
/*
Converts a decimal floating-point number into a byte buffer with the given format
**Inputs**
- buf: The byte buffer to store the formatted number
- shortest: If true, generates the shortest representation of the number
- neg: If true, the number is negative
- digs: The decimal number to be formatted
- precision: The number of digits after the decimal point
- fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G')
**Returns**
- A byte slice containing the formatted decimal floating-point number
*/
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte { format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
Buffer :: struct { Buffer :: struct {
b: []byte, b: []byte,
@@ -217,7 +251,15 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
} }
/*
Rounds the given decimal number to its shortest representation, considering the provided floating-point format
**Inputs**
- d: The decimal number to round
- mant: The mantissa of the floating-point number
- exp: The exponent of the floating-point number
- flt: Pointer to the Float_Info structure containing information about the floating-point format
*/
round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Info) { round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Info) {
if mant == 0 { // If mantissa is zero, the number is zero if mant == 0 { // If mantissa is zero, the number is zero
d.count = 0 d.count = 0
@@ -284,7 +326,17 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
} }
} }
/*
Converts a decimal number to its floating-point representation with the given format and returns the resulting bits
**Inputs**
- d: Pointer to the decimal number to convert
- info: Pointer to the Float_Info structure containing information about the floating-point format
**Returns**
- b: The bits representing the floating-point number
- overflow: A boolean indicating whether an overflow occurred during conversion
*/
@(private) @(private)
decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) { decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (bits: u64) { end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (bits: u64) {
+49 -1
View File
@@ -9,7 +9,18 @@ Int_Flags :: bit_set[Int_Flag]
MAX_BASE :: 32 MAX_BASE :: 32
digits := "0123456789abcdefghijklmnopqrstuvwxyz" digits := "0123456789abcdefghijklmnopqrstuvwxyz"
/*
Determines whether the given unsigned 64-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
**Inputs**
- x: The unsigned 64-bit integer to check for negativity
- is_signed: A boolean indicating if the input should be treated as a signed integer
- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64)
**Returns**
- u: The absolute value of the input integer
- neg: A boolean indicating whether the input integer is negative
*/
is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) { is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) {
u = x u = x
if is_signed { if is_signed {
@@ -36,7 +47,21 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64,
} }
return return
} }
/*
Appends the string representation of an integer to a buffer with specified base, flags, and digit set.
**Inputs**
- buf: The buffer to append the integer representation to
- x: The integer value to convert
- base: The base for the integer representation (2 <= base <= MAX_BASE)
- is_signed: A boolean indicating if the input should be treated as a signed integer
- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64)
- digits: The digit set used for the integer representation
- flags: The Int_Flags bit set to control integer formatting
**Returns**
- The string containing the integer representation appended to the buffer
*/
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE { if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits") panic("strconv: illegal base passed to append_bits")
@@ -78,7 +103,18 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
copy(buf, out) copy(buf, out)
return string(buf[0:len(out)]) return string(buf[0:len(out)])
} }
/*
Determines whether the given unsigned 128-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
**Inputs**
- x: The unsigned 128-bit integer to check for negativity
- is_signed: A boolean indicating if the input should be treated as a signed integer
- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128)
**Returns**
- u: The absolute value of the input integer
- neg: A boolean indicating whether the input integer is negative
*/
is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) { is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) {
u = x u = x
if is_signed { if is_signed {
@@ -109,9 +145,21 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u:
} }
return return
} }
/*
Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
// import "core:runtime" **Inputs**
- buf: The buffer to append the integer representation to
- x: The 128-bit integer value to convert
- base: The base for the integer representation (2 <= base <= MAX_BASE)
- is_signed: A boolean indicating if the input should be treated as a signed integer
- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128)
- digits: The digit set used for the integer representation
- flags: The Int_Flags bit set to control integer formatting
**Returns**
- The string containing the integer representation appended to the buffer
*/
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE { if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits") panic("strconv: illegal base passed to append_bits")
+647 -169
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -1081,7 +1081,6 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
} }
/* /*
Splits the input string by the separator string in an iterator fashion. Splits the input string by the separator string in an iterator fashion.
Destructively consumes the original string until the end.
Inputs: Inputs:
- s: Pointer to the input string, which is modified during the search. - s: Pointer to the input string, which is modified during the search.
@@ -1116,7 +1115,6 @@ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
} }
/* /*
Splits the input string after every separator string in an iterator fashion. Splits the input string after every separator string in an iterator fashion.
Destructively consumes the original string until the end.
Inputs: Inputs:
- s: Pointer to the input string, which is modified during the search. - s: Pointer to the input string, which is modified during the search.
+1 -1
View File
@@ -409,7 +409,7 @@ foreign kernel32 {
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setfilecompletionnotificationmodes) // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setfilecompletionnotificationmodes)
SetFileCompletionNotificationModes :: proc(FileHandle: HANDLE, Flags: u8) -> BOOL --- SetFileCompletionNotificationModes :: proc(FileHandle: HANDLE, Flags: u8) -> BOOL ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-createiocompletionport) // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-createiocompletionport)
CreateIoCompletionPort :: proc(FileHandle: HANDLE, ExistingCompletionPort: HANDLE, CompletionKey: uintptr, NumberOfConcurrentThreads: DWORD) -> HANDLE --- CreateIoCompletionPort :: proc(FileHandle: HANDLE, ExistingCompletionPort: HANDLE, CompletionKey: ^uintptr, NumberOfConcurrentThreads: DWORD) -> HANDLE ---
//[MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus) //[MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus)
GetQueuedCompletionStatus :: proc(CompletionPort: HANDLE, lpNumberOfBytesTransferred: ^DWORD, lpCompletionKey: uintptr, lpOverlapped: ^^OVERLAPPED, dwMilliseconds: DWORD) -> BOOL --- GetQueuedCompletionStatus :: proc(CompletionPort: HANDLE, lpNumberOfBytesTransferred: ^DWORD, lpCompletionKey: uintptr, lpOverlapped: ^^OVERLAPPED, dwMilliseconds: DWORD) -> BOOL ---
// [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatusex) // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatusex)