Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion src/FixedPointNumbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export
# Functions
scaledual,
wrapping_neg, wrapping_abs, wrapping_add, wrapping_sub, wrapping_mul,
saturating_neg, saturating_abs, saturating_add, saturating_sub, saturating_mul
wrapping_fdiv,
saturating_neg, saturating_abs, saturating_add, saturating_sub, saturating_mul,
saturating_fdiv,
checked_fdiv

include("utilities.jl")

Expand Down Expand Up @@ -207,6 +210,10 @@ wrapping_abs(x::X) where {X <: FixedPoint} = X(abs(x.i), 0)
wrapping_add(x::X, y::X) where {X <: FixedPoint} = X(x.i + y.i, 0)
wrapping_sub(x::X, y::X) where {X <: FixedPoint} = X(x.i - y.i, 0)
wrapping_mul(x::X, y::X) where {X <: FixedPoint} = (float(x) * float(y)) % X
function wrapping_fdiv(x::X, y::X) where {X <: FixedPoint}
z = floattype(X)(x.i) / floattype(X)(y.i)
isfinite(z) ? z % X : zero(X)
end

# saturating arithmetic
saturating_neg(x::X) where {X <: FixedPoint} = X(~min(x.i - true, x.i), 0)
Expand All @@ -225,6 +232,9 @@ saturating_sub(x::X, y::X) where {X <: FixedPoint{<:Unsigned}} = X(x.i - min(x.i

saturating_mul(x::X, y::X) where {X <: FixedPoint} = clamp(float(x) * float(y), X)

saturating_fdiv(x::X, y::X) where {X <: FixedPoint} =
clamp(floattype(X)(x.i) / floattype(X)(y.i), X)

# checked arithmetic
checked_neg(x::X) where {X <: FixedPoint} = checked_sub(zero(X), x)
function checked_abs(x::X) where {X <: FixedPoint}
Expand All @@ -248,6 +258,16 @@ function checked_mul(x::X, y::X) where {X <: FixedPoint}
typemin(X) - eps(X)/2 <= z < typemax(X) + eps(X)/2 || throw_overflowerror(:*, x, y)
z % X
end
function checked_fdiv(x::X, y::X) where {T, X <: FixedPoint{T}}
y === zero(X) && throw(DivideError())
z = floattype(X)(x.i) / floattype(X)(y.i)
if T <: Unsigned
z < typemax(X) + eps(X)/2 || throw_overflowerror(:/, x, y)
else
typemin(X) - eps(X)/2 <= z < typemax(X) + eps(X)/2 || throw_overflowerror(:/, x, y)
end
z % X
end

# default arithmetic
const DEFAULT_ARITHMETIC = :wrapping
Expand All @@ -264,6 +284,7 @@ for (op, name) in ((:+, :add), (:-, :sub), (:*, :mul))
$op(x::X, y::X) where {X <: FixedPoint} = $f(x, y)
end
end
/(x::X, y::X) where {X <: FixedPoint} = checked_fdiv(x, y) # force checked arithmetic


function minmax(x::X, y::X) where {X <: FixedPoint}
Expand Down
1 change: 0 additions & 1 deletion src/fixed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ function mul_with_rounding(x::F, y::F, ::RoundingMode{:Down}) where
F((widemul(x.i, y.i) >> f) % T, 0)
end

/(x::Fixed{T,f}, y::Fixed{T,f}) where {T,f} = Fixed{T,f}(div(convert(widen(T), x.i) << f, y.i), 0)

function trunc(x::Fixed{T,f}) where {T, f}
f == 0 && return x
Expand Down
1 change: 0 additions & 1 deletion src/normed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ end
# Override the default arithmetic with `checked` for backward compatibility
*(x::N, y::N) where {N <: Normed} = checked_mul(x, y)

/(x::T, y::T) where {T <: Normed} = convert(T,convert(floattype(T), x)/convert(floattype(T), y))

# Functions
trunc(x::N) where {N <: Normed} = floor(x)
Expand Down
12 changes: 12 additions & 0 deletions test/common.jl
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,18 @@ function test_mul(TX::Type)
end
end

function test_fdiv(TX::Type)
for X in target(TX, :i8; ex = :thin)
xys = xypairs(X)
fdiv(x, y) = oftype(float(x), big(x) / big(y))
fdivz(x, y) = y === zero(y) ? float(y) : fdiv(x, y)
@test all(((x, y),) -> wrapping_fdiv(x, y) === fdivz(x, y) % X, xys)
@test all(((x, y),) -> saturating_fdiv(x, y) === clamp(fdiv(x, y), X), xys)
@test all(((x, y),) -> !(typemin(X) <= fdiv(x, y) <= typemax(X)) ||
wrapping_fdiv(x, y) === checked_fdiv(x, y), xys)
end
end

function test_isapprox(TX::Type)
@testset "approx $X" for X in target(TX, :i8, :i16; ex = :light)
xs = typemin(X):eps(X):typemax(X)-eps(X)
Expand Down
25 changes: 25 additions & 0 deletions test/fixed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,31 @@ end
FixedPointNumbers.mul_with_rounding(1.5Q6f1, -0.5Q6f1, RoundDown) === -1.0Q6f1
end

@testset "fdiv" begin
for F in target(Fixed; ex = :thin)
@test wrapping_fdiv(typemax(F), -typemax(F)) === F(-1)
@test saturating_fdiv(typemax(F), -typemax(F)) === F(-1)
@test checked_fdiv(typemax(F), -typemax(F)) === F(-1)

@test wrapping_fdiv(zero(F), typemin(F)) === zero(F)
@test saturating_fdiv(zero(F), typemin(F)) === zero(F)
@test checked_fdiv(zero(F), typemin(F)) === zero(F)

@test wrapping_fdiv(typemin(F), F(-1)) === wrapping_neg(typemin(F))
@test saturating_fdiv(typemin(F), F(-1)) === typemax(F)
@test_throws OverflowError checked_fdiv(typemin(F), F(-1))

@test wrapping_fdiv(zero(F), zero(F)) === zero(F)
@test saturating_fdiv(zero(F), zero(F)) === zero(F)
@test_throws DivideError checked_fdiv(zero(F), zero(F))

@test wrapping_fdiv(-eps(F), zero(F)) === zero(F)
@test saturating_fdiv(-eps(F), zero(F)) === typemin(F)
@test_throws DivideError checked_fdiv(-eps(F), zero(F))
end
test_fdiv(Fixed)
end

@testset "rounding" begin
for sym in (:i8, :i16, :i32, :i64)
T = symbol_to_inttype(Fixed, sym)
Expand Down
25 changes: 25 additions & 0 deletions test/normed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,31 @@ end
test_mul(Normed)
end

@testset "fdiv" begin
for N in target(Normed; ex = :thin)
@test wrapping_fdiv(typemax(N), typemax(N)) === one(N)
@test saturating_fdiv(typemax(N), typemax(N)) === one(N)
@test checked_fdiv(typemax(N), typemax(N)) === one(N)

@test wrapping_fdiv(zero(N), eps(N)) === zero(N)
@test saturating_fdiv(zero(N), eps(N)) === zero(N)
@test checked_fdiv(zero(N), eps(N)) === zero(N)

@test wrapping_fdiv(typemax(N), eps(N)) === (floattype(N))(typemax(rawtype(N))) % N
@test saturating_fdiv(typemax(N), eps(N)) === typemax(N)
@test_throws OverflowError checked_fdiv(typemax(N), eps(N))

@test wrapping_fdiv(zero(N), zero(N)) === zero(N)
@test saturating_fdiv(zero(N), zero(N)) === zero(N)
@test_throws DivideError checked_fdiv(zero(N), zero(N))

@test wrapping_fdiv(eps(N), zero(N)) === zero(N)
@test saturating_fdiv(eps(N), zero(N)) === typemax(N)
@test_throws DivideError checked_fdiv(eps(N), zero(N))
end
test_fdiv(Normed)
end

@testset "div/fld1" begin
@test div(reinterpret(N0f8, 0x10), reinterpret(N0f8, 0x02)) == fld(reinterpret(N0f8, 0x10), reinterpret(N0f8, 0x02)) == 8
@test div(reinterpret(N0f8, 0x0f), reinterpret(N0f8, 0x02)) == fld(reinterpret(N0f8, 0x0f), reinterpret(N0f8, 0x02)) == 7
Expand Down