From 5f216791305b80c670a4b8e05978c5c10eece70c Mon Sep 17 00:00:00 2001 From: Antoine Levitt Date: Thu, 16 Jul 2026 11:56:31 +0200 Subject: [PATCH 1/2] Don't widen the primal when differentiating `x^y` with a real base `2^x` is `Float32` for a `Float32` `x`, but `2^Dual{T,Float32,N}` came back `Dual{T,Float64,N}`: `expv = x^v` honours Base's promotion, while `log(x)` was evaluated from `x` alone, so an integer base gave a Float64 partial and the `Dual` constructor unified value and partials upward. Differentiating should not change the primal's type. Take the log in `expv`'s type instead, which is the promotion `^` already chose for this base/exponent pair. Float64 results are bit-identical; only bases narrower than the exponent's own promotion (e.g. `Integer`) change, and only in type. `NaNMath.pow` shares the definition and is fixed too. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_017an8Gie5b1ULeSjhep3Sjb --- src/dual.jl | 2 +- test/DualTest.jl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/dual.jl b/src/dual.jl index 7552dd5f..0c5d5ebc 100644 --- a/src/dual.jl +++ b/src/dual.jl @@ -576,7 +576,7 @@ for (f, log) in ((:(Base.:^), :(Base.log)), (:(NaNMath.pow), :(NaNMath.log))) begin v = value(y) expv = ($f)(x, v) - deriv = (iszero(x) && v > 0) ? zero(expv) : expv*($log)(x) + deriv = (iszero(x) && v > 0) ? zero(expv) : expv*($log)(oftype(expv, x)) return Dual{Ty}(expv, deriv * partials(y)) end ) diff --git a/test/DualTest.jl b/test/DualTest.jl index df9f9dcb..03021b9d 100644 --- a/test/DualTest.jl +++ b/test/DualTest.jl @@ -499,6 +499,14 @@ ForwardDiff.:≺(::Type{OuterTestTag}, ::Type{TestTag}) = false @test partials(NaNMath.pow(Dual{TestTag}(-2.0, 1.0), Dual{TestTag}(2.0, 0.0)), 1) == -4.0 + # differentiating must not widen the primal: `2^x` is Float32 for a Float32 `x` + @testset "$f: real base keeps $W exponent" for f in (^, NaNMath.pow), + W in (Float16, Float32, Float64) + w = W(4)/W(3) + @test typeof(value(f(2, Dual{TestTag}(w, one(W))))) === typeof(f(2, w)) + @test typeof(value(f(2.0f0, Dual{TestTag}(w, one(W))))) === typeof(f(2.0f0, w)) + end + ################################### # General Mathematical Operations # ################################### From 5e05ba55c73767367dfdcf1ebdda881af3f305b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20M=C3=BCller-Widmann?= Date: Mon, 27 Jul 2026 15:15:19 +0200 Subject: [PATCH 2/2] Bump version to 1.4.2 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 77f40590..05ab97c0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "ForwardDiff" uuid = "f6369f11-7733-5829-9624-2563aa707210" -version = "1.4.1" +version = "1.4.2" [deps] CommonSubexpressions = "bbf7d656-a473-5ed7-a52c-81e309532950"