OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [go/] [strconv/] [ftoa.go] - Rev 747

Compare with Previous | Blame | View Log

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Binary to decimal floating point conversion.
// Algorithm:
//   1) store mantissa in multiprecision decimal
//   2) shift decimal by exponent
//   3) read digits out & format

package strconv

import "math"

// TODO: move elsewhere?
type floatInfo struct {
        mantbits uint
        expbits  uint
        bias     int
}

var float32info = floatInfo{23, 8, -127}
var float64info = floatInfo{52, 11, -1023}

// FormatFloat converts the floating-point number f to a string,
// according to the format fmt and precision prec.  It rounds the
// result assuming that the original was obtained from a floating-point
// value of bitSize bits (32 for float32, 64 for float64).
//
// The format fmt is one of
// 'b' (-ddddp±ddd, a binary exponent),
// 'e' (-d.dddde±dd, a decimal exponent),
// 'E' (-d.ddddE±dd, a decimal exponent),
// 'f' (-ddd.dddd, no exponent),
// 'g' ('e' for large exponents, 'f' otherwise), or
// 'G' ('E' for large exponents, 'f' otherwise).
//
// The precision prec controls the number of digits
// (excluding the exponent) printed by the 'e', 'E', 'f', 'g', and 'G' formats.
// For 'e', 'E', and 'f' it is the number of digits after the decimal point.
// For 'g' and 'G' it is the total number of digits.
// The special precision -1 uses the smallest number of digits
// necessary such that ParseFloat will return f exactly.
func FormatFloat(f float64, fmt byte, prec, bitSize int) string {
        return string(genericFtoa(make([]byte, 0, max(prec+4, 24)), f, fmt, prec, bitSize))
}

// AppendFloat appends the string form of the floating-point number f,
// as generated by FormatFloat, to dst and returns the extended buffer.
func AppendFloat(dst []byte, f float64, fmt byte, prec int, bitSize int) []byte {
        return genericFtoa(dst, f, fmt, prec, bitSize)
}

func genericFtoa(dst []byte, val float64, fmt byte, prec, bitSize int) []byte {
        var bits uint64
        var flt *floatInfo
        switch bitSize {
        case 32:
                bits = uint64(math.Float32bits(float32(val)))
                flt = &float32info
        case 64:
                bits = math.Float64bits(val)
                flt = &float64info
        default:
                panic("strconv: illegal AppendFloat/FormatFloat bitSize")
        }

        neg := bits>>(flt.expbits+flt.mantbits) != 0
        exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
        mant := bits & (uint64(1)<<flt.mantbits - 1)

        switch exp {
        case 1<<flt.expbits - 1:
                // Inf, NaN
                var s string
                switch {
                case mant != 0:
                        s = "NaN"
                case neg:
                        s = "-Inf"
                default:
                        s = "+Inf"
                }
                return append(dst, s...)

        case 0:
                // denormalized
                exp++

        default:
                // add implicit top bit
                mant |= uint64(1) << flt.mantbits
        }
        exp += flt.bias

        // Pick off easy binary format.
        if fmt == 'b' {
                return fmtB(dst, neg, mant, exp, flt)
        }

        // Negative precision means "only as much as needed to be exact."
        shortest := prec < 0

        d := new(decimal)
        if shortest {
                ok := false
                if optimize && bitSize == 64 {
                        // Try Grisu3 algorithm.
                        f := new(extFloat)
                        lower, upper := f.AssignComputeBounds(val)
                        ok = f.ShortestDecimal(d, &lower, &upper)
                }
                if !ok {
                        // Create exact decimal representation.
                        // The shift is exp - flt.mantbits because mant is a 1-bit integer
                        // followed by a flt.mantbits fraction, and we are treating it as
                        // a 1+flt.mantbits-bit integer.
                        d.Assign(mant)
                        d.Shift(exp - int(flt.mantbits))
                        roundShortest(d, mant, exp, flt)
                }
                // Precision for shortest representation mode.
                if prec < 0 {
                        switch fmt {
                        case 'e', 'E':
                                prec = d.nd - 1
                        case 'f':
                                prec = max(d.nd-d.dp, 0)
                        case 'g', 'G':
                                prec = d.nd
                        }
                }
        } else {
                // Create exact decimal representation.
                d.Assign(mant)
                d.Shift(exp - int(flt.mantbits))
                // Round appropriately.
                switch fmt {
                case 'e', 'E':
                        d.Round(prec + 1)
                case 'f':
                        d.Round(d.dp + prec)
                case 'g', 'G':
                        if prec == 0 {
                                prec = 1
                        }
                        d.Round(prec)
                }
        }

        switch fmt {
        case 'e', 'E':
                return fmtE(dst, neg, d, prec, fmt)
        case 'f':
                return fmtF(dst, neg, d, prec)
        case 'g', 'G':
                // trailing fractional zeros in 'e' form will be trimmed.
                eprec := prec
                if eprec > d.nd && d.nd >= d.dp {
                        eprec = d.nd
                }
                // %e is used if the exponent from the conversion
                // is less than -4 or greater than or equal to the precision.
                // if precision was the shortest possible, use precision 6 for this decision.
                if shortest {
                        eprec = 6
                }
                exp := d.dp - 1
                if exp < -4 || exp >= eprec {
                        if prec > d.nd {
                                prec = d.nd
                        }
                        return fmtE(dst, neg, d, prec-1, fmt+'e'-'g')
                }
                if prec > d.dp {
                        prec = d.nd
                }
                return fmtF(dst, neg, d, max(prec-d.dp, 0))
        }

        // unknown format
        return append(dst, '%', fmt)
}

// Round d (= mant * 2^exp) to the shortest number of digits
// that will let the original floating point value be precisely
// reconstructed.  Size is original floating point size (64 or 32).
func roundShortest(d *decimal, mant uint64, exp int, flt *floatInfo) {
        // If mantissa is zero, the number is zero; stop now.
        if mant == 0 {
                d.nd = 0
                return
        }

        // Compute upper and lower such that any decimal number
        // between upper and lower (possibly inclusive)
        // will round to the original floating point number.

        // We may see at once that the number is already shortest.
        //
        // Suppose d is not denormal, so that 2^exp <= d < 10^dp.
        // The closest shorter number is at least 10^(dp-nd) away.
        // The lower/upper bounds computed below are at distance
        // at most 2^(exp-mantbits).
        //
        // So the number is already shortest if 10^(dp-nd) > 2^(exp-mantbits),
        // or equivalently log2(10)*(dp-nd) > exp-mantbits.
        // It is true if 332/100*(dp-nd) >= exp-mantbits (log2(10) > 3.32).
        minexp := flt.bias + 1 // minimum possible exponent
        if exp > minexp && 332*(d.dp-d.nd) >= 100*(exp-int(flt.mantbits)) {
                // The number is already shortest.
                return
        }

        // d = mant << (exp - mantbits)
        // Next highest floating point number is mant+1 << exp-mantbits.
        // Our upper bound is halfway inbetween, mant*2+1 << exp-mantbits-1.
        upper := new(decimal)
        upper.Assign(mant*2 + 1)
        upper.Shift(exp - int(flt.mantbits) - 1)

        // d = mant << (exp - mantbits)
        // Next lowest floating point number is mant-1 << exp-mantbits,
        // unless mant-1 drops the significant bit and exp is not the minimum exp,
        // in which case the next lowest is mant*2-1 << exp-mantbits-1.
        // Either way, call it mantlo << explo-mantbits.
        // Our lower bound is halfway inbetween, mantlo*2+1 << explo-mantbits-1.
        var mantlo uint64
        var explo int
        if mant > 1<<flt.mantbits || exp == minexp {
                mantlo = mant - 1
                explo = exp
        } else {
                mantlo = mant*2 - 1
                explo = exp - 1
        }
        lower := new(decimal)
        lower.Assign(mantlo*2 + 1)
        lower.Shift(explo - int(flt.mantbits) - 1)

        // The upper and lower bounds are possible outputs only if
        // the original mantissa is even, so that IEEE round-to-even
        // would round to the original mantissa and not the neighbors.
        inclusive := mant%2 == 0

        // Now we can figure out the minimum number of digits required.
        // Walk along until d has distinguished itself from upper and lower.
        for i := 0; i < d.nd; i++ {
                var l, m, u byte // lower, middle, upper digits
                if i < lower.nd {
                        l = lower.d[i]
                } else {
                        l = '0'
                }
                m = d.d[i]
                if i < upper.nd {
                        u = upper.d[i]
                } else {
                        u = '0'
                }

                // Okay to round down (truncate) if lower has a different digit
                // or if lower is inclusive and is exactly the result of rounding down.
                okdown := l != m || (inclusive && l == m && i+1 == lower.nd)

                // Okay to round up if upper has a different digit and
                // either upper is inclusive or upper is bigger than the result of rounding up.
                okup := m != u && (inclusive || m+1 < u || i+1 < upper.nd)

                // If it's okay to do either, then round to the nearest one.
                // If it's okay to do only one, do it.
                switch {
                case okdown && okup:
                        d.Round(i + 1)
                        return
                case okdown:
                        d.RoundDown(i + 1)
                        return
                case okup:
                        d.RoundUp(i + 1)
                        return
                }
        }
}

// %e: -d.ddddde±dd
func fmtE(dst []byte, neg bool, d *decimal, prec int, fmt byte) []byte {
        // sign
        if neg {
                dst = append(dst, '-')
        }

        // first digit
        ch := byte('0')
        if d.nd != 0 {
                ch = d.d[0]
        }
        dst = append(dst, ch)

        // .moredigits
        if prec > 0 {
                dst = append(dst, '.')
                for i := 1; i <= prec; i++ {
                        ch = '0'
                        if i < d.nd {
                                ch = d.d[i]
                        }
                        dst = append(dst, ch)
                }
        }

        // e±
        dst = append(dst, fmt)
        exp := d.dp - 1
        if d.nd == 0 { // special case: 0 has exponent 0
                exp = 0
        }
        if exp < 0 {
                ch = '-'
                exp = -exp
        } else {
                ch = '+'
        }
        dst = append(dst, ch)

        // dddd
        var buf [3]byte
        i := len(buf)
        for exp >= 10 {
                i--
                buf[i] = byte(exp%10 + '0')
                exp /= 10
        }
        // exp < 10
        i--
        buf[i] = byte(exp + '0')

        // leading zeroes
        if i > len(buf)-2 {
                i--
                buf[i] = '0'
        }

        return append(dst, buf[i:]...)
}

// %f: -ddddddd.ddddd
func fmtF(dst []byte, neg bool, d *decimal, prec int) []byte {
        // sign
        if neg {
                dst = append(dst, '-')
        }

        // integer, padded with zeros as needed.
        if d.dp > 0 {
                var i int
                for i = 0; i < d.dp && i < d.nd; i++ {
                        dst = append(dst, d.d[i])
                }
                for ; i < d.dp; i++ {
                        dst = append(dst, '0')
                }
        } else {
                dst = append(dst, '0')
        }

        // fraction
        if prec > 0 {
                dst = append(dst, '.')
                for i := 0; i < prec; i++ {
                        ch := byte('0')
                        if j := d.dp + i; 0 <= j && j < d.nd {
                                ch = d.d[j]
                        }
                        dst = append(dst, ch)
                }
        }

        return dst
}

// %b: -ddddddddp+ddd
func fmtB(dst []byte, neg bool, mant uint64, exp int, flt *floatInfo) []byte {
        var buf [50]byte
        w := len(buf)
        exp -= int(flt.mantbits)
        esign := byte('+')
        if exp < 0 {
                esign = '-'
                exp = -exp
        }
        n := 0
        for exp > 0 || n < 1 {
                n++
                w--
                buf[w] = byte(exp%10 + '0')
                exp /= 10
        }
        w--
        buf[w] = esign
        w--
        buf[w] = 'p'
        n = 0
        for mant > 0 || n < 1 {
                n++
                w--
                buf[w] = byte(mant%10 + '0')
                mant /= 10
        }
        if neg {
                w--
                buf[w] = '-'
        }
        return append(dst, buf[w:]...)
}

func max(a, b int) int {
        if a > b {
                return a
        }
        return b
}

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.