Skip to content

Commit 076f55b

Browse files
committed
Add checked_abs, wrapping_abs and saturating_abs
1 parent 5dcaf97 commit 076f55b

File tree

3 files changed

+60
-5
lines changed

3 files changed

+60
-5
lines changed

src/FixedPointNumbers.jl

Lines changed: 20 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_add, checked_sub, checked_abs, 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_add, wrapping_sub, wrapping_abs, wrapping_mul,
41+
saturating_neg, saturating_add, saturating_sub, saturating_abs, saturating_mul
4142

4243
include("utilities.jl")
4344

@@ -189,6 +190,7 @@ float(x::FixedPoint) = convert(floattype(x), x)
189190
wrapping_neg(x::X) where {X <: FixedPoint} = X(-x.i, 0)
190191
wrapping_add(x::X, y::X) where {X <: FixedPoint} = X(x.i + y.i, 0)
191192
wrapping_sub(x::X, y::X) where {X <: FixedPoint} = X(x.i - y.i, 0)
193+
wrapping_abs(x::X) where {X <: FixedPoint} = X(abs(x.i), 0)
192194

193195
# saturating arithmetic
194196
saturating_neg(x::X) where {X <: FixedPoint} = X(~min(x.i - true, x.i), 0)
@@ -202,6 +204,9 @@ saturating_sub(x::X, y::X) where {X <: FixedPoint} =
202204
X(x.i - ifelse(x.i < 0, min(y.i, x.i - typemin(x.i)), max(y.i, x.i - typemax(x.i))), 0)
203205
saturating_sub(x::X, y::X) where {X <: FixedPoint{<:Unsigned}} = X(x.i - min(x.i, y.i), 0)
204206

207+
saturating_abs(x::X) where {X <: FixedPoint} =
208+
X(ifelse(signbit(abs(x.i)), typemax(x.i), abs(x.i)), 0)
209+
205210
# checked arithmetic
206211
checked_neg(x::X) where {X <: FixedPoint} = checked_sub(zero(X), x)
207212
function checked_add(x::X, y::X) where {X <: FixedPoint}
@@ -216,11 +221,15 @@ function checked_sub(x::X, y::X) where {X <: FixedPoint}
216221
f && throw_overflowerror(:-, x, y)
217222
z
218223
end
224+
function checked_abs(x::X) where {X <: FixedPoint}
225+
abs(x.i) >= 0 || throw_overflowerror_abs(x)
226+
X(abs(x.i), 0)
227+
end
219228

220229
# default arithmetic
221230
const DEFAULT_ARITHMETIC = :wrapping
222231

223-
for (op, name) in ((:-, :neg), )
232+
for (op, name) in ((:-, :neg), (:abs, :abs))
224233
f = Symbol(DEFAULT_ARITHMETIC, :_, name)
225234
@eval begin
226235
$op(x::X) where {X <: FixedPoint} = $f(x)
@@ -283,7 +292,7 @@ for f in (:(==), :<, :<=, :div, :fld, :fld1)
283292
$f(x::X, y::X) where {X <: FixedPoint} = $f(x.i, y.i)
284293
end
285294
end
286-
for f in (:~, :abs)
295+
for f in (:~, )
287296
@eval begin
288297
$f(x::X) where {X <: FixedPoint} = X($f(x.i), 0)
289298
end
@@ -443,6 +452,12 @@ end
443452
showtype(io, typeof(x))
444453
throw(OverflowError(String(take!(io))))
445454
end
455+
@noinline function throw_overflowerror_abs(@nospecialize(x))
456+
io = IOBuffer()
457+
print(io, "abs(", x, ") overflowed for type ")
458+
showtype(io, typeof(x))
459+
throw(OverflowError(String(take!(io))))
460+
end
446461

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

test/fixed.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,26 @@ end
349349
end
350350
end
351351

352+
@testset "abs" begin
353+
for F in target(Fixed; ex = :thin)
354+
@test wrapping_abs(typemax(F)) === typemax(F)
355+
@test saturating_abs(typemax(F)) === typemax(F)
356+
@test checked_abs(typemax(F)) === typemax(F)
357+
358+
@test wrapping_abs(typemin(F)) === typemin(F)
359+
@test saturating_abs(typemin(F)) === typemax(F)
360+
@test_throws OverflowError checked_abs(typemin(F))
361+
end
362+
for F in target(Fixed, :i8; ex = :thin)
363+
xs = typemin(F):eps(F):typemax(F)
364+
fabs(x) = abs(float(x))
365+
@test all(x -> wrapping_abs(x) === (x > 0 ? x : wrapping_neg(x)), xs)
366+
@test all(x -> saturating_abs(x) === clamp(fabs(x), F), xs)
367+
@test all(x -> !(typemin(F) <= fabs(x) <= typemax(F)) ||
368+
wrapping_abs(x) === checked_abs(x) === fabs(x) % F, xs)
369+
end
370+
end
371+
352372
@testset "rounding" begin
353373
for sym in (:i8, :i16, :i32, :i64)
354374
T = symbol_to_inttype(Fixed, sym)

test/normed.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,26 @@ end
374374
end
375375
end
376376

377+
@testset "abs" begin
378+
for N in target(Normed; ex = :thin)
379+
@test wrapping_abs(typemax(N)) === typemax(N)
380+
@test saturating_abs(typemax(N)) === typemax(N)
381+
@test checked_abs(typemax(N)) === typemax(N)
382+
383+
@test wrapping_abs(typemin(N)) === typemin(N)
384+
@test saturating_abs(typemin(N)) === typemin(N)
385+
@test checked_abs(typemin(N)) === typemin(N)
386+
end
387+
for N in target(Normed, :i8; ex = :thin)
388+
xs = typemin(N):eps(N):typemax(N)
389+
fabs(x) = abs(float(x))
390+
@test all(x -> wrapping_abs(x) === (x > 0 ? x : wrapping_neg(x)), xs)
391+
@test all(x -> saturating_abs(x) === clamp(fabs(x), N), xs)
392+
@test all(x -> !(typemin(N) <= fabs(x) <= typemax(N)) ||
393+
wrapping_abs(x) === checked_abs(x) === fabs(x) % N, xs)
394+
end
395+
end
396+
377397
@testset "div/fld1" begin
378398
@test div(reinterpret(N0f8, 0x10), reinterpret(N0f8, 0x02)) == fld(reinterpret(N0f8, 0x10), reinterpret(N0f8, 0x02)) == 8
379399
@test div(reinterpret(N0f8, 0x0f), reinterpret(N0f8, 0x02)) == fld(reinterpret(N0f8, 0x0f), reinterpret(N0f8, 0x02)) == 7

0 commit comments

Comments
 (0)