123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427 |
- package strconv
- import "decimal"
- Decimal_Slice :: struct {
- digits: []byte,
- count: int,
- decimal_point: int,
- neg: bool,
- }
- Float_Info :: struct {
- mantbits: uint,
- expbits: uint,
- bias: int,
- }
- _f16_info := Float_Info{10, 5, -15}
- _f32_info := Float_Info{23, 8, -127}
- _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 {
- bits: u64
- flt: ^Float_Info
- switch bit_size {
- case 16:
- bits = u64(transmute(u16)f16(val))
- flt = &_f16_info
- case 32:
- bits = u64(transmute(u32)f32(val))
- flt = &_f32_info
- case 64:
- bits = transmute(u64)val
- flt = &_f64_info
- case:
- panic("strconv: invalid bit_size")
- }
- neg := bits>>(flt.expbits+flt.mantbits) != 0
- exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
- mant := bits & (u64(1) << flt.mantbits - 1)
- switch exp {
- case 1<<flt.expbits - 1:
- s: string
- if mant != 0 {
- s = "NaN"
- } else if neg {
- s = "-Inf"
- } else {
- s = "+Inf"
- }
- n := copy(buf, s)
- return buf[:n]
- case 0: // denormalized
- exp += 1
- case:
- mant |= u64(1) << flt.mantbits
- }
- exp += flt.bias
- d_: decimal.Decimal
- d := &d_
- decimal.assign(d, mant)
- decimal.shift(d, exp - int(flt.mantbits))
- digs: Decimal_Slice
- prec := precision
- shortest := prec < 0
- if shortest {
- round_shortest(d, mant, exp, flt)
- digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point}
- switch fmt {
- case 'e', 'E': prec = digs.count-1
- case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0)
- case 'g', 'G': prec = digs.count
- }
- } else {
- switch fmt {
- case 'e', 'E':
- prec += 1
- decimal.round(d, prec)
- case 'f', 'F':
- decimal.round(d, d.decimal_point+prec)
- case 'g', 'G':
- if prec == 0 {
- prec = 1
- }
- decimal.round(d, prec)
- }
- digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point}
- }
- 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 {
- Buffer :: struct {
- b: []byte,
- n: int,
- }
- to_bytes :: proc(b: Buffer) -> []byte {
- return b.b[:b.n]
- }
- add_bytes :: proc(buf: ^Buffer, bytes: ..byte) {
- buf.n += copy(buf.b[buf.n:], bytes)
- }
- b := Buffer{b = buf}
- prec := precision
- switch fmt {
- case 'f', 'F':
- add_bytes(&b, '-' if neg else '+')
- // integer, padded with zeros when needed
- if digs.decimal_point > 0 {
- m := min(digs.count, digs.decimal_point)
- add_bytes(&b, ..digs.digits[0:m])
- for ; m < digs.decimal_point; m += 1 {
- add_bytes(&b, '0')
- }
- } else {
- add_bytes(&b, '0')
- }
- // fractional part
- if prec > 0 {
- add_bytes(&b, '.')
- for i in 0..<prec {
- c: byte = '0'
- if j := digs.decimal_point + i; 0 <= j && j < digs.count {
- c = digs.digits[j]
- }
- add_bytes(&b, c)
- }
- }
- return to_bytes(b)
- case 'e', 'E':
- add_bytes(&b, '-' if neg else '+')
- ch := byte('0')
- if digs.count != 0 {
- ch = digs.digits[0]
- }
- add_bytes(&b, ch)
- if prec > 0 {
- add_bytes(&b, '.')
- i := 1
- m := min(digs.count, prec+1)
- if i < m {
- add_bytes(&b, ..digs.digits[i:m])
- i = m
- }
- for ; i <= prec; i += 1 {
- add_bytes(&b, '0')
- }
- }
- add_bytes(&b, fmt)
- exp := digs.decimal_point-1
- if digs.count == 0 {
- // Zero has exponent of 0
- exp = 0
- }
- ch = '+'
- if exp < 0 {
- ch = '-'
- exp = -exp
- }
- add_bytes(&b, ch)
- switch {
- case exp < 10: add_bytes(&b, '0', byte(exp)+'0') // add prefix 0
- case exp < 100: add_bytes(&b, byte(exp/10)+'0', byte(exp%10)+'0')
- case: add_bytes(&b, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0')
- }
- return to_bytes(b)
- case 'g', 'G':
- eprec := prec
- if eprec > digs.count && digs.count >= digs.decimal_point {
- eprec = digs.count
- }
- if shortest {
- eprec = 6
- }
- exp := digs.decimal_point - 1
- if exp < -4 || exp >= eprec {
- if prec > digs.count {
- prec = digs.count
- }
- return format_digits(buf, shortest, neg, digs, prec-1, fmt+'e'-'g') // keep the same case
- }
- if prec > digs.decimal_point {
- prec = digs.count
- }
- return format_digits(buf, shortest, neg, digs, max(prec-digs.decimal_point, 0), 'f')
- case:
- add_bytes(&b, '%', fmt)
- return to_bytes(b)
- }
- }
- /*
- 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) {
- if mant == 0 { // If mantissa is zero, the number is zero
- d.count = 0
- return
- }
- /*
- 10^(dp-nd) > 2^(exp-mantbits)
- log2(10) * (dp-nd) > exp-mantbits
- log(2) >~ 0.332
- 332*(dp-nd) >= 100*(exp-mantbits)
- */
- minexp := flt.bias+1
- if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
- // Number is already its shortest
- return
- }
- upper_: decimal.Decimal; upper := &upper_
- decimal.assign(upper, 2*mant - 1)
- decimal.shift(upper, exp - int(flt.mantbits) - 1)
- mantlo: u64
- explo: int
- if mant > 1<<flt.mantbits || exp == minexp {
- mantlo = mant-1
- explo = exp
- } else {
- mantlo = 2*mant - 1
- explo = exp-1
- }
- lower_: decimal.Decimal; lower := &lower_
- decimal.assign(lower, 2*mantlo + 1)
- decimal.shift(lower, explo - int(flt.mantbits) - 1)
- inclusive := mant%2 == 0
- for i in 0..<d.count {
- l: byte = '0' // lower digit
- if i < lower.count {
- l = lower.digits[i]
- }
- m := d.digits[i] // middle digit
- u: byte = '0' // upper digit
- if i < upper.count {
- u = upper.digits[i]
- }
- ok_round_down := l != m || inclusive && i+1 == lower.count
- ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count)
- if ok_round_down && ok_round_up {
- decimal.round(d, i+1)
- return
- }
- if ok_round_down {
- decimal.round_down(d, i+1)
- return
- }
- if ok_round_up {
- decimal.round_up(d, i+1)
- return
- }
- }
- }
- /*
- 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)
- 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) {
- bits = mant & (u64(1)<<info.mantbits - 1)
- bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
- if d.neg {
- bits |= 1<< info.mantbits << info.expbits
- }
- return
- }
- set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
- mant^ = 0
- exp^ = 1<<info.expbits - 1 + info.bias
- return true
- }
- mant: u64
- exp: int
- if d.count == 0 {
- mant = 0
- exp = info.bias
- b = end(d, mant, exp, info)
- return
- }
- if d.decimal_point > 310 {
- set_overflow(&mant, &exp, info)
- b = end(d, mant, exp, info)
- return
- } else if d.decimal_point < -330 {
- mant = 0
- exp = info.bias
- b = end(d, mant, exp, info)
- return
- }
- @static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
- exp = 0
- for d.decimal_point > 0 {
- n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
- decimal.shift(d, -n)
- exp += n
- }
- for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
- n := 27 if -d.decimal_point >= len(power_table) else power_table[-d.decimal_point]
- decimal.shift(d, n)
- exp -= n
- }
- // go from [0.5, 1) to [1, 2)
- exp -= 1
- if exp < info.bias + 1 {
- n := info.bias + 1 - exp
- decimal.shift(d, n)
- exp += n
- }
- if (exp-info.bias) >= (1<<info.expbits - 1) {
- set_overflow(&mant, &exp, info)
- b = end(d, mant, exp, info)
- return
- }
- decimal.shift(d, int(1 + info.mantbits))
- mant = decimal.rounded_integer(d)
- if mant == 2<<info.mantbits {
- mant >>= 1
- exp += 1
- if (exp-info.bias) >= (1<<info.expbits - 1) {
- set_overflow(&mant, &exp, info)
- b = end(d, mant, exp, info)
- return
- }
- }
- if mant & (1<<info.mantbits) == 0 {
- exp = info.bias
- }
- b = end(d, mant, exp, info)
- return
- }
|