@@ -11,9 +11,6 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox,
1111
1212import Random: Random, AbstractRNG, SamplerType, rand!
1313
14- import Base. Checked: checked_neg, checked_abs, checked_add, checked_sub, checked_mul,
15- checked_div, checked_fld, checked_cld, checked_rem, checked_mod
16-
1714using Base: @pure
1815
1916"""
@@ -35,14 +32,11 @@ export
3532# "special" typealiases
3633 # Q and N typealiases are exported in separate source files
3734# Functions
38- scaledual,
39- wrapping_neg, wrapping_abs, wrapping_add, wrapping_sub, wrapping_mul,
40- wrapping_div, wrapping_fld, wrapping_cld, wrapping_rem, wrapping_mod,
41- saturating_neg, saturating_abs, saturating_add, saturating_sub, saturating_mul,
42- saturating_div, saturating_fld, saturating_cld, saturating_rem, saturating_mod,
43- wrapping_fdiv, saturating_fdiv, checked_fdiv
35+ scaledual
4436
4537include (" utilities.jl" )
38+ using . Utilities
39+ import . Utilities: floattype, rawone, nbitsfrac, rawtype, signbits, nbitsint, scaledual
4640
4741# reinterpretation
4842reinterpret (x:: FixedPoint ) = x. i
@@ -57,18 +51,6 @@ rawtype(::Type{X}) where {T, X <: FixedPoint{T}} = T
5751signbits (:: Type{X} ) where {T, X <: FixedPoint{T} } = T <: Unsigned ? 0 : 1
5852nbitsint (:: Type{X} ) where {X <: FixedPoint } = bitwidth (X) - nbitsfrac (X) - signbits (X)
5953
60- # construction using the (approximate) intended value, i.e., N0f8
61- * (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _convert (X, x)
62- wrapping_mul (x:: Real , :: Type{X} ) where {X <: FixedPoint } = x % X
63- saturating_mul (x:: Real , :: Type{X} ) where {X <: FixedPoint } = clamp (x, X)
64- checked_mul (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _convert (X, x)
65-
66- # type modulus
67- rem (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _rem (x, X)
68- wrapping_rem (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _rem (x, X)
69- saturating_rem (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _rem (x, X)
70- checked_rem (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _rem (x, X)
71-
7254# constructor-style conversions
7355(:: Type{X} )(x:: X ) where {X <: FixedPoint } = x
7456(:: Type{X} )(x:: Number ) where {X <: FixedPoint } = _convert (X, x)
@@ -139,9 +121,6 @@ zero(::Type{X}) where {X <: FixedPoint} = X(zero(rawtype(X)), 0)
139121oneunit (:: Type{X} ) where {X <: FixedPoint } = X (rawone (X), 0 )
140122one (:: Type{X} ) where {X <: FixedPoint } = oneunit (X)
141123
142- # for Julia v1.0, which does not fold `div_float` before inlining
143- inv_rawone (x) = (@generated ) ? (y = 1.0 / rawone (x); :($ y)) : 1.0 / rawone (x)
144-
145124# traits
146125eps (:: Type{X} ) where {X <: FixedPoint } = X (oneunit (rawtype (X)), 0 )
147126typemax (:: Type{T} ) where {T <: FixedPoint } = T (typemax (rawtype (T)), 0 )
@@ -192,164 +171,12 @@ RGB{Float32}
192171
193172`RGB` itself is not a subtype of `AbstractFloat`, but unlike `RGB{N0f8}` operations with `RGB{Float32}` are not subject to integer overflow.
194173"""
195- floattype (:: Type{T} ) where {T <: AbstractFloat } = T # fallback (we want a MethodError if no method producing AbstractFloat is defined)
196- floattype (:: Type{T} ) where {T <: Union{ShortInts, Bool} } = Float32
197- floattype (:: Type{T} ) where {T <: Integer } = Float64
198- floattype (:: Type{T} ) where {T <: LongInts } = BigFloat
199- floattype (:: Type{T} ) where {I <: Integer , T <: Rational{I} } = typeof (zero (I)/ oneunit (I))
200- floattype (:: Type{<:AbstractIrrational} ) = Float64
201174floattype (:: Type{X} ) where {T <: ShortInts , X <: FixedPoint{T} } = Float32
202175floattype (:: Type{X} ) where {T <: Integer , X <: FixedPoint{T} } = Float64
203176floattype (:: Type{X} ) where {T <: LongInts , X <: FixedPoint{T} } = BigFloat
204177
205- # Non-Real types
206- floattype (:: Type{Complex{T}} ) where T = Complex{floattype (T)}
207- floattype (:: Type{Base.TwicePrecision{Float64}} ) = Float64 # wider would be nice, but hardware support is paramount
208- floattype (:: Type{Base.TwicePrecision{T}} ) where T<: Union{Float16,Float32} = widen (T)
209-
210178float (x:: FixedPoint ) = convert (floattype (x), x)
211179
212- # wrapping arithmetic
213- wrapping_neg (x:: X ) where {X <: FixedPoint } = X (- x. i, 0 )
214- wrapping_abs (x:: X ) where {X <: FixedPoint } = X (abs (x. i), 0 )
215- wrapping_add (x:: X , y:: X ) where {X <: FixedPoint } = X (x. i + y. i, 0 )
216- wrapping_sub (x:: X , y:: X ) where {X <: FixedPoint } = X (x. i - y. i, 0 )
217- wrapping_mul (x:: X , y:: X ) where {X <: FixedPoint } = (float (x) * float (y)) % X
218- function wrapping_fdiv (x:: X , y:: X ) where {X <: FixedPoint }
219- z = floattype (X)(x. i) / floattype (X)(y. i)
220- isfinite (z) ? z % X : zero (X)
221- end
222- function wrapping_div (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {T, X <: FixedPoint{T} }
223- z = round (floattype (X)(x. i) / floattype (X)(y. i), r)
224- isfinite (z) || return zero (T)
225- if T <: Unsigned
226- _unsafe_trunc (T, z)
227- else
228- z > typemax (T) ? typemin (T) : _unsafe_trunc (T, z)
229- end
230- end
231- wrapping_fld (x:: X , y:: X ) where {X <: FixedPoint } = wrapping_div (x, y, RoundDown)
232- wrapping_cld (x:: X , y:: X ) where {X <: FixedPoint } = wrapping_div (x, y, RoundUp)
233- wrapping_rem (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {T, X <: FixedPoint{T} } =
234- X (x. i - wrapping_div (x, y, r) * y. i, 0 )
235- wrapping_mod (x:: X , y:: X ) where {X <: FixedPoint } = wrapping_rem (x, y, RoundDown)
236-
237- # saturating arithmetic
238- saturating_neg (x:: X ) where {X <: FixedPoint } = X (~ min (x. i - true , x. i), 0 )
239- saturating_neg (x:: X ) where {X <: FixedPoint{<:Unsigned} } = zero (X)
240-
241- saturating_abs (x:: X ) where {X <: FixedPoint } =
242- X (ifelse (signbit (abs (x. i)), typemax (x. i), abs (x. i)), 0 )
243-
244- saturating_add (x:: X , y:: X ) where {X <: FixedPoint } =
245- X (x. i + ifelse (x. i < 0 , max (y. i, typemin (x. i) - x. i), min (y. i, typemax (x. i) - x. i)), 0 )
246- saturating_add (x:: X , y:: X ) where {X <: FixedPoint{<:Unsigned} } = X (x. i + min (~ x. i, y. i), 0 )
247-
248- saturating_sub (x:: X , y:: X ) where {X <: FixedPoint } =
249- X (x. i - ifelse (x. i < 0 , min (y. i, x. i - typemin (x. i)), max (y. i, x. i - typemax (x. i))), 0 )
250- saturating_sub (x:: X , y:: X ) where {X <: FixedPoint{<:Unsigned} } = X (x. i - min (x. i, y. i), 0 )
251-
252- saturating_mul (x:: X , y:: X ) where {X <: FixedPoint } = clamp (float (x) * float (y), X)
253-
254- saturating_fdiv (x:: X , y:: X ) where {X <: FixedPoint } =
255- clamp (floattype (X)(x. i) / floattype (X)(y. i), X)
256-
257- function saturating_div (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {T, X <: FixedPoint{T} }
258- z = round (floattype (X)(x. i) / floattype (X)(y. i), r)
259- isnan (z) && return zero (T)
260- if T <: Unsigned
261- isfinite (z) ? _unsafe_trunc (T, z) : typemax (T)
262- else
263- _unsafe_trunc (T, clamp (z, typemin (T), typemax (T)))
264- end
265- end
266- saturating_fld (x:: X , y:: X ) where {X <: FixedPoint } = saturating_div (x, y, RoundDown)
267- saturating_cld (x:: X , y:: X ) where {X <: FixedPoint } = saturating_div (x, y, RoundUp)
268- function saturating_rem (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {T, X <: FixedPoint{T} }
269- T <: Unsigned && r isa RoundingMode{:Up } && return zero (X)
270- X (x. i - saturating_div (x, y, r) * y. i, 0 )
271- end
272- saturating_mod (x:: X , y:: X ) where {X <: FixedPoint } = saturating_rem (x, y, RoundDown)
273-
274- # checked arithmetic
275- checked_neg (x:: X ) where {X <: FixedPoint } = checked_sub (zero (X), x)
276- function checked_abs (x:: X ) where {X <: FixedPoint }
277- abs (x. i) >= 0 || throw_overflowerror_abs (x)
278- X (abs (x. i), 0 )
279- end
280- function checked_add (x:: X , y:: X ) where {X <: FixedPoint }
281- r, f = Base. Checked. add_with_overflow (x. i, y. i)
282- z = X (r, 0 ) # store first
283- f && throw_overflowerror (:+ , x, y)
284- z
285- end
286- function checked_sub (x:: X , y:: X ) where {X <: FixedPoint }
287- r, f = Base. Checked. sub_with_overflow (x. i, y. i)
288- z = X (r, 0 ) # store first
289- f && throw_overflowerror (:- , x, y)
290- z
291- end
292- function checked_mul (x:: X , y:: X ) where {X <: FixedPoint }
293- z = float (x) * float (y)
294- typemin (X) - eps (X)/ 2 <= z < typemax (X) + eps (X)/ 2 || throw_overflowerror (:* , x, y)
295- z % X
296- end
297- function checked_fdiv (x:: X , y:: X ) where {T, X <: FixedPoint{T} }
298- y === zero (X) && throw (DivideError ())
299- z = floattype (X)(x. i) / floattype (X)(y. i)
300- if T <: Unsigned
301- z < typemax (X) + eps (X)/ 2 || throw_overflowerror (:/ , x, y)
302- else
303- typemin (X) - eps (X)/ 2 <= z < typemax (X) + eps (X)/ 2 || throw_overflowerror (:/ , x, y)
304- end
305- z % X
306- end
307- function checked_div (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {T, X <: FixedPoint{T} }
308- y === zero (X) && throw (DivideError ())
309- z = round (floattype (X)(x. i) / floattype (X)(y. i), r)
310- if T <: Signed
311- z <= typemax (T) || throw_overflowerror_div (r, x, y)
312- end
313- _unsafe_trunc (T, z)
314- end
315- checked_fld (x:: X , y:: X ) where {X <: FixedPoint } = checked_div (x, y, RoundDown)
316- checked_cld (x:: X , y:: X ) where {X <: FixedPoint } = checked_div (x, y, RoundUp)
317- function checked_rem (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {T, X <: FixedPoint{T} }
318- y === zero (X) && throw (DivideError ())
319- fx, fy = floattype (X)(x. i), floattype (X)(y. i)
320- z = fx - round (fx / fy, r) * fy
321- if T <: Unsigned && r isa RoundingMode{:Up }
322- z >= zero (z) || throw_overflowerror_rem (r, x, y)
323- end
324- X (_unsafe_trunc (T, z), 0 )
325- end
326- checked_mod (x:: X , y:: X ) where {X <: FixedPoint } = checked_rem (x, y, RoundDown)
327-
328- # default arithmetic
329- const DEFAULT_ARITHMETIC = :wrapping
330-
331- for (op, name) in ((:- , :neg ), (:abs , :abs ))
332- f = Symbol (DEFAULT_ARITHMETIC, :_ , name)
333- @eval begin
334- $ op (x:: X ) where {X <: FixedPoint } = $ f (x)
335- end
336- end
337- for (op, name) in ((:+ , :add ), (:- , :sub ), (:* , :mul ))
338- f = Symbol (DEFAULT_ARITHMETIC, :_ , name)
339- @eval begin
340- $ op (x:: X , y:: X ) where {X <: FixedPoint } = $ f (x, y)
341- end
342- end
343- # force checked arithmetic
344- / (x:: X , y:: X ) where {X <: FixedPoint } = checked_fdiv (x, y)
345- div (x:: X , y:: X , r:: RoundingMode = RoundToZero) where {X <: FixedPoint } = checked_div (x, y, r)
346- fld (x:: X , y:: X ) where {X <: FixedPoint } = checked_div (x, y, RoundDown)
347- cld (x:: X , y:: X ) where {X <: FixedPoint } = checked_div (x, y, RoundUp)
348- rem (x:: X , y:: X ) where {X <: FixedPoint } = checked_rem (x, y, RoundToZero)
349- rem (x:: X , y:: X , :: RoundingMode{:Down} ) where {X <: FixedPoint } = checked_rem (x, y, RoundDown)
350- rem (x:: X , y:: X , :: RoundingMode{:Up} ) where {X <: FixedPoint } = checked_rem (x, y, RoundUp)
351- mod (x:: X , y:: X ) where {X <: FixedPoint } = checked_rem (x, y, RoundDown)
352-
353180function minmax (x:: X , y:: X ) where {X <: FixedPoint }
354181 a, b = minmax (reinterpret (x), reinterpret (y))
355182 X (a,0 ), X (b,0 )
@@ -518,6 +345,34 @@ include("normed.jl")
518345include (" deprecations.jl" )
519346const UF = (N0f8, N6f10, N4f12, N2f14, N0f16)
520347
348+ include (" arithmetic/arithmetic.jl" )
349+ using . FixedPointArithmetic
350+ # re-export
351+ for name in names (FixedPointArithmetic. Wrapping)
352+ startswith (string (name), " wrapping" ) || continue
353+ @eval export $ name
354+ end
355+ for name in names (FixedPointArithmetic. Saturating)
356+ startswith (string (name), " saturating" ) || continue
357+ @eval export $ name
358+ end
359+ for name in names (FixedPointArithmetic. Checked)
360+ startswith (string (name), " checked" ) || continue
361+ @eval export $ name
362+ end
363+
364+ # construction using the (approximate) intended value, i.e., N0f8
365+ * (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _convert (X, x)
366+ Wrapping. wrapping_mul (x:: Real , :: Type{X} ) where {X <: FixedPoint } = x % X
367+ Saturating. saturating_mul (x:: Real , :: Type{X} ) where {X <: FixedPoint } = clamp (x, X)
368+ Checked. checked_mul (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _convert (X, x)
369+
370+ # type modulus
371+ rem (x:: Real , :: Type{X} ) where {X <: FixedPoint } = _rem (x, X)
372+ Wrapping. wrapping_rem (x:: Real , :: Type{X} ) where {X<: FixedPoint } = _rem (x, X)
373+ Saturating. saturating_rem (x:: Real , :: Type{X} ) where {X<: FixedPoint } = _rem (x, X)
374+ Checked. checked_rem (x:: Real , :: Type{X} ) where {X<: FixedPoint } = _rem (x, X)
375+
521376# Promotions
522377promote_rule (:: Type{X} , :: Type{Tf} ) where {X <: FixedPoint , Tf <: AbstractFloat } =
523378 promote_type (floattype (X), Tf)
@@ -585,29 +440,6 @@ scaledual(::Type{Tdual}, x::AbstractArray{T}) where {Tdual, T <: FixedPoint} =
585440 throw (ArgumentError (String (take! (io))))
586441end
587442
588- @noinline function throw_overflowerror (op:: Symbol , @nospecialize (x), @nospecialize (y))
589- io = IOBuffer ()
590- print (io, x, ' ' , op, ' ' , y, " overflowed for type " )
591- showtype (io, typeof (x))
592- throw (OverflowError (String (take! (io))))
593- end
594- @noinline function throw_overflowerror_abs (@nospecialize (x))
595- io = IOBuffer ()
596- print (io, " abs(" , x, " ) overflowed for type " )
597- showtype (io, typeof (x))
598- throw (OverflowError (String (take! (io))))
599- end
600- @noinline function throw_overflowerror_div (r:: RoundingMode , @nospecialize (x), @nospecialize (y))
601- io = IOBuffer ()
602- op = r === RoundUp ? " cld(" : r === RoundDown ? " fld(" : " div("
603- print (io, op, x, " , " , y, " ) overflowed for type " , rawtype (x))
604- throw (OverflowError (String (take! (io))))
605- end
606- @noinline function throw_overflowerror_rem (r:: RoundingMode , @nospecialize (x), @nospecialize (y))
607- io = IOBuffer ()
608- print (io, " rem(" , x, " , " , y, " , " , r, " ) overflowed for type " , typeof (x))
609- throw (OverflowError (String (take! (io))))
610- end
611443
612444function Random. rand (r:: AbstractRNG , :: SamplerType{X} ) where X <: FixedPoint
613445 X (rand (r, rawtype (X)), 0 )
0 commit comments