diff --git a/src/FixedPointNumbers.jl b/src/FixedPointNumbers.jl index 85783f3c..7e4ab7d3 100644 --- a/src/FixedPointNumbers.jl +++ b/src/FixedPointNumbers.jl @@ -6,6 +6,7 @@ import Base: ==, <, <=, -, +, *, /, ~, isapprox, zero, oneunit, one, typemin, typemax, floatmin, floatmax, eps, reinterpret, big, rationalize, float, trunc, round, floor, ceil, bswap, clamp, div, fld, rem, mod, mod1, fld1, min, max, minmax, + signed, unsigned, copysign, flipsign, signbit, rand, length import Statistics # for _mean_promote @@ -46,6 +47,9 @@ reinterpret(::Type{X}, x::T) where {T <: Integer, X <: FixedPoint{T}} = X(x, 0) nbitsfrac(::Type{X}) where {T, f, X <: FixedPoint{T,f}} = f rawtype(::Type{X}) where {T, X <: FixedPoint{T}} = T +# traits based on static parameters +signbits(::Type{X}) where {T, X <: FixedPoint{T}} = T <: Unsigned ? 0 : 1 + # construction using the (approximate) intended value, i.e., N0f8 *(x::Real, ::Type{X}) where {X <: FixedPoint} = _convert(X, x) @@ -189,6 +193,30 @@ clamp(x::X, lo::X, hi::X) where {X <: FixedPoint} = X(clamp(x.i, lo.i, hi.i), 0) clamp(x, ::Type{X}) where {X <: FixedPoint} = clamp(x, typemin(X), typemax(X)) % X +# Since `FixedPoint` is not an integer type, it is not clear in what type +# `signed` and `unsigned` for `FixedPoint` should return values. They should +# currently throw errors in case we support "unsigned Fixed" or "signed Normed" +# in the future. The following "incomplete" code is necessary for Julia v1.0 +# etc. to prevent accidental conversion to an integer type. +signed(x::X) where {X <: FixedPoint} = signed(X)(signed(x.i), 0) +unsigned(x::X) where {X <: FixedPoint} = unsigned(X)(unsigned(x.i), 0) + +function copysign(x::X, y::Real) where {T, X <: FixedPoint{T}} + T <: Signed ? X(copysign(x.i, y), 0) : throw_not_a_signed_number_error(x) +end +function flipsign(x::X, y::Real) where {T, X <: FixedPoint{T}} + T <: Signed ? X(flipsign(x.i, y), 0) : throw_not_a_signed_number_error(x) +end +if copysign(-1, 0x1) !== 1 # for Julia v1.0 and v1.1 (julia #30748) + copysign(x::X, y::Unsigned) where {T, X <: FixedPoint{T}} = copysign(x, signed(y)) + flipsign(x::X, y::Unsigned) where {T, X <: FixedPoint{T}} = flipsign(x, signed(y)) +end +@noinline function throw_not_a_signed_number_error(x) + throw(ArgumentError("$x is not a signed number.")) +end + +signbit(x::X) where {X <: FixedPoint} = signbit(x.i) + for f in (:zero, :oneunit, :one, :eps, :rawone, :rawtype, :floattype) @eval begin $f(x::FixedPoint) = $f(typeof(x)) diff --git a/src/fixed.jl b/src/fixed.jl index d185e58c..b0245848 100644 --- a/src/fixed.jl +++ b/src/fixed.jl @@ -25,7 +25,6 @@ struct Fixed{T <: Signed, f} <: FixedPoint{T, f} end typechar(::Type{X}) where {X <: Fixed} = 'Q' -signbits(::Type{X}) where {X <: Fixed} = 1 for T in (Int8, Int16, Int32, Int64) io = IOBuffer() diff --git a/src/normed.jl b/src/normed.jl index b34ae065..6499dd69 100644 --- a/src/normed.jl +++ b/src/normed.jl @@ -20,7 +20,6 @@ struct Normed{T <: Unsigned, f} <: FixedPoint{T, f} end typechar(::Type{X}) where {X <: Normed} = 'N' -signbits(::Type{X}) where {X <: Normed} = 0 for T in (UInt8, UInt16, UInt32, UInt64) io = IOBuffer() @@ -248,8 +247,6 @@ Base.BigFloat(x::Normed) = reinterpret(x) / BigFloat(rawone(x)) Base.Rational(x::Normed) = reinterpret(x)//rawone(x) -abs(x::Normed) = x - # unchecked arithmetic *(x::T, y::T) where {T <: Normed} = convert(T,convert(floattype(T), x)*convert(floattype(T), y)) /(x::T, y::T) where {T <: Normed} = convert(T,convert(floattype(T), x)/convert(floattype(T), y)) diff --git a/test/fixed.jl b/test/fixed.jl index 1c8e7a4a..1c958a01 100644 --- a/test/fixed.jl +++ b/test/fixed.jl @@ -419,6 +419,23 @@ end @test clamp(1.5Q1f6, Q0f7) === 0.992Q0f7 end +@testset "sign-related functions" begin + @test_throws Exception signed(Q0f7) + @test_throws Exception signed(0.5Q0f7) + @test_throws Exception unsigned(Q0f7) + @test_throws Exception unsigned(0.5Q0f7) + @test copysign(0.5Q0f7, 0x1) === 0.5Q0f7 + @test copysign(0.5Q0f7, -1) === -0.5Q0f7 + @test flipsign(0.5Q0f7, 0x1) === 0.5Q0f7 + @test flipsign(0.5Q0f7, -1) === -0.5Q0f7 + @test_throws ArgumentError sign(0Q0f7) + @test sign(0Q1f6) === 0Q1f6 + @test sign(0.5Q1f6) === 1Q1f6 + @test sign(-0.5Q1f6) === -1Q1f6 + @test signbit(0.5Q0f7) === false + @test signbit(-0.5Q0f7) === true +end + @testset "Promotion within Fixed" begin @test @inferred(promote(Q0f7(0.25), Q0f7(0.75))) === (Q0f7(0.25), Q0f7(0.75)) diff --git a/test/normed.jl b/test/normed.jl index fd620860..e9d78d5e 100644 --- a/test/normed.jl +++ b/test/normed.jl @@ -351,6 +351,19 @@ end @test clamp(2.0N1f7, N0f8) === 1.0N0f8 end +@testset "sign-related functions" begin + @test_throws Exception signed(N0f8) + @test_throws Exception signed(1N0f8) + @test_throws Exception unsigned(N0f8) + @test_throws Exception unsigned(1N0f8) + @test_throws ArgumentError copysign(1N0f8, 0x1) + @test_throws ArgumentError copysign(1N0f8, -1) + @test_throws ArgumentError flipsign(1N0f8, 0x1) + @test_throws ArgumentError flipsign(1N0f8, -1) + @test_throws ArgumentError sign(0N0f8) + @test signbit(1N0f8) === false +end + @testset "unit range" begin @test length(N0f8(0):N0f8(1)) == 2 @test length(N0f8(1):N0f8(0)) == 0