Skip to content

Commit 1079f47

Browse files
committed
adapt_precision converts all non-integer numbers
1 parent c6f1aeb commit 1079f47

3 files changed

Lines changed: 75 additions & 53 deletions

File tree

‎NEWS.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ This page describes the most important changes in `TypeUtils`. The format is bas
66

77
## Unreleased
88

9+
### Breaking changes
10+
11+
- ``adapt_precision` converts all non-integer numbers. The new behavior is that an integer
12+
or complex number with integer real and imaginary parts is left unchanged while a rational
13+
number is converted to floating-point. It is still possible to overcome this behavior for
14+
a custom type by specializing these methods.
15+
916
### Changed
1017

1118
- `get_precision` can work on any types and instances.

‎src/precision.jl‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,11 @@ corresponding type with precision `T`.
8686
Example:
8787
8888
```jldoctest; setup=:(using TypeUtils)
89-
julia> adapt_precision(Float32, (1, 0x07, ("hello", 1.0, 3.0 - 2.0im, π)))
90-
(1.0f0, 7.0f0, ("hello", 1.0f0, 3.0f0 - 2.0f0im, 3.1415927f0))
89+
julia> adapt_precision(Float32, (1, 3.0 - 2.0im, ("hello", 0x07, 1.0, π)))
90+
(1, 3.0f0 - 2.0f0im, ("hello", 0x07, 1.0f0, 3.1415927f0))
9191
```
9292
93-
As can be seen, all numerical values are converted.
93+
As can be seen, only non-integer numerical values are converted.
9494
9595
Basically, `adapt_precision` supports numbers, and arrays or tuples of numbers. It can be
9696
specialized for other object types defined in foreign packages by specializing:
@@ -117,10 +117,12 @@ adapt_precision(::Type{T}, x::Any) where{T<:Precision} = x
117117
adapt_precision(::Type{T}, ::Type{S}) where {T<:Precision,S<:Any} = S
118118

119119
# For bare numbers and bare numerical types, `adapt_precision` behaves like
120-
# `convert_real_type`.
120+
# `convert_real_type` but leaving integers unchanged.
121121
adapt_precision(::Type{T}, x::T) where {T<:Precision} = x
122+
adapt_precision(::Type{T}, x::Integer) where {T<:Precision} = x
122123
adapt_precision(::Type{T}, x::BareNumber) where {T<:Precision} = convert_real_type(T, x)
123124
adapt_precision(::Type{T}, ::Type{T}) where {T<:Precision} = T
125+
adapt_precision(::Type{T}, ::Type{S}) where {T<:Precision,S<:Integer} = S
124126
adapt_precision(::Type{T}, ::Type{S}) where {T<:Precision,S<:BareNumber} =
125127
convert_real_type(T, S)
126128

‎test/runtests.jl‎

Lines changed: 62 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,11 +1022,13 @@ same_value_and_type(x::T, y::T) where {T} = (x === y) || (x == y)
10221022
@test TypeUtils.default_precision == Float64
10231023
f = @inferred adapt_precision(AbstractFloat)
10241024
@test f === @inferred adapt_precision(TypeUtils.default_precision)
1025-
@test 7.0 === @inferred f(7)
1025+
@test 7 === @inferred f(7)
1026+
@test 0x07 === @inferred f(0x07)
10261027
@test 7.0 === @inferred f(7.0f0)
1027-
@test 7.0 === @inferred f(big(7))
1028+
let x = big(7); @test @inferred(f(x)) === x; end
10281029
@test 7.0 === @inferred f(big(7.0))
1029-
@test -3.0u"kg/s" === @inferred f(-3u"kg/s")
1030+
@test -3.0u"kg/s" === @inferred f(-3f0u"kg/s")
1031+
@test -3u"kg/s" === @inferred f(-3u"kg/s")
10301032

10311033
@testset "adapt_precision($T, x)" for T in (AbstractFloat, Float16, Float32, Float64, BigFloat)
10321034
if isconcretetype(T)
@@ -1037,39 +1039,42 @@ same_value_and_type(x::T, y::T) where {T} = (x === y) || (x == y)
10371039

10381040
@test @inferred(adapt_precision(T, Symbol )) === Symbol
10391041
@test @inferred(adapt_precision(T, String )) === String
1040-
@test @inferred(adapt_precision(T, Bool )) === S
1041-
@test @inferred(adapt_precision(T, UInt8 )) === S
1042-
@test @inferred(adapt_precision(T, UInt16 )) === S
1043-
@test @inferred(adapt_precision(T, UInt32 )) === S
1044-
@test @inferred(adapt_precision(T, UInt64 )) === S
1045-
@test @inferred(adapt_precision(T, UInt128 )) === S
1046-
@test @inferred(adapt_precision(T, Int8 )) === S
1047-
@test @inferred(adapt_precision(T, Int16 )) === S
1048-
@test @inferred(adapt_precision(T, Int32 )) === S
1049-
@test @inferred(adapt_precision(T, Int64 )) === S
1050-
@test @inferred(adapt_precision(T, Int128 )) === S
1051-
@test @inferred(adapt_precision(T, BigInt )) === S
1042+
@test @inferred(adapt_precision(T, Bool )) === Bool
1043+
@test @inferred(adapt_precision(T, UInt8 )) === UInt8
1044+
@test @inferred(adapt_precision(T, UInt16 )) === UInt16
1045+
@test @inferred(adapt_precision(T, UInt32 )) === UInt32
1046+
@test @inferred(adapt_precision(T, UInt64 )) === UInt64
1047+
@test @inferred(adapt_precision(T, UInt128 )) === UInt128
1048+
@test @inferred(adapt_precision(T, Int8 )) === Int8
1049+
@test @inferred(adapt_precision(T, Int16 )) === Int16
1050+
@test @inferred(adapt_precision(T, Int32 )) === Int32
1051+
@test @inferred(adapt_precision(T, Int64 )) === Int64
1052+
@test @inferred(adapt_precision(T, Int128 )) === Int128
1053+
@test @inferred(adapt_precision(T, BigInt )) === BigInt
10521054
@test @inferred(adapt_precision(T, Float16 )) === S
10531055
@test @inferred(adapt_precision(T, Float32 )) === S
10541056
@test @inferred(adapt_precision(T, Float64 )) === S
10551057

10561058
str = "string"
10571059
@test @inferred(adapt_precision(T, :symbol )) === :symbol
10581060
@test @inferred(adapt_precision(T, str )) === str # same object
1059-
@test @inferred(adapt_precision(T, true )) ≗ one(S)
1060-
@test @inferred(adapt_precision(T, false )) ≗ zero(S)
1061-
@test @inferred(adapt_precision(T, 0x03 )) ≗ S(3)
1062-
@test @inferred(adapt_precision(T, 3 )) ≗ S(3)
1063-
@test @inferred(adapt_precision(T, big(3) )) ≗ S(3)
1061+
@test @inferred(adapt_precision(T, true )) === true
1062+
@test @inferred(adapt_precision(T, false )) === false
1063+
@test @inferred(adapt_precision(T, 0x03 )) === 0x03
1064+
@test @inferred(adapt_precision(T, 3 )) === 3
1065+
let x = big(3); @test @inferred(adapt_precision(T, x)) === x; end
10641066
@test @inferred(adapt_precision(T, 3//2 )) ≗ S(3//2)
10651067
@test @inferred(adapt_precision(T, π )) ≗ S(π)
10661068
@test @inferred(adapt_precision(T, Float16(3))) ≗ S(3)
10671069
@test @inferred(adapt_precision(T, 3.0f0 )) ≗ S(3)
10681070
@test @inferred(adapt_precision(T, 3.0 )) ≗ S(3)
10691071
@test @inferred(adapt_precision(T, big(3.0) )) ≗ S(3)
10701072

1071-
# Tests with an array.
1073+
# Tests with an abstract (reshaped) array.
10721074
A = reshape(-3:20, 2,3,4)
1075+
@test @inferred(adapt_precision(T, A)) === A
1076+
A = reshape(-3.0:20.0, 2,3,4)
1077+
@test S === @inferred adapt_precision(T, eltype(A))
10731078
B = @inferred adapt_precision(T, A)
10741079
@test eltype(B) === S
10751080
@test axes(B) == axes(A)
@@ -1079,9 +1084,11 @@ same_value_and_type(x::T, y::T) where {T} = (x === y) || (x == y)
10791084
@test B === @inferred adapt_precision(T, B) # same precision yields identical object
10801085

10811086
# Tests with a real rectangular matrix.
1082-
A = [4 -1 0; 2 3 -1]
1087+
A = [4 -1 0; 2 3 -1] # integer type
1088+
@test @inferred(adapt_precision(T, A)) === A
1089+
A = map(x -> x//1, A)
10831090
AAt = A*A'
1084-
@test S == @inferred adapt_precision(T, eltype(A))
1091+
@test S === @inferred adapt_precision(T, eltype(A))
10851092
B = @inferred adapt_precision(T, A)
10861093
@test eltype(B) == S
10871094
@test axes(B) == axes(A)
@@ -1287,22 +1294,29 @@ same_value_and_type(x::T, y::T) where {T} = (x === y) || (x == y)
12871294
end
12881295

12891296
@testset "adapt_precision(T, x::Tuple)" begin
1290-
@test adapt_precision(
1291-
Float32, (
1292-
true, 1, 0x07, ("hello", 1.0, 3.0 - 2.0im, π))) === (
1293-
1.0f0, 1.0f0, 7.0f0, ("hello", 1.0f0, 3.0f0 - 2.0f0im, Float32(π)))
1297+
@test adapt_precision(Float32, (true, 1, 0x07, ("hello", 1.0, 3.0 - 2.0im, π))) === (
1298+
true, 1, 0x07, ("hello", 1.0f0, 3.0f0 - 2.0f0im, Float32(π)))
12941299
x = (; a = false, b = :symbol, d = [-1, 2])
1300+
@test get_precision(x) === AbstractFloat
1301+
@test @inferred(adapt_precision(Float32, x)) === x
12951302
y = (; a = 0.0, b = :symbol, d = [-1.0, 2.0])
1296-
z = @inferred adapt_precision(Float64, x)
1297-
@test z == y
1298-
@test typeof(z.d) == typeof(y.d)
1299-
# a very long tuple
1300-
x = ([(isodd(x) ? Int8( x) : string(x)) for x in -20:30]...,)
1301-
y = ([(isodd(x) ? Float32(x) : string(x)) for x in -20:30]...,)
1302-
z = adapt_precision(Float32, x)
1303+
@test y == x
1304+
@test get_precision(y) === Float64
1305+
z = @inferred adapt_precision(Float32, y)
1306+
@test get_precision(z) === Float32
13031307
@test z == y
1308+
@test typeof(z.a) == Float32
1309+
@test typeof(z.b) == Symbol
1310+
@test eltype(z.d) == Float32
1311+
# a very long tuple (likely too long for inference)
1312+
x = ([(isodd(x) ? Int8(x) : string(x)) for x in -20:30]...,)
13041313
@test get_precision(x) === AbstractFloat
1305-
@test get_precision(y) === get_precision(z) === Float32
1314+
@test adapt_precision(Float32, x) === x
1315+
y = ([(isodd(x) ? Float64(x) : string(x)) for x in -20:30]...,)
1316+
@test get_precision(y) === Float64
1317+
z = adapt_precision(Float32, y)
1318+
@test get_precision(z) === Float32
1319+
@test z == y
13061320
end
13071321
end
13081322

@@ -1534,23 +1548,22 @@ same_value_and_type(x::T, y::T) where {T} = (x === y) || (x == y)
15341548
@test BigFloat === @inferred get_precision( BigFloat(35) * u"GHz" )
15351549

15361550
@testset "adapt_precision($T, x::Quantity)" for T in (AbstractFloat, Float16, Float32, Float64, BigFloat)
1537-
S = isconcretetype(T) ? T : TypeUtils.default_precision
15381551
u = u"GHz"
15391552
v = -35
1540-
r = S(v)*u
1541-
@test typeof(r) === @inferred adapt_precision(T, typeof( v * u))
1542-
@test typeof(r) === @inferred adapt_precision(T, typeof(Float16( v) * u))
1543-
@test typeof(r) === @inferred adapt_precision(T, typeof(Float32( v) * u))
1544-
@test typeof(r) === @inferred adapt_precision(T, typeof(Float64( v) * u))
1545-
@test typeof(r) === @inferred adapt_precision(T, typeof(BigInt( v) * u))
1546-
@test typeof(r) === @inferred adapt_precision(T, typeof(BigFloat(v) * u))
1553+
f = adapt_precision(T)
1554+
let x = v ; @test typeof(@inferred(f(x*u))) === @inferred(f(typeof(x*u))); end
1555+
let x = Float16( v); @test typeof(@inferred(f(x*u))) === @inferred(f(typeof(x*u))); end
1556+
let x = Float32( v); @test typeof(@inferred(f(x*u))) === @inferred(f(typeof(x*u))); end
1557+
let x = Float64( v); @test typeof(@inferred(f(x*u))) === @inferred(f(typeof(x*u))); end
1558+
let x = BigInt( v); @test typeof(@inferred(f(x*u))) === @inferred(f(typeof(x*u))); end
1559+
let x = BigFloat(v); @test typeof(@inferred(f(x*u))) === @inferred(f(typeof(x*u))); end
15471560
#
1548-
@test r ≗ @inferred adapt_precision(T, v * u)
1549-
@test r ≗ @inferred adapt_precision(T, Float16( v) * u)
1550-
@test r ≗ @inferred adapt_precision(T, Float32( v) * u)
1551-
@test r ≗ @inferred adapt_precision(T, Float64( v) * u)
1552-
@test r ≗ @inferred adapt_precision(T, BigInt( v) * u)
1553-
@test r ≗ @inferred adapt_precision(T, BigFloat(v) * u)
1561+
let x = v ; @test @inferred(f(x*u)) ≗ @inferred(f(x)*u); end
1562+
let x = Float16( v); @test @inferred(f(x*u)) ≗ @inferred(f(x)*u); end
1563+
let x = Float32( v); @test @inferred(f(x*u)) ≗ @inferred(f(x)*u); end
1564+
let x = Float64( v); @test @inferred(f(x*u)) ≗ @inferred(f(x)*u); end
1565+
let x = BigInt( v); @test @inferred(f(x*u)) ≗ @inferred(f(x)*u); end
1566+
let x = BigFloat(v); @test @inferred(f(x*u)) ≗ @inferred(f(x)*u); end
15541567
end
15551568

15561569
# unitless

0 commit comments

Comments
 (0)