@@ -29,10 +29,68 @@ const _buffer = fill(0x00, 1024)
2929import Base. promote_rule
3030import Base. Grisu. DIGITS
3131
32+ # ############################################################################
33+ # exception handling via global flags
34+ # (todo: recompile library with GLOBAL_FLAGS=0 for thread-safety)
35+
36+ const flags = Ref {Ptr{Cuint}} () # set to __bid_IDEC_glbflags in __init__
37+
38+ # clear exception flags and return x
39+ function nox (x)
40+ unsafe_store! (flags[], 0 )
41+ return x
42+ end
43+ # Check exception flags in mask & throw, otherwise returning x;
44+ # always clearing exceptions. These are macros so that
45+ # the error message is only evaluated if an exception occurs.
46+
47+ macro xchk (x, args... )
48+ mask= 0x3f
49+ if ! isempty (args) && Meta. isexpr (args[end ], :(= )) && args[end ]. args[1 ] == :mask # mask=... keyword at end
50+ mask = esc (args[end ]. args[2 ])
51+ args = args[1 : end - 1 ]
52+ end
53+ quote
54+ ret = $ (esc (x))
55+ f = unsafe_load (flags[])
56+ unsafe_store! (flags[], 0 )
57+ f & $ mask != 0 && xchk_throw (f, $ (map (esc,args)... ))
58+ ret
59+ end
60+ end
61+
62+ macro xchk1 (x, exc, args... )
63+ mask= 0x3f
64+ if ! isempty (args) && Meta. isexpr (args[end ], :(= )) && args[end ]. args[1 ] == :mask # mask=... keyword at end
65+ mask = esc (args[end ]. args[2 ])
66+ args = args[1 : end - 1 ]
67+ end
68+ quote
69+ ret = $ (esc (x))
70+ f = unsafe_load (flags[])
71+ unsafe_store! (flags[], 0 )
72+ f & $ mask != 0 && throw ($ exc ($ (map (esc,args)... )))
73+ ret
74+ end
75+ end
76+
77+ # separate this exception throwing code into a function to avoid
78+ # inlining it over and over in the @xchk macro
79+ function xchk_throw (f, args... )
80+ f & INEXACT != 0 && throw (InexactError (args... ))
81+ f & OVERFLOW != 0 && throw (OverflowError (args... ))
82+ f & DIVBYZERO != 0 && throw (DivideError ())
83+ f & INVALID != 0 && throw (DomainError (args... ))
84+ f & UNDERFLOW != 0 && error (" underflow" )
85+ f & UNNORMAL != 0 && error (" unnormal" )
86+ @assert false # this should be unreachable
87+ end
88+
89+ # ############################################################################
90+
3291const rounding = Ref {Ptr{Cuint}} ()
33- const flags = Ref {Ptr{Cuint}} ()
34- # rounding modes, from bid_functions.h
3592
93+ # rounding modes, from bid_functions.h
3694const rounding_c2j = [RoundNearest, RoundDown, RoundUp, RoundToZero, RoundFromZero]
3795const rounding_j2c = Dict {RoundingMode, UInt32} ([(rounding_c2j[i], Cuint (i- 1 )) for i in 1 : length (rounding_c2j)])
3896
@@ -109,7 +167,7 @@ for w in (32,64,128)
109167 if isnan (x) && ! isnanstr (s)
110168 throw (ArgumentError (" invalid number format $s " ))
111169 end
112- return xchk (x, InexactError, :parse , $ BID, s)
170+ return @xchk1 (x, InexactError, :parse , $ BID, s)
113171 end
114172
115173 $ BID (x:: AbstractString ) = parse ($ BID, x)
@@ -175,7 +233,7 @@ for w in (32,64,128)
175233 n = length (DIGITS) - 1
176234 end
177235 # rounded = round(x * exp10($BID(n)), RoundNearestTiesAway)
178- rounded = xchk (ccall (($ (bidsym (w," round_integral_nearest_away" )), libbid), $ BID, ($ BID,), x * exp10 ($ BID (n))), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
236+ rounded = @xchk1 (ccall (($ (bidsym (w," round_integral_nearest_away" )), libbid), $ BID, ($ BID,), x * exp10 ($ BID (n))), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
179237 if rounded == 0
180238 DIGITS[1 ] = UInt8 (' 0' )
181239 return Int32 (1 ), Int32 (1 ), signbit (x)
@@ -224,7 +282,7 @@ for w in (32,64,128)
224282 end
225283 normalized_exponent = nox (ccall (($ (bidsym (w," ilogb" )), libbid), Cint, ($ BID,), x))
226284 # rounded = round(x * exp10($BID(n - 1 - normalized_exponent)), RoundNearestTiesAway)
227- rounded = xchk (ccall (($ (bidsym (w," round_integral_nearest_away" )), libbid), $ BID, ($ BID,), x * exp10 ($ BID (n - 1 - normalized_exponent))), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
285+ rounded = @ xchk (ccall (($ (bidsym (w," round_integral_nearest_away" )), libbid), $ BID, ($ BID,), x * exp10 ($ BID (n - 1 - normalized_exponent))), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
228286 rounded_exponent = nox (ccall (($ (bidsym (w," ilogb" )), libbid), Cint, ($ BID,), rounded))
229287 ccall (($ (bidsym (w," to_string" )), libbid), Cvoid, (Ptr{UInt8}, $ BID), _buffer, rounded)
230288 i = 2
@@ -248,7 +306,7 @@ for w in (32,64,128)
248306
249307 Base. nextfloat (x:: $BID ) = nox (_nextfloat (x))
250308 Base. prevfloat (x:: $BID ) = nox (_prevfloat (x))
251- Base. eps (x:: $BID ) = ifelse (isfinite (x), xchk (nextfloat (x) - x, OVERFLOW, " $($ BID) value overflow" ), $ (_parse (T, " NaN" )))
309+ Base. eps (x:: $BID ) = ifelse (isfinite (x), @ xchk (nextfloat (x) - x, " $($ BID) value overflow" , mask = OVERFLOW ), $ (_parse (T, " NaN" )))
252310
253311 # the meaning of the exponent is different than for binary FP: it is 10^n, not 2^n:
254312 # Base.exponent(x::$BID) = nox(ccall(($(bidsym(w,"ilogb")), libbid), Cint, ($BID,), x))
@@ -264,11 +322,11 @@ for w in (32,64,128)
264322 end
265323
266324 for f in (:exp ,:log ,:sin ,:cos ,:tan ,:asin ,:acos ,:atan ,:sinh ,:cosh ,:tanh ,:asinh ,:acosh ,:atanh ,:log1p ,:expm1 ,:log10 ,:log2 ,:exp2 ,:exp10 ,:lgamma ,:sqrt ,:cbrt ,:abs )
267- @eval Base.$ f (x:: $BID ) = xchk (ccall (($ (bidsym (w,f)), libbid), $ BID, ($ BID,), x), " invalid operation '$($ f) ' on $($ BID) " , mask= INVALID)
325+ @eval Base.$ f (x:: $BID ) = @ xchk (ccall (($ (bidsym (w,f)), libbid), $ BID, ($ BID,), x), " invalid operation '$($ f) ' on $($ BID) " , mask= INVALID)
268326 end
269327
270328 for (f,c) in ((:gamma ," tgamma" ), (:- ," negate" ), (:round ," nearbyint" ))
271- @eval Base.$ f (x:: $BID ) = xchk (ccall (($ (bidsym (w,c)), libbid), $ BID, ($ BID,), x), " invalid operation '$($ c) ' on $($ BID) " , mask= INVALID)
329+ @eval Base.$ f (x:: $BID ) = @ xchk (ccall (($ (bidsym (w,c)), libbid), $ BID, ($ BID,), x), " invalid operation '$($ c) ' on $($ BID) " , mask= INVALID)
272330 end
273331
274332 for (f,c) in ((:(== )," quiet_equal" ), (:> ," quiet_greater" ), (:< ," quiet_less" ), (:(>= ), " quiet_greater_equal" ), (:(<= ), " quiet_less_equal" ))
@@ -300,7 +358,7 @@ for w in (32,64,128)
300358 @eval promote_rule (:: Type{$BID} , :: Type{$BID′} ) = $ BID
301359 end
302360 if w != w′
303- @eval Base. convert (:: Type{$BID} , x:: $BID′ ) = xchk (ccall (($ (string (" __bid" ,w′," _to_" ," bid" ,w)), libbid), $ BID, ($ BID′,), x), INEXACT , :convert , $ BID, x)
361+ @eval Base. convert (:: Type{$BID} , x:: $BID′ ) = @xchk1 (ccall (($ (string (" __bid" ,w′," _to_" ," bid" ,w)), libbid), $ BID, ($ BID′,), x), InexactError , :convert , $ BID, x, mask = INEXACT )
304362 end
305363
306364 # promote binary*decimal -> decimal, for consistency with other operations above
@@ -321,12 +379,12 @@ for w in (32,64,128)
321379 for (i′, i′str) in ((" Int$w′ " , " int$w′ " ), (" UInt$w′ " , " uint$w′ " ))
322380 Ti′ = eval (Symbol (i′))
323381 @eval begin
324- Base. trunc (:: Type{$Ti′} , x:: $BID ) = xchk (ccall (($ (bidsym (w," to_" ,i′str," _xint" )), libbid), $ Ti′, ($ BID,), x), InexactError, :trunc , $ BID, x, mask= INVALID | OVERFLOW)
325- Base. floor (:: Type{$Ti′} , x:: $BID ) = xchk (ccall (($ (bidsym (w," to_" ,i′str," _xfloor" )), libbid), $ Ti′, ($ BID,), x), InexactError, :floor , $ BID, x, mask= INVALID | OVERFLOW)
326- Base. ceil (:: Type{$Ti′} , x:: $BID ) = xchk (ccall (($ (bidsym (w," to_" ,i′str," _xceil" )), libbid), $ Ti′, ($ BID,), x), InexactError, :ceil , $ BID, x, mask= INVALID | OVERFLOW)
327- Base. round (:: Type{$Ti′} , x:: $BID ) = xchk (ccall (($ (bidsym (w," to_" ,i′str," _xrnint" )), libbid), $ Ti′, ($ BID,), x), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
328- Base. round (:: Type{$Ti′} , x:: $BID , :: RoundingMode{:NearestTiesAway} ) = xchk (ccall (($ (bidsym (w," to_" ,i′str," _xrninta" )), libbid), $ Ti′, ($ BID,), x), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
329- Base. convert (:: Type{$Ti′} , x:: $BID ) = xchk (ccall (($ (bidsym (w," to_" ,i′str," _xfloor" )), libbid), $ Ti′, ($ BID,), x), InexactError, :convert , $ BID, x)
382+ Base. trunc (:: Type{$Ti′} , x:: $BID ) = @xchk1 (ccall (($ (bidsym (w," to_" ,i′str," _xint" )), libbid), $ Ti′, ($ BID,), x), InexactError, :trunc , $ BID, x, mask= INVALID | OVERFLOW)
383+ Base. floor (:: Type{$Ti′} , x:: $BID ) = @xchk1 (ccall (($ (bidsym (w," to_" ,i′str," _xfloor" )), libbid), $ Ti′, ($ BID,), x), InexactError, :floor , $ BID, x, mask= INVALID | OVERFLOW)
384+ Base. ceil (:: Type{$Ti′} , x:: $BID ) = @xchk1 (ccall (($ (bidsym (w," to_" ,i′str," _xceil" )), libbid), $ Ti′, ($ BID,), x), InexactError, :ceil , $ BID, x, mask= INVALID | OVERFLOW)
385+ Base. round (:: Type{$Ti′} , x:: $BID ) = @xchk1 (ccall (($ (bidsym (w," to_" ,i′str," _xrnint" )), libbid), $ Ti′, ($ BID,), x), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
386+ Base. round (:: Type{$Ti′} , x:: $BID , :: RoundingMode{:NearestTiesAway} ) = @xchk1 (ccall (($ (bidsym (w," to_" ,i′str," _xrninta" )), libbid), $ Ti′, ($ BID,), x), InexactError, :round , $ BID, x, mask= INVALID | OVERFLOW)
387+ Base. convert (:: Type{$Ti′} , x:: $BID ) = @xchk1 (ccall (($ (bidsym (w," to_" ,i′str," _xfloor" )), libbid), $ Ti′, ($ BID,), x), InexactError, :convert , $ BID, x)
330388 Base.$ (Symbol (" $Ti′ " ))(x:: $BID ) = convert ($ Ti′, x)
331389 end
332390 end
@@ -405,35 +463,7 @@ macro d32_str(s, flags...) parse(Dec32, s) end
405463macro d64_str (s, flags... ) parse (Dec64, s) end
406464macro d128_str (s, flags... ) parse (Dec128, s) end
407465
408- # clear exception flags and return x
409- function nox (x)
410- unsafe_store! (flags[], 0 )
411- return x
412- end
413-
414- # check exception flags in mask & throw, otherwise returning x;
415- # always clearing exceptions
416- function xchk (x, args... ; mask:: Integer = 0x3f )
417- f = unsafe_load (flags[])
418- unsafe_store! (flags[], 0 )
419- if f & mask != 0
420- f & INEXACT != 0 && throw (InexactError (args... ))
421- f & OVERFLOW != 0 && throw (OverflowError (args... ))
422- f & DIVBYZERO != 0 && throw (DivideError ())
423- f & INVALID != 0 && throw (DomainError (args... ))
424- f & UNDERFLOW != 0 && error (" underflow" )
425- f & UNNORMAL != 0 && error (" unnormal" )
426- end
427- return x
428- end
429-
430- function xchk (x, exc:: Type{E} , args... ; mask:: Integer = 0x3f ) where {E<: Exception }
431- f = unsafe_load (flags[])
432- unsafe_store! (flags[], 0 )
433- f & mask != 0 && throw (exc (args... ))
434- return x
435- end
436-
466+ # for zero-padding in printing routines above
437467function writezeros (io:: IO , n:: Int )
438468 for i = 1 : n
439469 write (io, UInt8 (' 0' ))
0 commit comments