Skip to content

Commit cb29336

Browse files
authored
Add support for Normed construction from Rational (Fixes #157) (#169)
This also fixes the overflow problem with `Fixed` construction from `Rational`.
1 parent 05fd7e7 commit cb29336

File tree

4 files changed

+23
-7
lines changed

4 files changed

+23
-7
lines changed

src/fixed.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,13 @@ function _convert(::Type{F}, x::AbstractFloat) where {T, f, F <: Fixed{T,f}}
6262
end
6363

6464
function _convert(::Type{F}, x::Rational) where {T, f, F <: Fixed{T,f}}
65-
F(x.num)/F(x.den) # TODO: optimization and input range checking
65+
xmin = widemul(denominator(x), widen1(T)(typemin(T)) << 0x1 - 0x1)
66+
xmax = widemul(denominator(x), oneunit(widen1(T)) << bitwidth(T) - 0x1)
67+
if xmin <= (widen1(numerator(x)) << UInt8(f + 1)) < xmax
68+
reinterpret(F, round(T, convert(floattype(T), x) * @exp2(f)))
69+
else
70+
throw_converterror(F, x)
71+
end
6672
end
6773

6874
# unchecked arithmetic

src/normed.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,14 @@ function _convert(::Type{N}, x::Tf) where {T, f, N <: Normed{T,f}, Tf <: Union{F
100100
return reinterpret(N, unsafe_trunc(T, yi >> (ex & bits)))
101101
end
102102

103+
function _convert(::Type{N}, x::Rational) where {T, f, N <: Normed{T,f}}
104+
if 0 <= x <= Rational(typemax(N))
105+
reinterpret(N, round(T, convert(floattype(T), x) * rawone(N)))
106+
else
107+
throw_converterror(N, x)
108+
end
109+
end
110+
103111
rem(x::N, ::Type{N}) where {N <: Normed} = x
104112
rem(x::Normed, ::Type{N}) where {T, N <: Normed{T}} = reinterpret(N, _unsafe_trunc(T, round((rawone(N)/rawone(x))*reinterpret(x))))
105113
rem(x::Real, ::Type{N}) where {T, N <: Normed{T}} = reinterpret(N, _unsafe_trunc(T, round(rawone(N)*x)))

test/fixed.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,10 @@ end
9999
@test_throws InexactError convert(Fixed{Int8, 7}, 128)
100100

101101
@test convert(Q2f5, -1//2) === -0.5Q2f5
102-
@test_broken convert(Q1f6, Rational{Int8}(-3//4)) === -0.75Q1f6
103-
@test_broken convert(Q0f7, Rational{Int16}(-3//4)) === -0.75Q0f7
104-
@test_broken convert(Q0f7, Rational{UInt8}(3//4)) === 0.75Q0f7
102+
@test convert(Q1f6, Rational{Int8}(-3//4)) === -0.75Q1f6
103+
@test convert(Q0f7, Rational{Int16}(-3//4)) === -0.75Q0f7
104+
@test convert(Q0f7, Rational{UInt8}(3//4)) === 0.75Q0f7
105+
@test_throws ArgumentError convert(Q0f7, typemax(Rational{Int8}))
105106

106107
@test convert(Q0f7, Base.TwicePrecision(0.5)) === 0.5Q0f7
107108
@test_throws InexactError convert(Q7f8, Base.TwicePrecision(0x80, 0x01))

test/normed.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,10 @@ end
9292

9393
@test convert(N0f8, 1.1f0/typemax(UInt8)) == eps(N0f8)
9494

95-
@test_broken convert(N0f8, 1//255) === eps(N0f8)
96-
@test_broken convert(N0f8, Rational{Int8}(3//5)) === N0f8(3/5)
97-
@test_broken convert(N0f8, Rational{UInt8}(3//5)) === N0f8(3/5)
95+
@test convert(N0f8, 1//255) === eps(N0f8)
96+
@test convert(N0f8, Rational{Int8}(3//5)) === N0f8(3/5)
97+
@test convert(N0f8, Rational{UInt8}(3//5)) === N0f8(3/5)
98+
@test_throws ArgumentError convert(N0f8, typemax(Rational{UInt8}))
9899

99100
@test convert(N0f8, Base.TwicePrecision(1.0)) === 1N0f8
100101

0 commit comments

Comments
 (0)