package v1 /** * Copyright 2015 Paul Querna, Klaus Post * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* Most of this file are on Go stdlib's strconv/ftoa.go */ // 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. import "math" // TODO: move elsewhere? type floatInfo struct { mantbits uint expbits uint bias int } var optimize = true // can change for testing var float32info = floatInfo{23, 8, -127} var float64info = floatInfo{52, 11, -1023} // AppendFloat appends the string form of the floating-point number f, // as generated by FormatFloat func AppendFloat(dst EncodingBuffer, val float64, fmt byte, prec, bitSize int) { 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< digs.nd && digs.nd >= digs.dp { eprec = digs.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 := digs.dp - 1 if exp < -4 || exp >= eprec { if prec > digs.nd { prec = digs.nd } fmtE(dst, neg, digs, prec-1, fmt+'e'-'g') return } if prec > digs.dp { prec = digs.nd } fmtF(dst, neg, digs, max(prec-digs.dp, 0)) return } // unknown format dst.Write([]byte{'%', fmt}) return } // 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 between, 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 between, mantlo*2+1 << explo-mantbits-1. var mantlo uint64 var explo int if mant > 1< 0 { dst.WriteByte('.') i := 1 m := min(d.nd, prec+1) if i < m { dst.Write(d.d[i:m]) i = m } for i <= prec { dst.WriteByte('0') i++ } } // e± dst.WriteByte(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.WriteByte(ch) // dd or ddd switch { case exp < 10: dst.WriteByte('0') dst.WriteByte(byte(exp) + '0') case exp < 100: dst.WriteByte(byte(exp/10) + '0') dst.WriteByte(byte(exp%10) + '0') default: dst.WriteByte(byte(exp/100) + '0') dst.WriteByte(byte(exp/10)%10 + '0') dst.WriteByte(byte(exp%10) + '0') } return } // %f: -ddddddd.ddddd func fmtF(dst EncodingBuffer, neg bool, d decimalSlice, prec int) { // sign if neg { dst.WriteByte('-') } // integer, padded with zeros as needed. if d.dp > 0 { m := min(d.nd, d.dp) dst.Write(d.d[:m]) for ; m < d.dp; m++ { dst.WriteByte('0') } } else { dst.WriteByte('0') } // fraction if prec > 0 { dst.WriteByte('.') for i := 0; i < prec; i++ { ch := byte('0') if j := d.dp + i; 0 <= j && j < d.nd { ch = d.d[j] } dst.WriteByte(ch) } } return } // %b: -ddddddddp±ddd func fmtB(dst EncodingBuffer, neg bool, mant uint64, exp int, flt *floatInfo) { // sign if neg { dst.WriteByte('-') } // mantissa formatBits(dst, mant, 10, false) // p dst.WriteByte('p') // ±exponent exp -= int(flt.mantbits) if exp >= 0 { dst.WriteByte('+') } formatBits(dst, uint64(exp), 10, exp < 0) return } func min(a, b int) int { if a < b { return a } return b } func max(a, b int) int { if a > b { return a } return b } // formatBits computes the string representation of u in the given base. // If neg is set, u is treated as negative int64 value. func formatBits(dst EncodingBuffer, u uint64, base int, neg bool) { if base < 2 || base > len(digits) { panic("strconv: illegal AppendInt/FormatInt base") } // 2 <= base && base <= len(digits) var a [64 + 1]byte // +1 for sign of 64bit value in base 2 i := len(a) if neg { u = -u } // convert bits if base == 10 { // common case: use constants for / because // the compiler can optimize it into a multiply+shift if ^uintptr(0)>>32 == 0 { for u > uint64(^uintptr(0)) { q := u / 1e9 us := uintptr(u - q*1e9) // us % 1e9 fits into a uintptr for j := 9; j > 0; j-- { i-- qs := us / 10 a[i] = byte(us - qs*10 + '0') us = qs } u = q } } // u guaranteed to fit into a uintptr us := uintptr(u) for us >= 10 { i-- q := us / 10 a[i] = byte(us - q*10 + '0') us = q } // u < 10 i-- a[i] = byte(us + '0') } else if s := shifts[base]; s > 0 { // base is power of 2: use shifts and masks instead of / and % b := uint64(base) m := uintptr(b) - 1 // == 1<= b { i-- a[i] = digits[uintptr(u)&m] u >>= s } // u < base i-- a[i] = digits[uintptr(u)] } else { // general case b := uint64(base) for u >= b { i-- q := u / b a[i] = digits[uintptr(u-q*b)] u = q } // u < base i-- a[i] = digits[uintptr(u)] } // add sign, if any if neg { i-- a[i] = '-' } dst.Write(a[i:]) }