From 00ba915af853302d9f399d7402d0970c9456a3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Wed, 30 Sep 2026 11:34:37 +0200 Subject: [PATCH 1/2] Define `floatmin`, `floatmax`, `maxintfloat` and `issubnormal` for `Dual` instances (#852) `typemin`, `typemax`, `floatmin`, `floatmax` and `maxintfloat` now return the primal for both `Dual` instances and types instead of a `Dual` with zero partials, which only added overhead and could produce NaNs. Co-Authored-By: Claude Opus 5.5 --- src/dual.jl | 12 ++++-------- src/prelude.jl | 2 +- test/DualTest.jl | 34 +++++++++++++++------------------- 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/dual.jl b/src/dual.jl index 6d13dec3..8b8f6327 100644 --- a/src/dual.jl +++ b/src/dual.jl @@ -305,8 +305,10 @@ end Base.copy(d::Dual) = d -Base.eps(d::Dual) = eps(value(d)) -Base.eps(::Type{D}) where {D<:Dual} = eps(valtype(D)) +for op in (:eps, :typemin, :typemax, :floatmin, :floatmax, :maxintfloat) + @eval Base.$op(d::Dual) = $op(d.value) + @eval Base.$op(::Type{<:Dual{T,V}}) where {T,V} = $op(V) +end # The `base` keyword was added in Julia 1.8: # https://github.com/JuliaLang/julia/pull/42428 @@ -894,10 +896,4 @@ function Base.show(io::IO, d::Dual{T,V,N}) where {T,V,N} print(io, ")") end -for op in (:(Base.typemin), :(Base.typemax), :(Base.floatmin), :(Base.floatmax)) - @eval function $op(::Type{ForwardDiff.Dual{T,V,N}}) where {T,V,N} - ForwardDiff.Dual{T,V,N}($op(V)) - end -end - Printf.tofloat(d::Dual) = Printf.tofloat(value(d)) diff --git a/src/prelude.jl b/src/prelude.jl index 9e037afa..3d2eef91 100644 --- a/src/prelude.jl +++ b/src/prelude.jl @@ -3,7 +3,7 @@ const DEFAULT_CHUNK_THRESHOLD = @load_preference("default_chunk_threshold", 12) const AMBIGUOUS_TYPES = (AbstractFloat, Irrational, Integer, Rational, Real, RoundingMode) -const UNARY_PREDICATES = Symbol[:isinf, :isnan, :isfinite, :iseven, :isodd, :isreal, :isinteger] +const UNARY_PREDICATES = Symbol[:isinf, :isnan, :isfinite, :issubnormal, :iseven, :isodd, :isreal, :isinteger] struct Chunk{N} end diff --git a/test/DualTest.jl b/test/DualTest.jl index 67d9c9f7..d3c3fbd5 100644 --- a/test/DualTest.jl +++ b/test/DualTest.jl @@ -383,6 +383,12 @@ ForwardDiff.:≺(::Type{OuterTestTag}, ::Type{TestTag}) = false @test isinf(Dual{TestTag}(Dual{TestTag}(Inf, M_PARTIALS), NESTED_PARTIALS)) @test !(isinf(NESTED_FDNUM)) + @test issubnormal(Dual{TestTag}(1.0f-38, PARTIALS)) + @test !(issubnormal(Dual{TestTag}(1.0f-37, PARTIALS))) + + @test issubnormal(Dual{TestTag}(Dual{TestTag}(1.0f-38, M_PARTIALS), NESTED_PARTIALS)) + @test !(issubnormal(Dual{TestTag}(Dual{TestTag}(1.0f-37, M_PARTIALS), NESTED_PARTIALS))) + @test isreal(FDNUM) @test isreal(NESTED_FDNUM) @@ -681,25 +687,15 @@ end @test iszero(ForwardDiff.partials(ForwardDiff.partials(x^y)[1])) end -@testset "Type min/max" begin - d1 = Dual(1.0) - dinf = typemax(typeof(d1)) - dminf = typemin(typeof(d1)) - @test dminf < d1 < dinf - @test typeof(dminf) === typeof(d1) - @test typeof(dinf) === typeof(d1) - @test !isfinite(dminf) - @test !isfinite(dinf) - - dfmin = floatmin(typeof(d1)) - dfmax = floatmax(typeof(d1)) - @test dfmin < d1 < dfmax - @test typeof(dfmin) === typeof(d1) - @test typeof(dfmax) === typeof(d1) - @test isfinite(dfmin) - @test isfinite(dfmax) - - @test floatmin(Dual{Nothing, ForwardDiff.Dual{Nothing, Float64, 2}, 1}) === Dual{Nothing}(Dual{Nothing}(floatmin(Float64),0.0,0.0),Dual{Nothing}(0.0,0.0,0.0)) +@testset "Type min/max: $T" for T in (Float32, Float64) + d = Dual(T(1), T(2)) + nested = Dual(Dual(T(1), T(2), T(3)), Dual(T(4), T(5), T(6))) + for f in (typemin, typemax, floatmin, floatmax, maxintfloat) + @test f(d) === f(T) + @test f(typeof(d)) === f(T) + @test f(nested) === f(T) + @test f(typeof(nested)) === f(T) + end end @testset "Integer" begin From b0d87da0609b8ccc0aa936cde0b7edec7220e693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Wed, 30 Sep 2026 11:34:37 +0200 Subject: [PATCH 2/2] Bump version from 1.4.6 to 1.5.0 Co-Authored-By: Claude Opus 5.5 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 801792c6..91eac238 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ForwardDiff" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "1.4.6" +version = "1.5.0" [deps] CommonSubexpressions = "bbf7d656-a473-5ed7-a52c-81e309532950"