Skip to content

Commit 929bbf7

Browse files
committed
Move arithmetic functions into submodule FixedPointArithmetic
This moves only the definitions, leaving the implementations outside of `FixedPointArithmetic`. This also adds new `unchecked_*` functions. Currently, `unchecked_*` falls back to `wrapping_*`. This makes `wrapping_*` and `saturating_*` functions unexported.
1 parent 3242f10 commit 929bbf7

File tree

5 files changed

+180
-40
lines changed

5 files changed

+180
-40
lines changed

src/FixedPointNumbers.jl

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
module FixedPointNumbers
22

3-
import Base: ==, <, <=, -, +, *, /, ~, isapprox,
3+
# arithmetic functions (operators) are imported in "arithmetic.jl"
4+
import Base: ==, <, <=, ~, isapprox,
45
convert, promote_rule, print, show, bitstring, abs, decompose,
56
isnan, isinf, isfinite, isinteger,
67
zero, oneunit, one, typemin, typemax, floatmin, floatmax, eps, reinterpret,
78
big, rationalize, float, trunc, round, floor, ceil, bswap, clamp,
8-
div, fld, cld, rem, mod, mod1, fld1, min, max, minmax,
9+
mod1, fld1, min, max, minmax,
910
signed, unsigned, copysign, flipsign, signbit,
1011
length
1112

1213
import Random: Random, AbstractRNG, SamplerType, rand!
1314

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-
1715
using Base: @pure
1816

1917
"""
@@ -26,7 +24,6 @@ of fraction bits.
2624
"""
2725
abstract type FixedPoint{T <: Integer, f} <: Real end
2826

29-
3027
export
3128
FixedPoint,
3229
Fixed,
@@ -35,12 +32,18 @@ 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
36+
37+
include("arithmetic.jl")
38+
39+
import .FixedPointArithmetic
40+
import .FixedPointArithmetic: Wrapping, Saturating, Checked, Unchecked
41+
42+
for modname in (:Wrapping, :Saturating, :Checked, :Unchecked)
43+
for name in names(getproperty(FixedPointArithmetic, modname))
44+
@eval import .$modname: $name
45+
end
46+
end
4447

4548
include("utilities.jl")
4649

@@ -58,16 +61,18 @@ signbits(::Type{X}) where {T, X <: FixedPoint{T}} = T <: Unsigned ? 0 : 1
5861
nbitsint(::Type{X}) where {X <: FixedPoint} = bitwidth(X) - nbitsfrac(X) - signbits(X)
5962

6063
# construction using the (approximate) intended value, i.e., N0f8
61-
*(x::Real, ::Type{X}) where {X <: FixedPoint} = _convert(X, x)
64+
Base.:*(x::Real, ::Type{X}) where {X <: FixedPoint} = _convert(X, x)
6265
wrapping_mul(x::Real, ::Type{X}) where {X <: FixedPoint} = x % X
6366
saturating_mul(x::Real, ::Type{X}) where {X <: FixedPoint} = clamp(x, X)
6467
checked_mul(x::Real, ::Type{X}) where {X <: FixedPoint} = _convert(X, x)
68+
unchecked_mul(x::Real, ::Type{X}) where {X <: FixedPoint} = x % X
6569

6670
# type modulus
67-
rem(x::Real, ::Type{X}) where {X <: FixedPoint} = _rem(x, X)
71+
Base.rem(x::Real, ::Type{X}) where {X <: FixedPoint} = _rem(x, X)
6872
wrapping_rem(x::Real, ::Type{X}) where {X <: FixedPoint} = _rem(x, X)
6973
saturating_rem(x::Real, ::Type{X}) where {X <: FixedPoint} = _rem(x, X)
7074
checked_rem(x::Real, ::Type{X}) where {X <: FixedPoint} = _rem(x, X)
75+
unchecked_rem(x::Real, ::Type{X}) where {X <: FixedPoint} = _rem(x, X)
7176

7277
# constructor-style conversions
7378
(::Type{X})(x::X) where {X <: FixedPoint} = x
@@ -325,31 +330,6 @@ function checked_rem(x::X, y::X, r::RoundingMode = RoundToZero) where {T, X <: F
325330
end
326331
checked_mod(x::X, y::X) where {X <: FixedPoint} = checked_rem(x, y, RoundDown)
327332

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-
353333
function minmax(x::X, y::X) where {X <: FixedPoint}
354334
a, b = minmax(reinterpret(x), reinterpret(y))
355335
X(a,0), X(b,0)

src/arithmetic.jl

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
module FixedPointArithmetic
2+
3+
using ..FixedPointNumbers
4+
5+
import Base: -, +, *, /, abs, div, fld, cld, rem, mod
6+
7+
module Wrapping
8+
9+
export wrapping_neg, wrapping_abs, wrapping_add, wrapping_sub, wrapping_mul,
10+
wrapping_div, wrapping_fld, wrapping_cld, wrapping_rem, wrapping_mod,
11+
wrapping_fdiv
12+
13+
for name in names(Wrapping)
14+
startswith(string(name), "wrapping_") || continue
15+
@eval function $name end
16+
end
17+
18+
end # module Wrapping
19+
20+
module Saturating
21+
22+
export saturating_neg, saturating_abs, saturating_add, saturating_sub, saturating_mul,
23+
saturating_div, saturating_fld, saturating_cld, saturating_rem, saturating_mod,
24+
saturating_fdiv
25+
26+
for name in names(Saturating)
27+
startswith(string(name), "saturating_") || continue
28+
@eval function $name end
29+
end
30+
31+
end # module Saturating
32+
33+
module Checked
34+
35+
import Base.Checked: checked_neg, checked_abs, checked_add, checked_sub, checked_mul,
36+
checked_div, checked_fld, checked_cld, checked_rem, checked_mod
37+
38+
export checked_neg, checked_abs, checked_add, checked_sub, checked_mul,
39+
checked_div, checked_fld, checked_cld, checked_rem, checked_mod,
40+
checked_fdiv
41+
42+
function checked_fdiv end
43+
44+
end # module Checked
45+
46+
module Unchecked
47+
48+
using ..FixedPointNumbers
49+
using ..Wrapping
50+
51+
export unchecked_neg, unchecked_abs, unchecked_add, unchecked_sub, unchecked_mul,
52+
unchecked_div, unchecked_fld, unchecked_cld, unchecked_rem, unchecked_mod,
53+
unchecked_fdiv
54+
55+
for name in (:neg, :abs)
56+
fu = Symbol(:unchecked_, name)
57+
fw = Symbol(:wrapping_, name)
58+
@eval begin
59+
$fu(x::X) where {X <: FixedPoint} = $fw(x)
60+
end
61+
end
62+
for name in (:add, :sub, :mul, :div, :fld, :cld, :rem, :mod, :fdiv)
63+
fu = Symbol(:unchecked_, name)
64+
fw = Symbol(:wrapping_, name)
65+
@eval begin
66+
$fu(x::X, y::X) where {X <: FixedPoint} = $fw(x, y)
67+
end
68+
name in (:div, :rem) || continue
69+
@eval begin
70+
$fu(x::X, y::X, r::RoundingMode{M}) where {X <: FixedPoint, M} = $fw(x, y, r)
71+
end
72+
end
73+
74+
end # module Unchecked
75+
76+
using .Wrapping, .Saturating, .Checked, .Unchecked
77+
78+
# re-export
79+
for Mod in (Wrapping, Saturating, Checked, Unchecked)
80+
for name in names(Mod)
81+
@eval export $name
82+
end
83+
end
84+
85+
# default arithmetic
86+
const DEFAULT_ARITHMETIC = :wrapping
87+
88+
for (op, name) in ((:-, :neg), (:abs, :abs))
89+
f = Symbol(DEFAULT_ARITHMETIC, :_, name)
90+
@eval begin
91+
$op(x::X) where {X <: FixedPoint} = $f(x)
92+
end
93+
end
94+
for (op, name) in ((:+, :add), (:-, :sub), (:*, :mul))
95+
f = Symbol(DEFAULT_ARITHMETIC, :_, name)
96+
@eval begin
97+
$op(x::X, y::X) where {X <: FixedPoint} = $f(x, y)
98+
end
99+
end
100+
101+
const DEFAULT_DIV_ARITHMETIC = :checked
102+
103+
for name in (:fdiv, :div, :fld, :cld, :rem, :mod)
104+
f = Symbol(DEFAULT_DIV_ARITHMETIC, :_, name)
105+
if name === :fdiv
106+
@eval begin
107+
/(x::X, y::X) where {X <: FixedPoint} = $f(x, y)
108+
end
109+
continue
110+
end
111+
@eval begin
112+
$name(x::X, y::X) where {X <: FixedPoint} = $f(x, y)
113+
end
114+
name in (:div, :rem) || continue
115+
end
116+
117+
for m in (:(:Nearest), :(:ToZero), :(:Up), :(:Down))
118+
_div = Symbol(DEFAULT_DIV_ARITHMETIC, :_div)
119+
_rem = Symbol(DEFAULT_DIV_ARITHMETIC, :_rem)
120+
@eval begin
121+
div(x::X, y::X, r::RoundingMode{$m}) where {X <: FixedPoint} = $_div(x, y, r)
122+
rem(x::X, y::X, r::RoundingMode{$m}) where {X <: FixedPoint} = $_rem(x, y, r)
123+
end
124+
end
125+
126+
end # module FixedPointArithmetic

test/common.jl

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using FixedPointNumbers, Statistics, Random, StableRNGs, Test
22
using FixedPointNumbers: bitwidth, rawtype, nbitsfrac
3+
using FixedPointNumbers.FixedPointArithmetic
34
using Base.Checked
45

56
SP = VERSION >= v"1.6.0-DEV.771" ? " " : "" # JuliaLang/julia #37085
@@ -157,7 +158,8 @@ function test_rem_type(TX::Type)
157158
@testset "% $X" for X in target(TX, :i8, :i16; ex = :thin)
158159
xs = typemin(X):0.1:typemax(X)
159160
@test all(x -> x % X === X(x), xs)
160-
@test wrapping_rem(2, X) === saturating_rem(2, X) === checked_rem(2, X) === 2 % X
161+
@test wrapping_rem(2, X) === saturating_rem(2, X) ===
162+
checked_rem(2, X) === unchecked_rem(2, X) === 2 % X
161163
end
162164
end
163165

@@ -168,6 +170,28 @@ function test_rem_nan(TX::Type)
168170
end
169171
end
170172

173+
function test_unchecked(TX::Type)
174+
for X in target(TX, :i8, :i16, :i32, :i64; ex = :thin)
175+
xs = (typemin(X), eps(X))
176+
ys = (typemax(X), zero(X))
177+
@test all(unchecked_neg.(xs) === wrapping_neg.(xs))
178+
@test all(unchecked_abs.(xs) === wrapping_abs.(xs))
179+
@test all(unchecked_add.(xs, ys) === wrapping_add.(xs, ys))
180+
@test all(unchecked_sub.(xs, ys) === wrapping_sub.(xs, ys))
181+
@test all(unchecked_mul.(xs, ys) === wrapping_mul.(xs, ys))
182+
@test all(unchecked_div.(xs, ys) === wrapping_div.(xs, ys))
183+
@test all(unchecked_fld.(xs, ys) === wrapping_fld.(xs, ys))
184+
@test all(unchecked_cld.(xs, ys) === wrapping_cld.(xs, ys))
185+
@test all(unchecked_rem.(xs, ys) === wrapping_rem.(xs, ys))
186+
@test all(unchecked_mod.(xs, ys) === wrapping_mod.(xs, ys))
187+
@test all(unchecked_fdiv.(xs, ys) === wrapping_fdiv.(xs, ys))
188+
for r in (RoundNearest, RoundToZero, RoundUp, RoundDown)
189+
@test all(unchecked_div.(xs, ys, r) === wrapping_div.(xs, ys, r))
190+
@test all(unchecked_rem.(xs, ys, r) === wrapping_rem.(xs, ys, r))
191+
end
192+
end
193+
end
194+
171195
function test_neg(TX::Type)
172196
for X in target(TX, :i8; ex = :thin)
173197
xs = typemin(X):eps(X):typemax(X)

test/fixed.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ end
109109
@test saturating_mul(1.635, Q0f7) === Q0f7(0.992)
110110
@test checked_mul(0.635, Q0f7) === Q0f7(0.635)
111111
@test_throws ArgumentError checked_mul(1.635, Q0f7)
112+
@test unchecked_mul(1.635, Q0f7) === wrapping_mul(1.635, Q0f7)
112113
end
113114

114115
@testset "reinterpret/bitstring" begin
@@ -312,6 +313,10 @@ end
312313
@test -2 % Q0f7 === Q0f7(0)
313314
end
314315

316+
@testset "unchecked arithmetic" begin
317+
test_unchecked(Fixed)
318+
end
319+
315320
@testset "neg" begin
316321
for F in target(Fixed; ex = :thin)
317322
@test wrapping_neg(typemin(F)) === typemin(F)

test/normed.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ end
5757
@test saturating_mul(1.635, N0f8) === N0f8(1.0)
5858
@test checked_mul(0.635, N0f8) === N0f8(0.635)
5959
@test_throws ArgumentError checked_mul(1.635, N0f8)
60+
@test unchecked_mul(1.635, N0f8) === wrapping_mul(1.635, N0f8)
6061
end
6162

6263
@testset "reinterpret/bitstring" begin
@@ -328,6 +329,10 @@ end
328329
end
329330
end
330331

332+
@testset "unchecked arithmetic" begin
333+
test_unchecked(Normed)
334+
end
335+
331336
@testset "neg" begin
332337
for N in target(Normed; ex = :thin)
333338
@test wrapping_neg(typemin(N)) === zero(N)

0 commit comments

Comments
 (0)