Line | Exclusive | Inclusive | Code |
---|---|---|---|
1 | # This file is a part of Julia. License is MIT: https://julialang.org/license | ||
2 | |||
3 | module Grisu | ||
4 | |||
5 | export print_shortest | ||
6 | export DIGITS, DIGITSs, grisu | ||
7 | |||
8 | const SHORTEST = 1 | ||
9 | const FIXED = 2 | ||
10 | const PRECISION = 3 | ||
11 | |||
12 | include("grisu/float.jl") | ||
13 | include("grisu/fastshortest.jl") | ||
14 | include("grisu/fastprecision.jl") | ||
15 | include("grisu/fastfixed.jl") | ||
16 | include("grisu/bignums.jl") | ||
17 | include("grisu/bignum.jl") | ||
18 | |||
19 | const DIGITS = Vector{UInt8}(undef, 309+17) | ||
20 | const BIGNUMS = [Bignums.Bignum(),Bignums.Bignum(),Bignums.Bignum(),Bignums.Bignum()] | ||
21 | |||
22 | # thread-safe code should use a per-thread DIGITS buffer DIGITSs[Threads.threadid()] | ||
23 | const DIGITSs = [DIGITS] | ||
24 | const BIGNUMSs = [BIGNUMS] | ||
25 | function __init__() | ||
26 | Threads.resize_nthreads!(DIGITSs) | ||
27 | Threads.resize_nthreads!(BIGNUMSs) | ||
28 | end | ||
29 | |||
30 | """ | ||
31 | (len, point, neg) = Grisu.grisu(v::AbstractFloat, mode, requested_digits, | ||
32 | buffer=DIGITSs[Threads.threadid()], bignums=BIGNUMSs[Threads.threadid()]) | ||
33 | |||
34 | Convert the number `v` to decimal using the Grisu algorithm. | ||
35 | |||
36 | `mode` can be one of: | ||
37 | - `Grisu.SHORTEST`: convert to the shortest decimal representation which can be "round-tripped" back to `v`. | ||
38 | - `Grisu.FIXED`: round to `requested_digits` digits. | ||
39 | - `Grisu.PRECISION`: round to `requested_digits` significant digits. | ||
40 | |||
41 | The characters are written as bytes to `buffer`, with a terminating NUL byte, and `bignums` are used internally as part of the correction step. | ||
42 | |||
43 | The returned tuple contains: | ||
44 | |||
45 | - `len`: the number of digits written to `buffer` (excluding NUL) | ||
46 | - `point`: the location of the radix point relative to the start of the array (e.g. if | ||
47 | `point == 3`, then the radix point should be inserted between the 3rd and 4th | ||
48 | digit). Note that this can be negative (for very small values), or greater than `len` | ||
49 | (for very large values). | ||
50 | - `neg`: the signbit of `v` (see [`signbit`](@ref)). | ||
51 | """ | ||
52 |
12 (28.57%) samples spent in grisu
function grisu(v::AbstractFloat,mode,requested_digits,buffer=DIGITSs[Threads.threadid()],bignums=BIGNUMSs[Threads.threadid()])
0 (ex.), 12 (100.00%) (incl.) when called from _show line 97 1 (100.00%) (ex.), 12 (100.00%) (incl.) when called from grisu line 53 |
||
53 | 12 (28.57%) |
12 (100.00%)
samples spent calling
grisu
if signbit(v)
|
|
54 | neg = true | ||
55 | v = -v | ||
56 | else | ||
57 | neg = false | ||
58 | end | ||
59 | if mode == PRECISION && requested_digits == 0 | ||
60 | buffer[1] = 0x00 | ||
61 | len = 0 | ||
62 | return 0, 0, neg | ||
63 | end | ||
64 | if v == 0.0 | ||
65 | buffer[1] = 0x30 | ||
66 | buffer[2] = 0x00 | ||
67 | len = point = 1 | ||
68 | return len, point, neg | ||
69 | end | ||
70 | if mode == SHORTEST | ||
71 | 11 (26.19%) |
11 (100.00%)
samples spent calling
fastshortest
status,len,point = fastshortest(v,buffer)
|
|
72 | elseif mode == FIXED | ||
73 | status,len,point = fastfixedtoa(v,0,requested_digits,buffer) | ||
74 | elseif mode == PRECISION | ||
75 | status,len,point = fastprecision(v,requested_digits,buffer) | ||
76 | end | ||
77 | status && return len-1, point, neg | ||
78 | status, len, point = bignumdtoa(v,mode,requested_digits,buffer,bignums) | ||
79 | return len-1, point, neg | ||
80 | end | ||
81 | |||
82 | nanstr(x::AbstractFloat) = "NaN" | ||
83 | nanstr(x::Float32) = "NaN32" | ||
84 | nanstr(x::Float16) = "NaN16" | ||
85 | infstr(x::AbstractFloat) = "Inf" | ||
86 | infstr(x::Float32) = "Inf32" | ||
87 | infstr(x::Float16) = "Inf16" | ||
88 | |||
89 | function _show(io::IO, x::AbstractFloat, mode, n::Int, typed, compact) | ||
90 | isnan(x) && return print(io, typed ? nanstr(x) : "NaN") | ||
91 | if isinf(x) | ||
92 | signbit(x) && print(io,'-') | ||
93 | print(io, typed ? infstr(x) : "Inf") | ||
94 | return | ||
95 | end | ||
96 | typed && isa(x,Float16) && print(io, "Float16(") | ||
97 | 12 (28.57%) |
12 (100.00%)
samples spent calling
grisu
(len,pt,neg),buffer = grisu(x,mode,n),DIGITSs[Threads.threadid()]
|
|
98 | pdigits = pointer(buffer) | ||
99 | if mode == PRECISION | ||
100 | while len > 1 && buffer[len] == 0x30 | ||
101 | len -= 1 | ||
102 | end | ||
103 | end | ||
104 | neg && print(io,'-') | ||
105 | exp_form = pt <= -4 || pt > 6 | ||
106 | exp_form = exp_form || (pt >= len && abs(mod(x + 0.05, 10^(pt - len)) - 0.05) > 0.05) # see issue #6608 | ||
107 | if exp_form # .00001 to 100000. | ||
108 | # => #.#######e### | ||
109 | # assumes ASCII/UTF8 encoding of digits is okay for out: | ||
110 | unsafe_write(io, pdigits, 1) | ||
111 | print(io, '.') | ||
112 | if len > 1 | ||
113 | unsafe_write(io, pdigits+1, len-1) | ||
114 | else | ||
115 | print(io, '0') | ||
116 | end | ||
117 | print(io, (typed && isa(x,Float32)) ? 'f' : 'e') | ||
118 | print(io, string(pt - 1)) | ||
119 | typed && isa(x,Float16) && print(io, ")") | ||
120 | return | ||
121 | elseif pt <= 0 | ||
122 | # => 0.00######## | ||
123 | 1 (2.38%) |
1 (100.00%)
samples spent calling
print
print(io, "0.")
|
|
124 | while pt < 0 | ||
125 | print(io, '0') | ||
126 | pt += 1 | ||
127 | end | ||
128 | 1 (2.38%) |
1 (100.00%)
samples spent calling
unsafe_write
unsafe_write(io, pdigits, len)
|
|
129 | elseif pt >= len | ||
130 | # => ########00.0 | ||
131 | unsafe_write(io, pdigits, len) | ||
132 | while pt > len | ||
133 | print(io, '0') | ||
134 | len += 1 | ||
135 | end | ||
136 | print(io, ".0") | ||
137 | else # => ####.#### | ||
138 | unsafe_write(io, pdigits, pt) | ||
139 | print(io, '.') | ||
140 | unsafe_write(io, pdigits+pt, len-pt) | ||
141 | end | ||
142 | typed && !compact && isa(x,Float32) && print(io, "f0") | ||
143 | typed && isa(x,Float16) && print(io, ")") | ||
144 | nothing | ||
145 | end | ||
146 | |||
147 | function Base.show(io::IO, x::Union{Float64,Float32}) | ||
148 | if get(io, :compact, false) | ||
149 | _show(io, x, PRECISION, 6, x isa Float64, true) | ||
150 | else | ||
151 | 14 (33.33%) |
14 (100.00%)
samples spent calling
_show
_show(io, x, SHORTEST, 0, get(io, :typeinfo, Any) !== typeof(x), false)
|
|
152 | end | ||
153 | end | ||
154 | |||
155 | function Base.show(io::IO, x::Float16) | ||
156 | hastypeinfo = Float16 === get(io, :typeinfo, Any) | ||
157 | # if hastypeinfo, the printing would be more compact using `SHORTEST` | ||
158 | # while still retaining all the information | ||
159 | # BUT: we want to print all digits in `show`, not in display, so we rely | ||
160 | # on the :compact property to make the decision | ||
161 | # (cf. https://github.com/JuliaLang/julia/pull/24651#issuecomment-345535687) | ||
162 | if get(io, :compact, false) && !hastypeinfo | ||
163 | _show(io, x, PRECISION, 5, false, true) | ||
164 | else | ||
165 | _show(io, x, SHORTEST, 0, !hastypeinfo, false) | ||
166 | end | ||
167 | end | ||
168 | |||
169 | Base.print(io::IO, x::Float32) = _show(io, x, SHORTEST, 0, false, false) | ||
170 | Base.print(io::IO, x::Float16) = _show(io, x, SHORTEST, 0, false, false) | ||
171 | |||
172 | # normal: | ||
173 | # 0 < pt < len ####.#### len+1 | ||
174 | # pt <= 0 0.000######## len-pt+1 | ||
175 | # len <= pt (dot) ########000. pt+1 | ||
176 | # len <= pt (no dot) ########000 pt | ||
177 | # exponential: | ||
178 | # pt <= 0 ########e-### len+k+2 | ||
179 | # 0 < pt ########e### len+k+1 | ||
180 | |||
181 | function _print_shortest(io::IO, x::AbstractFloat, dot::Bool, mode, n::Int) | ||
182 | isnan(x) && return print(io, "NaN") | ||
183 | x < 0 && print(io,'-') | ||
184 | isinf(x) && return print(io, "Inf") | ||
185 | (len,pt,neg),buffer = grisu(x,mode,n),DIGITSs[Threads.threadid()] | ||
186 | pdigits = pointer(buffer) | ||
187 | e = pt-len | ||
188 | k = -9<=e<=9 ? 1 : 2 | ||
189 | if -pt > k+1 || e+dot > k+1 | ||
190 | # => ########e### | ||
191 | unsafe_write(io, pdigits+0, len) | ||
192 | print(io, 'e') | ||
193 | print(io, string(e)) | ||
194 | return | ||
195 | elseif pt <= 0 | ||
196 | # => 0.000######## | ||
197 | print(io, "0.") | ||
198 | while pt < 0 | ||
199 | print(io, '0') | ||
200 | pt += 1 | ||
201 | end | ||
202 | unsafe_write(io, pdigits+0, len) | ||
203 | elseif e >= dot | ||
204 | # => ########000. | ||
205 | unsafe_write(io, pdigits+0, len) | ||
206 | while e > 0 | ||
207 | print(io, '0') | ||
208 | e -= 1 | ||
209 | end | ||
210 | if dot | ||
211 | print(io, '.') | ||
212 | end | ||
213 | else # => ####.#### | ||
214 | unsafe_write(io, pdigits+0, pt) | ||
215 | print(io, '.') | ||
216 | unsafe_write(io, pdigits+pt, len-pt) | ||
217 | end | ||
218 | nothing | ||
219 | end | ||
220 | |||
221 | """ | ||
222 | print_shortest(io::IO, x) | ||
223 | |||
224 | Print the shortest possible representation, with the minimum number of consecutive non-zero | ||
225 | digits, of number `x`, ensuring that it would parse to the exact same number. | ||
226 | """ | ||
227 | print_shortest(io::IO, x::AbstractFloat, dot::Bool) = _print_shortest(io, x, dot, SHORTEST, 0) | ||
228 | print_shortest(io::IO, x::Union{AbstractFloat,Integer}) = print_shortest(io, float(x), false) | ||
229 | |||
230 | end # module |