Skip to content

Commit ad288ff

Browse files
authored
Specialize sign-related functions (#187)
This also prohibits `copysign`/`flipsign` for (unsigned) `Normed`.
1 parent ba9d832 commit ad288ff

File tree

5 files changed

+58
-4
lines changed

5 files changed

+58
-4
lines changed

src/FixedPointNumbers.jl

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox,
66
zero, oneunit, one, typemin, typemax, floatmin, floatmax, eps, reinterpret,
77
big, rationalize, float, trunc, round, floor, ceil, bswap, clamp,
88
div, fld, rem, mod, mod1, fld1, min, max, minmax,
9+
signed, unsigned, copysign, flipsign, signbit,
910
rand, length
1011

1112
import Statistics # for _mean_promote
@@ -46,6 +47,9 @@ reinterpret(::Type{X}, x::T) where {T <: Integer, X <: FixedPoint{T}} = X(x, 0)
4647
nbitsfrac(::Type{X}) where {T, f, X <: FixedPoint{T,f}} = f
4748
rawtype(::Type{X}) where {T, X <: FixedPoint{T}} = T
4849

50+
# traits based on static parameters
51+
signbits(::Type{X}) where {T, X <: FixedPoint{T}} = T <: Unsigned ? 0 : 1
52+
4953
# construction using the (approximate) intended value, i.e., N0f8
5054
*(x::Real, ::Type{X}) where {X <: FixedPoint} = _convert(X, x)
5155

@@ -189,6 +193,30 @@ clamp(x::X, lo::X, hi::X) where {X <: FixedPoint} = X(clamp(x.i, lo.i, hi.i), 0)
189193

190194
clamp(x, ::Type{X}) where {X <: FixedPoint} = clamp(x, typemin(X), typemax(X)) % X
191195

196+
# Since `FixedPoint` is not an integer type, it is not clear in what type
197+
# `signed` and `unsigned` for `FixedPoint` should return values. They should
198+
# currently throw errors in case we support "unsigned Fixed" or "signed Normed"
199+
# in the future. The following "incomplete" code is necessary for Julia v1.0
200+
# etc. to prevent accidental conversion to an integer type.
201+
signed(x::X) where {X <: FixedPoint} = signed(X)(signed(x.i), 0)
202+
unsigned(x::X) where {X <: FixedPoint} = unsigned(X)(unsigned(x.i), 0)
203+
204+
function copysign(x::X, y::Real) where {T, X <: FixedPoint{T}}
205+
T <: Signed ? X(copysign(x.i, y), 0) : throw_not_a_signed_number_error(x)
206+
end
207+
function flipsign(x::X, y::Real) where {T, X <: FixedPoint{T}}
208+
T <: Signed ? X(flipsign(x.i, y), 0) : throw_not_a_signed_number_error(x)
209+
end
210+
if copysign(-1, 0x1) !== 1 # for Julia v1.0 and v1.1 (julia #30748)
211+
copysign(x::X, y::Unsigned) where {T, X <: FixedPoint{T}} = copysign(x, signed(y))
212+
flipsign(x::X, y::Unsigned) where {T, X <: FixedPoint{T}} = flipsign(x, signed(y))
213+
end
214+
@noinline function throw_not_a_signed_number_error(x)
215+
throw(ArgumentError("$x is not a signed number."))
216+
end
217+
218+
signbit(x::X) where {X <: FixedPoint} = signbit(x.i)
219+
192220
for f in (:zero, :oneunit, :one, :eps, :rawone, :rawtype, :floattype)
193221
@eval begin
194222
$f(x::FixedPoint) = $f(typeof(x))

src/fixed.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ struct Fixed{T <: Signed, f} <: FixedPoint{T, f}
2525
end
2626

2727
typechar(::Type{X}) where {X <: Fixed} = 'Q'
28-
signbits(::Type{X}) where {X <: Fixed} = 1
2928

3029
for T in (Int8, Int16, Int32, Int64)
3130
io = IOBuffer()

src/normed.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ struct Normed{T <: Unsigned, f} <: FixedPoint{T, f}
2020
end
2121

2222
typechar(::Type{X}) where {X <: Normed} = 'N'
23-
signbits(::Type{X}) where {X <: Normed} = 0
2423

2524
for T in (UInt8, UInt16, UInt32, UInt64)
2625
io = IOBuffer()
@@ -248,8 +247,6 @@ Base.BigFloat(x::Normed) = reinterpret(x) / BigFloat(rawone(x))
248247

249248
Base.Rational(x::Normed) = reinterpret(x)//rawone(x)
250249

251-
abs(x::Normed) = x
252-
253250
# unchecked arithmetic
254251
*(x::T, y::T) where {T <: Normed} = convert(T,convert(floattype(T), x)*convert(floattype(T), y))
255252
/(x::T, y::T) where {T <: Normed} = convert(T,convert(floattype(T), x)/convert(floattype(T), y))

test/fixed.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,23 @@ end
419419
@test clamp(1.5Q1f6, Q0f7) === 0.992Q0f7
420420
end
421421

422+
@testset "sign-related functions" begin
423+
@test_throws Exception signed(Q0f7)
424+
@test_throws Exception signed(0.5Q0f7)
425+
@test_throws Exception unsigned(Q0f7)
426+
@test_throws Exception unsigned(0.5Q0f7)
427+
@test copysign(0.5Q0f7, 0x1) === 0.5Q0f7
428+
@test copysign(0.5Q0f7, -1) === -0.5Q0f7
429+
@test flipsign(0.5Q0f7, 0x1) === 0.5Q0f7
430+
@test flipsign(0.5Q0f7, -1) === -0.5Q0f7
431+
@test_throws ArgumentError sign(0Q0f7)
432+
@test sign(0Q1f6) === 0Q1f6
433+
@test sign(0.5Q1f6) === 1Q1f6
434+
@test sign(-0.5Q1f6) === -1Q1f6
435+
@test signbit(0.5Q0f7) === false
436+
@test signbit(-0.5Q0f7) === true
437+
end
438+
422439
@testset "Promotion within Fixed" begin
423440
@test @inferred(promote(Q0f7(0.25), Q0f7(0.75))) ===
424441
(Q0f7(0.25), Q0f7(0.75))

test/normed.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,19 @@ end
351351
@test clamp(2.0N1f7, N0f8) === 1.0N0f8
352352
end
353353

354+
@testset "sign-related functions" begin
355+
@test_throws Exception signed(N0f8)
356+
@test_throws Exception signed(1N0f8)
357+
@test_throws Exception unsigned(N0f8)
358+
@test_throws Exception unsigned(1N0f8)
359+
@test_throws ArgumentError copysign(1N0f8, 0x1)
360+
@test_throws ArgumentError copysign(1N0f8, -1)
361+
@test_throws ArgumentError flipsign(1N0f8, 0x1)
362+
@test_throws ArgumentError flipsign(1N0f8, -1)
363+
@test_throws ArgumentError sign(0N0f8)
364+
@test signbit(1N0f8) === false
365+
end
366+
354367
@testset "unit range" begin
355368
@test length(N0f8(0):N0f8(1)) == 2
356369
@test length(N0f8(1):N0f8(0)) == 0

0 commit comments

Comments
 (0)