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