github.com/tobgu/qframe@v0.4.0/internal/ryu/ryu.go (about) 1 // Copyright 2018 Ulf Adams 2 // Modifications copyright 2019 Caleb Spare 3 // 4 // The contents of this file may be used under the terms of the Apache License, 5 // Version 2.0. 6 // 7 // (See accompanying file LICENSE or copy at 8 // http://www.apache.org/licenses/LICENSE-2.0) 9 // 10 // Unless required by applicable law or agreed to in writing, this software 11 // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 12 // KIND, either express or implied. 13 // 14 // The code in this file is part of a Go translation of the C code written by 15 // Ulf Adams which may be found at https://github.com/ulfjack/ryu. That source 16 // code is licensed under Apache 2.0 and this code is derivative work thereof. 17 18 // Package ryu implements the Ryu algorithm for quickly converting floating 19 // point numbers into strings. 20 package ryu 21 22 import ( 23 "math" 24 "reflect" 25 "unsafe" 26 ) 27 28 const ( 29 mantBits32 = 23 30 expBits32 = 8 31 bias32 = 127 32 33 mantBits64 = 52 34 expBits64 = 11 35 bias64 = 1023 36 ) 37 38 // FormatFloat32 converts a 32-bit floating point number f to a string. 39 // It behaves like strconv.FormatFloat(float64(f), 'e', -1, 32). 40 func FormatFloat32(f float32) string { 41 b := make([]byte, 0, 15) 42 b = AppendFloat32(b, f) 43 44 // Convert the output to a string without copying. 45 var s string 46 sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 47 sh.Data = uintptr(unsafe.Pointer(&b[0])) 48 sh.Len = len(b) 49 return s 50 } 51 52 // AppendFloat32 appends the string form of the 32-bit floating point number f, 53 // as generated by FormatFloat32, to b and returns the extended buffer. 54 func AppendFloat32(b []byte, f float32) []byte { 55 // Step 1: Decode the floating-point number. 56 // Unify normalized and subnormal cases. 57 u := math.Float32bits(f) 58 neg := u>>(mantBits32+expBits32) != 0 59 mant := u & (uint32(1)<<mantBits32 - 1) 60 exp := (u >> mantBits32) & (uint32(1)<<expBits32 - 1) 61 62 // Exit early for easy cases. 63 if exp == uint32(1)<<expBits32-1 || (exp == 0 && mant == 0) { 64 return appendSpecial(b, neg, exp == 0, mant == 0) 65 } 66 67 d, ok := float32ToDecimalExactInt(mant, exp) 68 if !ok { 69 d = float32ToDecimal(mant, exp) 70 } 71 return d.append(b, neg) 72 } 73 74 // FormatFloat64 converts a 64-bit floating point number f to a string. 75 // It behaves like strconv.FormatFloat(f, 'e', -1, 64). 76 func FormatFloat64(f float64) string { 77 b := make([]byte, 0, 24) 78 b = AppendFloat64(b, f) 79 return byteSliceToString(b) 80 } 81 82 func byteSliceToString(b []byte) string { 83 // Zero alloc conversion following pattern found in stdlib strings.Builder. 84 return *(*string)(unsafe.Pointer(&b)) 85 } 86 87 // AppendFloat64 appends the string form of the 64-bit floating point number f, 88 // as generated by FormatFloat64, to b and returns the extended buffer. 89 // It behaves like strconv.AppendFloat(b, f, 'e', -1, 64). 90 func AppendFloat64(b []byte, f float64) []byte { 91 // Step 1: Decode the floating-point number. 92 // Unify normalized and subnormal cases. 93 u := math.Float64bits(f) 94 neg := u>>(mantBits64+expBits64) != 0 95 mant := u & (uint64(1)<<mantBits64 - 1) 96 exp := (u >> mantBits64) & (uint64(1)<<expBits64 - 1) 97 98 // Exit early for easy cases. 99 if exp == uint64(1)<<expBits64-1 || (exp == 0 && mant == 0) { 100 return appendSpecial(b, neg, exp == 0, mant == 0) 101 } 102 103 d, ok := float64ToDecimalExactInt(mant, exp) 104 if !ok { 105 d = float64ToDecimal(mant, exp) 106 } 107 return d.append(b, neg) 108 } 109 110 func appendSpecial(b []byte, neg, expZero, mantZero bool) []byte { 111 if !mantZero { 112 return append(b, "NaN"...) 113 } 114 if !expZero { 115 if neg { 116 return append(b, "-Inf"...) 117 } else { 118 return append(b, "+Inf"...) 119 } 120 } 121 if neg { 122 b = append(b, '-') 123 } 124 return append(b, "0e+00"...) 125 } 126 127 // FormatFloat64 converts a 64-bit floating point number f to a string. 128 // It behaves like strconv.FormatFloat(f, 'f', -1, 64). 129 func FormatFloat64f(f float64) string { 130 b := make([]byte, 0, 24) 131 b = AppendFloat64f(b, f) 132 return byteSliceToString(b) 133 } 134 135 // AppendFloat64 appends the string form of the 64-bit floating point number f, 136 // as generated by FormatFloat64, to b and returns the extended buffer. 137 // It behaves like strconv.AppendFloat(b, f, 'f', -1, 64). 138 func AppendFloat64f(b []byte, f float64) []byte { 139 // Step 1: Decode the floating-point number. 140 // Unify normalized and subnormal cases. 141 u := math.Float64bits(f) 142 neg := u>>(mantBits64+expBits64) != 0 143 mant := u & (uint64(1)<<mantBits64 - 1) 144 exp := (u >> mantBits64) & (uint64(1)<<expBits64 - 1) 145 146 // Exit early for easy cases. 147 if exp == uint64(1)<<expBits64-1 || (exp == 0 && mant == 0) { 148 return appendSpecialf(b, neg, exp == 0, mant == 0) 149 } 150 151 d, ok := float64ToDecimalExactInt(mant, exp) 152 if !ok { 153 d = float64ToDecimal(mant, exp) 154 } 155 return d.appendF(b, neg) 156 } 157 158 func appendSpecialf(b []byte, neg, expZero, mantZero bool) []byte { 159 if !mantZero { 160 return append(b, "NaN"...) 161 } 162 if !expZero { 163 if neg { 164 return append(b, "-Inf"...) 165 } else { 166 return append(b, "+Inf"...) 167 } 168 } 169 if neg { 170 b = append(b, '-') 171 } 172 return append(b, '0') 173 } 174 175 func assert(t bool, msg string) { 176 if !t { 177 panic(msg) 178 } 179 } 180 181 // log10Pow2 returns floor(log_10(2^e)). 182 func log10Pow2(e int32) uint32 { 183 // The first value this approximation fails for is 2^1651 184 // which is just greater than 10^297. 185 assert(e >= 0, "e >= 0") 186 assert(e <= 1650, "e <= 1650") 187 return (uint32(e) * 78913) >> 18 188 } 189 190 // log10Pow5 returns floor(log_10(5^e)). 191 func log10Pow5(e int32) uint32 { 192 // The first value this approximation fails for is 5^2621 193 // which is just greater than 10^1832. 194 assert(e >= 0, "e >= 0") 195 assert(e <= 2620, "e <= 2620") 196 return (uint32(e) * 732923) >> 20 197 } 198 199 // pow5Bits returns ceil(log_2(5^e)), or else 1 if e==0. 200 func pow5Bits(e int32) int32 { 201 // This approximation works up to the point that the multiplication 202 // overflows at e = 3529. If the multiplication were done in 64 bits, 203 // it would fail at 5^4004 which is just greater than 2^9297. 204 assert(e >= 0, "e >= 0") 205 assert(e <= 3528, "e <= 3528") 206 return int32((uint32(e)*1217359)>>19 + 1) 207 } 208 209 // These boolToXxx all inline as a movzx. 210 211 func boolToInt(b bool) int { 212 if b { 213 return 1 214 } 215 return 0 216 } 217 218 func boolToUint32(b bool) uint32 { 219 if b { 220 return 1 221 } 222 return 0 223 } 224 225 func boolToUint64(b bool) uint64 { 226 if b { 227 return 1 228 } 229 return 0 230 }