@@ -21,12 +21,15 @@ function julia_string_to_number(str::AbstractString, kind)
21
21
return x
22
22
elseif kind == K " Float"
23
23
if ! startswith (str," 0x" ) && ' f' in str && ! (' p' in str)
24
- # This is kind of awful. Should we have a separate Float32 literal
25
- # type produced by the lexer? The `f` suffix is nonstandard after all.
26
- return Base . parse (Float32, replace ( str, ' f ' => ' e ' ) )
24
+ # TODO : re-detecting Float32 here is kind of awful. Should have a
25
+ # separate Float32 literal type produced by the lexer?
26
+ x, code = _parse_float (Float32, str)
27
27
else
28
- return Base . parse (Float64, str)
28
+ x, code = _parse_float (Float64, str)
29
29
end
30
+ return code === :ok ? x :
31
+ code === :underflow ? x : # < TODO : emit warning somehow?
32
+ #= code === :overflow=# ErrorVal ()
30
33
elseif kind == K " HexInt"
31
34
ndigits = length (str)- 2
32
35
return ndigits <= 2 ? Base. parse (UInt8, str) :
@@ -66,6 +69,95 @@ function julia_string_to_number(str::AbstractString, kind)
66
69
end
67
70
68
71
72
+ # -------------------------------------------------------------------------------
73
+ """
74
+ Like `Base.parse(Union{Float64,Float32}, str)`, but permits float underflow
75
+
76
+ Parse a Float64. str[firstind:lastind] must be a valid floating point literal
77
+ string. If the value is outside Float64 range.
78
+ """
79
+ function _parse_float (:: Type{T} , str:: String ,
80
+ firstind:: Integer , lastind:: Integer ) where {T} # force specialize with where {T}
81
+ strsize = lastind - firstind + 1
82
+ bufsz = 50
83
+ if strsize < bufsz
84
+ buf = Ref {NTuple{bufsz, UInt8}} ()
85
+ ptr = Base. unsafe_convert (Ptr{UInt8}, pointer_from_objref (buf))
86
+ GC. @preserve str buf begin
87
+ unsafe_copyto! (ptr, pointer (str, firstind), strsize)
88
+ # Ensure ptr is null terminated
89
+ unsafe_store! (ptr, UInt8 (0 ), strsize + 1 )
90
+ _unsafe_parse_float (T, ptr, strsize)
91
+ end
92
+ else
93
+ # Slow path with allocation.
94
+ buf = Vector {UInt8} (str[firstind: lastind])
95
+ push! (buf, 0x00 )
96
+ ptr = pointer (buf)
97
+ GC. @preserve buf _unsafe_parse_float (T, ptr, strsize)
98
+ end
99
+ end
100
+
101
+ function _parse_float (T, str:: String )
102
+ _parse_float (T, str, firstindex (str), lastindex (str))
103
+ end
104
+
105
+ # Internals of _parse_float, split into a separate function to avoid some
106
+ # apparent codegen issues https://github.com/JuliaLang/julia/issues/46509
107
+ # (perhaps we don't want the `buf` in `GC.@preserve buf` to be stack allocated
108
+ # on one branch and heap allocated in another?)
109
+ @inline function _unsafe_parse_float (:: Type{Float64} , ptr, strsize)
110
+ Libc. errno (0 )
111
+ endptr = Ref {Ptr{UInt8}} (C_NULL )
112
+ x = @ccall jl_strtod_c (ptr:: Ptr{UInt8} , endptr:: Ptr{Ptr{UInt8}} ):: Cdouble
113
+ @check endptr[] == ptr + strsize
114
+ status = :ok
115
+ if Libc. errno () == Libc. ERANGE
116
+ # strtod man page:
117
+ # * If the correct value would cause overflow, plus or
118
+ # minus HUGE_VAL, HUGE_VALF, or HUGE_VALL is returned and
119
+ # ERANGE is stored in errno.
120
+ # * If the correct value would cause underflow, a value with
121
+ # magnitude no larger than DBL_MIN, FLT_MIN, or LDBL_MIN is
122
+ # returned and ERANGE is stored in errno.
123
+ status = abs (x) < 1 ? :underflow : :overflow
124
+ end
125
+ return (x, status)
126
+ end
127
+
128
+ @inline function _unsafe_parse_float (:: Type{Float32} , ptr, strsize)
129
+ # Convert float exponent 'f' to 'e' for strtof, eg, 1.0f0 => 1.0e0
130
+ # Presumes we can modify the data in ptr!
131
+ for p in ptr+ strsize- 1 : - 1 : ptr
132
+ if unsafe_load (p) == UInt8 (' f' )
133
+ unsafe_store! (p, UInt8 (' e' ))
134
+ break
135
+ end
136
+ end
137
+ Libc. errno (0 )
138
+ endptr = Ref {Ptr{UInt8}} (C_NULL )
139
+ status = :ok
140
+ @static if Sys. iswindows ()
141
+ # Call strtod here and convert to Float32 on the Julia side because
142
+ # strtof seems buggy on windows and doesn't set ERANGE correctly on
143
+ # overflow. See also
144
+ # https://github.com/JuliaLang/julia/issues/46544
145
+ x = Float32 (@ccall jl_strtod_c (ptr:: Ptr{UInt8} , endptr:: Ptr{Ptr{UInt8}} ):: Cdouble )
146
+ if isinf (x)
147
+ status = :overflow
148
+ # Underflow not detected, but that will only be a warning elsewhere.
149
+ end
150
+ else
151
+ x = @ccall jl_strtof_c (ptr:: Ptr{UInt8} , endptr:: Ptr{Ptr{UInt8}} ):: Cfloat
152
+ end
153
+ @check endptr[] == ptr + strsize
154
+ if Libc. errno () == Libc. ERANGE
155
+ status = abs (x) < 1 ? :underflow : :overflow
156
+ end
157
+ return (x, status)
158
+ end
159
+
160
+
69
161
# -------------------------------------------------------------------------------
70
162
is_indentation (c) = c == ' ' || c == ' \t '
71
163
0 commit comments