Skip to content

Commit c6f1aeb

Browse files
committed
Simplify and generalize get_precision
1 parent 62d3425 commit c6f1aeb

2 files changed

Lines changed: 51 additions & 33 deletions

File tree

‎NEWS.md‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ This page describes the most important changes in `TypeUtils`. The format is bas
88

99
### Changed
1010

11-
- Minimal Julia version is 1.0. This bound is imposed by the `InverseFunctions` package.
11+
- `get_precision` can work on any types and instances.
12+
13+
- Minimal Julia version is 1.0, this bound is imposed by the `InverseFunctions` package.
1214

1315
## Version 1.14.0 (2026-01-06)
1416

@@ -22,7 +24,7 @@ This new minor version essentially provides `lazymap` (see below).
2224
provided to specify the inverse of `f`. Related type alias `LazyMap{T,N,F,typeof(A)}` can
2325
be used to specialize on the type `B` with `N = ndims(A)` and `F = typeof(f)`.
2426

25-
### Change
27+
### Changed
2628

2729
- The former function `as_eltype(T,A)` is now a shortcut for `lazymap(T,identity,A)` unless
2830
`T == eltype(A)` holds in which case `A` is returned.

‎src/precision.jl‎

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,53 +6,69 @@ Return the numerical precision of number/object `x`. If `x` is a floating-point
66
floating-point type is returned; if `x` stores floating-point values, their promoted
77
floating-point type is returned; otherwise, `AbstractFloat` is returned.
88
9-
For type-stability, the precision shall be a *trait* which does only depend of the type of
10-
`x`.
11-
12-
Basically, `get_precision` supports numbers, and arrays or tuples of numbers. It can be
13-
specialized for other object types defined in foreign packages. For objects of type, say
9+
By default, `get_precision` is applicable to any object or type and assuming that the
10+
precision is a *trait*, hence, which only depends on the type of `x`. This ensures
11+
*type-stability*. However, if a specific behavior must be implemented to a type, say,
1412
`SomeType`, it is only needed to extend the method:
1513
1614
```julia
1715
TypeUtils.get_precision(::Type{T}) where {T<:SomeType} = ...
1816
```
1917
20-
See also [`adapt_precision`](@ref) and [`TypeUtils.Precision`](@ref).
21-
22-
"""
23-
get_precision() = AbstractFloat
24-
get_precision(x::T) where {T} = get_precision(T)
25-
get_precision(::Type) = AbstractFloat # pass-through
26-
get_precision(::Type{T}) where {T<:Precision} = T
27-
get_precision(::Type{<:Complex{T}}) where {T} = get_precision(T)
28-
get_precision(::Type{<:AbstractArray{T}}) where {T} = get_precision(T)
29-
get_precision(::Type{<:Factorization{T}}) where {T} = get_precision(T)
18+
which shall return one of the floating-point types of union `TypeUtils.Precision` or
19+
`AbstractFloat`.
3020
31-
# Second type parameter of a named tuple is a tuple of types.
32-
get_precision(::Type{NamedTuple{S,T}}) where {S,T} = get_precision(T)
21+
If 0, 2 or more arguments are specified, `get_precision` returns the promoted precision of
22+
all given arguments.
3323
34-
# Deal with types of n-tuple for moderate values of n. These have the form `Type{A,B,C,...}`.
35-
@inline get_precision(::Type{T}) where {T<:Tuple} = get_precision(unpack(T)...)
36-
37-
# See https://discourse.julialang.org/t/inferable-unpacking-of-tuple-type-to-tuple-of-types/66329
38-
@generated unpack(::Type{T}) where {T<:Tuple} = Tuple(T.parameters)
24+
See also [`adapt_precision`](@ref) and [`TypeUtils.Precision`](@ref).
3925
4026
"""
41-
get_precision(x, y, z...) -> T<:AbstractFloat
27+
get_precision(x::Any) = get_precision(typeof(x)) # precision is a trait
28+
get_precision(::Type{T}) where {T<:Precision} = T
29+
get_precision(::Type{T}) where {T<:Real} = AbstractFloat
30+
get_precision(::Type{T}) where {T<:TypeVar} = AbstractFloat
4231

43-
Return the promoted precision of all given arguments `x`, `y`, and `z...`.
32+
# The following specialization is needed for abstract arrays like `StepRangeLen` which have
33+
# fields of higher precision to avoid rounding errors.
34+
get_precision(::Type{<:AbstractArray{T}}) where {T} = get_precision(T)
4435

45-
"""
36+
# Get precision for 0 or more than 2 arguments.
37+
get_precision() = AbstractFloat
4638
@inline get_precision(x, y, z...) = get_precision(get_precision(x, y), z...)
47-
get_precision(x, y) =
48-
get_precision(get_precision(x)::Type{<:AbstractFloat},
49-
get_precision(y)::Type{<:AbstractFloat})::Type{<:AbstractFloat}
5039

40+
# Get precision of exactly 2 types. Type assertion in the most generic method is to avoid
41+
# stack-overflow for an invalid specialization.
5142
get_precision(::Type{AbstractFloat}, ::Type{AbstractFloat}) = AbstractFloat
52-
get_precision(::Type{T}, ::Type{AbstractFloat}) where {T<:AbstractFloat} = T
53-
get_precision(::Type{AbstractFloat}, ::Type{T}) where {T<:AbstractFloat} = T
43+
get_precision(::Type{T}, ::Type{AbstractFloat}) where {T<:AbstractFloat} = get_precision(T)
44+
get_precision(::Type{AbstractFloat}, ::Type{T}) where {T<:AbstractFloat} = get_precision(T)
5445
get_precision(::Type{S}, ::Type{T}) where {S<:AbstractFloat,T<:AbstractFloat} =
55-
promote_type(S, T)::Type{<:AbstractFloat}
46+
get_precision(promote_type(S, T))
47+
get_precision(x, y) = get_precision(get_precision(x)::Type{<:AbstractFloat},
48+
get_precision(y)::Type{<:AbstractFloat})
49+
50+
# Return a simple vector of type parameters. (Must be defined before any generated function
51+
# that may use it.)
52+
function type_parameters(::Type{T}) where {T}
53+
try
54+
getfield(T, :parameters)
55+
catch
56+
try
57+
getfield(getfield(T, :body), :parameters)
58+
catch
59+
Base.Core.svec()
60+
end
61+
end
62+
end
63+
64+
# Default method for structure, tuple, and named tuple types.
65+
@generated function get_precision(::Type{S}) where {S}
66+
T = AbstractFloat
67+
for p in type_parameters(S)
68+
T = get_precision(T, p)
69+
end
70+
return T
71+
end
5672

5773
"""
5874
adapt_precision(T::Type{<:AbstractFloat}, x) -> y

0 commit comments

Comments
 (0)