From 332e59835725b931b1b04e741a4f12bfe9a29748 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 15 Mar 2019 23:13:06 +0000 Subject: [PATCH] %e and %g support in fmt.printf --- core/decimal/decimal.odin | 25 +++++++---- core/fmt/fmt.odin | 37 ++++++++++++++-- core/strconv/strconv.odin | 90 ++++++++++++++++++++++++++++++++------- 3 files changed, 126 insertions(+), 26 deletions(-) diff --git a/core/decimal/decimal.odin b/core/decimal/decimal.odin index 22498cb8a..2acf7a03d 100644 --- a/core/decimal/decimal.odin +++ b/core/decimal/decimal.odin @@ -130,10 +130,15 @@ shift_right :: proc(a: ^Decimal, k: uint) { } shift_left :: proc(a: ^Decimal, k: uint) { - delta := int(k/4); + // NOTE(bill): used to determine buffer size required for the decimal from the binary shift + // 'k' means `1<= 0; r -= 1 { @@ -141,7 +146,7 @@ shift_left :: proc(a: ^Decimal, k: uint) { quo := n/10; rem := n - 10*quo; w -= 1; - if w < len(a.digits) { + if w < d { a.digits[w] = byte('0' + rem); } else if rem != 0 { a.trunc = true; @@ -153,7 +158,7 @@ shift_left :: proc(a: ^Decimal, k: uint) { quo := n/10; rem := n - 10*quo; w -= 1; - if 0 <= w && w < len(a.digits) { + if w < d { a.digits[w] = byte('0' + rem); } else if rem != 0 { a.trunc = true; @@ -161,9 +166,12 @@ shift_left :: proc(a: ^Decimal, k: uint) { n = quo; } - a.count += delta; - a.count = min(a.count, len(a.digits)); - a.decimal_point += delta; + // NOTE(bill): Remove unused buffer size + assert(w >= 0); + capacity -= w; + + a.count = min(a.count+capacity, d); + a.decimal_point += capacity; trim(a); } @@ -253,3 +261,4 @@ rounded_integer :: proc(a: ^Decimal) -> u64 { } return n; } + diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 344f61e80..db825663d 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -551,9 +551,6 @@ _pad :: proc(fi: ^Info, s: string) { fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { switch verb { - // case 'e', 'E', 'f', 'F', 'g', 'G', 'v': - // case 'f', 'F', 'v': - case 'f', 'F', 'v': prec: int = 3; if fi.prec_set do prec = fi.prec; @@ -588,6 +585,40 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) { _pad(fi, str[1:]); } + case 'e', 'E': + prec: int = 3; + if fi.prec_set do prec = fi.prec; + buf: [386]byte; + + str := strconv.append_float(buf[1:], v, 'e', prec, bit_size); + str = string(buf[:len(str)+1]); + if str[1] == '+' || str[1] == '-' { + str = str[1:]; + } else { + str[0] = '+'; + } + + if fi.space && !fi.plus && str[0] == '+' { + str[0] = ' '; + } + + if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') { + strings.write_string(fi.buf, str); + return; + } + + if fi.plus || str[0] != '+' { + if fi.zero && fi.width_set && fi.width > len(str) { + strings.write_byte(fi.buf, str[0]); + fmt_write_padding(fi, fi.width - len(str)); + strings.write_string(fi.buf, str[1:]); + } else { + _pad(fi, str); + } + } else { + _pad(fi, str[1:]); + } + case: fmt_bad_verb(fi, verb); } diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index d509d7b6a..07f506b6d 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -213,28 +213,28 @@ append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> str -DecimalSlice :: struct { +Decimal_Slice :: struct { digits: []byte, count: int, decimal_point: int, neg: bool, } -FloatInfo :: struct { +Float_Info :: struct { mantbits: uint, expbits: uint, bias: int, } -_f16_info := FloatInfo{10, 5, -15}; -_f32_info := FloatInfo{23, 8, -127}; -_f64_info := FloatInfo{52, 11, -1023}; +_f16_info := Float_Info{10, 5, -15}; +_f32_info := Float_Info{23, 8, -127}; +_f64_info := Float_Info{52, 11, -1023}; generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte { bits: u64; - flt: ^FloatInfo; + flt: ^Float_Info; switch bit_size { case 32: bits = u64(transmute(u32)f32(val)); @@ -276,11 +276,11 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [ d := &d_; assign(d, mant); shift(d, exp - int(flt.mantbits)); - digs: DecimalSlice; + digs: Decimal_Slice; shortest := prec < 0; if shortest { round_shortest(d, mant, exp, flt); - digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point}; + 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); @@ -297,14 +297,14 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [ round(d, prec); } - digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point}; + digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point}; } return format_digits(buf, shortest, neg, digs, prec, fmt); } -format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: byte) -> []byte { +format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte { Buffer :: struct { b: []byte, n: int, @@ -347,12 +347,72 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice return to_bytes(b); case 'e', 'E': - panic("strconv: e/E float printing is not yet supported"); - return to_bytes(b); // TODO + add_bytes(&b, neg ? '-' : '+'); + + 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': - panic("strconv: g/G float printing is not yet supported"); - return to_bytes(b); // TODO + 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); @@ -362,7 +422,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice } -round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^FloatInfo) { +round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) { if mant == 0 { // If mantissa is zero, the number is zero d.count = 0; return;