Skip to content

Commit 5794adf

Browse files
authored
Add checked_abs, wrapping_abs and saturating_abs (#217)
1 parent a22506b commit 5794adf

File tree

3 files changed

+61
-5
lines changed

3 files changed

+61
-5
lines changed

src/FixedPointNumbers.jl

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox,
1212
import Statistics # for _mean_promote
1313
import Random: Random, AbstractRNG, SamplerType, rand!
1414

15-
import Base.Checked: checked_neg, checked_add, checked_sub, checked_mul, checked_div
15+
import Base.Checked: checked_neg, checked_abs, checked_add, checked_sub, checked_mul,
16+
checked_div
1617

1718
using Base: @pure
1819

@@ -36,8 +37,8 @@ export
3637
# Q and N typealiases are exported in separate source files
3738
# Functions
3839
scaledual,
39-
wrapping_neg, wrapping_add, wrapping_sub, wrapping_mul,
40-
saturating_neg, saturating_add, saturating_sub, saturating_mul
40+
wrapping_neg, wrapping_abs, wrapping_add, wrapping_sub, wrapping_mul,
41+
saturating_neg, saturating_abs, saturating_add, saturating_sub, saturating_mul
4142

4243
include("utilities.jl")
4344

@@ -202,13 +203,17 @@ float(x::FixedPoint) = convert(floattype(x), x)
202203

203204
# wrapping arithmetic
204205
wrapping_neg(x::X) where {X <: FixedPoint} = X(-x.i, 0)
206+
wrapping_abs(x::X) where {X <: FixedPoint} = X(abs(x.i), 0)
205207
wrapping_add(x::X, y::X) where {X <: FixedPoint} = X(x.i + y.i, 0)
206208
wrapping_sub(x::X, y::X) where {X <: FixedPoint} = X(x.i - y.i, 0)
207209

208210
# saturating arithmetic
209211
saturating_neg(x::X) where {X <: FixedPoint} = X(~min(x.i - true, x.i), 0)
210212
saturating_neg(x::X) where {X <: FixedPoint{<:Unsigned}} = zero(X)
211213

214+
saturating_abs(x::X) where {X <: FixedPoint} =
215+
X(ifelse(signbit(abs(x.i)), typemax(x.i), abs(x.i)), 0)
216+
212217
saturating_add(x::X, y::X) where {X <: FixedPoint} =
213218
X(x.i + ifelse(x.i < 0, max(y.i, typemin(x.i) - x.i), min(y.i, typemax(x.i) - x.i)), 0)
214219
saturating_add(x::X, y::X) where {X <: FixedPoint{<:Unsigned}} = X(x.i + min(~x.i, y.i), 0)
@@ -217,8 +222,13 @@ saturating_sub(x::X, y::X) where {X <: FixedPoint} =
217222
X(x.i - ifelse(x.i < 0, min(y.i, x.i - typemin(x.i)), max(y.i, x.i - typemax(x.i))), 0)
218223
saturating_sub(x::X, y::X) where {X <: FixedPoint{<:Unsigned}} = X(x.i - min(x.i, y.i), 0)
219224

225+
220226
# checked arithmetic
221227
checked_neg(x::X) where {X <: FixedPoint} = checked_sub(zero(X), x)
228+
function checked_abs(x::X) where {X <: FixedPoint}
229+
abs(x.i) >= 0 || throw_overflowerror_abs(x)
230+
X(abs(x.i), 0)
231+
end
222232
function checked_add(x::X, y::X) where {X <: FixedPoint}
223233
r, f = Base.Checked.add_with_overflow(x.i, y.i)
224234
z = X(r, 0) # store first
@@ -235,7 +245,7 @@ end
235245
# default arithmetic
236246
const DEFAULT_ARITHMETIC = :wrapping
237247

238-
for (op, name) in ((:-, :neg), )
248+
for (op, name) in ((:-, :neg), (:abs, :abs))
239249
f = Symbol(DEFAULT_ARITHMETIC, :_, name)
240250
@eval begin
241251
$op(x::X) where {X <: FixedPoint} = $f(x)
@@ -298,7 +308,7 @@ for f in (:(==), :<, :<=, :div, :fld, :fld1)
298308
$f(x::X, y::X) where {X <: FixedPoint} = $f(x.i, y.i)
299309
end
300310
end
301-
for f in (:~, :abs)
311+
for f in (:~, )
302312
@eval begin
303313
$f(x::X) where {X <: FixedPoint} = X($f(x.i), 0)
304314
end
@@ -458,6 +468,12 @@ end
458468
showtype(io, typeof(x))
459469
throw(OverflowError(String(take!(io))))
460470
end
471+
@noinline function throw_overflowerror_abs(@nospecialize(x))
472+
io = IOBuffer()
473+
print(io, "abs(", x, ") overflowed for type ")
474+
showtype(io, typeof(x))
475+
throw(OverflowError(String(take!(io))))
476+
end
461477

462478
function Random.rand(r::AbstractRNG, ::SamplerType{X}) where X <: FixedPoint
463479
X(rand(r, rawtype(X)), 0)

test/fixed.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,26 @@ end
302302
end
303303
end
304304

305+
@testset "abs" begin
306+
for F in target(Fixed; ex = :thin)
307+
@test wrapping_abs(typemax(F)) === typemax(F)
308+
@test saturating_abs(typemax(F)) === typemax(F)
309+
@test checked_abs(typemax(F)) === typemax(F)
310+
311+
@test wrapping_abs(typemin(F)) === typemin(F)
312+
@test saturating_abs(typemin(F)) === typemax(F)
313+
@test_throws OverflowError checked_abs(typemin(F))
314+
end
315+
for F in target(Fixed, :i8; ex = :thin)
316+
xs = typemin(F):eps(F):typemax(F)
317+
fabs(x) = abs(float(x))
318+
@test all(x -> wrapping_abs(x) === (x > 0 ? x : wrapping_neg(x)), xs)
319+
@test all(x -> saturating_abs(x) === clamp(fabs(x), F), xs)
320+
@test all(x -> !(typemin(F) <= fabs(x) <= typemax(F)) ||
321+
wrapping_abs(x) === checked_abs(x) === fabs(x) % F, xs)
322+
end
323+
end
324+
305325
@testset "add" begin
306326
for F in target(Fixed; ex = :thin)
307327
@test wrapping_add(typemin(F), typemin(F)) === zero(F)

test/normed.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,26 @@ end
323323
end
324324
end
325325

326+
@testset "abs" begin
327+
for N in target(Normed; ex = :thin)
328+
@test wrapping_abs(typemax(N)) === typemax(N)
329+
@test saturating_abs(typemax(N)) === typemax(N)
330+
@test checked_abs(typemax(N)) === typemax(N)
331+
332+
@test wrapping_abs(typemin(N)) === typemin(N)
333+
@test saturating_abs(typemin(N)) === typemin(N)
334+
@test checked_abs(typemin(N)) === typemin(N)
335+
end
336+
for N in target(Normed, :i8; ex = :thin)
337+
xs = typemin(N):eps(N):typemax(N)
338+
fabs(x) = abs(float(x))
339+
@test all(x -> wrapping_abs(x) === (x > 0 ? x : wrapping_neg(x)), xs)
340+
@test all(x -> saturating_abs(x) === clamp(fabs(x), N), xs)
341+
@test all(x -> !(typemin(N) <= fabs(x) <= typemax(N)) ||
342+
wrapping_abs(x) === checked_abs(x) === fabs(x) % N, xs)
343+
end
344+
end
345+
326346
@testset "add" begin
327347
for N in target(Normed; ex = :thin)
328348
@test wrapping_add(typemin(N), typemin(N)) === zero(N)

0 commit comments

Comments
 (0)