https://github.com/Lurie97 created https://github.com/llvm/llvm-project/pull/212696
The FP32 __clc_exp/__clc_exp2 implementations scale the reduced result by 2^p using integer bit manipulation (as_int(y) + (p << 23)). That representation cannot encode a subnormal, so inputs whose result falls below the smallest normal were unconditionally flushed to zero via "x < llim ? 0.0f". On devices that report CL_FP_DENORM this loses the correct subnormal result (e.g. erfc(), which is built on exp(), failed CTS math_brute_force with the reference subnormal vs a returned 0). Recompute the subnormal range with __clc_ldexp, which does produce subnormals, gated on __clc_fp32_subnormals_supported() so flush-to-zero targets keep the previous fast path. The ldexp path is restricted to [subnorm_llim, llim); inputs below subnorm_llim (including -inf and large-magnitude negatives) still flush to zero, since their reduced exponent argument would be ill-formed for ldexp. >From aa9d44de0adb1f21eccda24a7721031ac9ce8594 Mon Sep 17 00:00:00 2001 From: jiajia Qian <[email protected]> Date: Thu, 30 Jul 2026 14:03:12 +0800 Subject: [PATCH] [libclc] Produce subnormal results for exp/exp2 on FP32 The FP32 __clc_exp/__clc_exp2 implementations scale the reduced result by 2^p using integer bit manipulation (as_int(y) + (p << 23)). That representation cannot encode a subnormal, so inputs whose result falls below the smallest normal were unconditionally flushed to zero via "x < llim ? 0.0f". On devices that report CL_FP_DENORM this loses the correct subnormal result (e.g. erfc(), which is built on exp(), failed CTS math_brute_force with the reference subnormal vs a returned 0). Recompute the subnormal range with __clc_ldexp, which does produce subnormals, gated on __clc_fp32_subnormals_supported() so flush-to-zero targets keep the previous fast path. The ldexp path is restricted to [subnorm_llim, llim); inputs below subnorm_llim (including -inf and large-magnitude negatives) still flush to zero, since their reduced exponent argument would be ill-formed for ldexp. --- libclc/clc/lib/generic/math/clc_exp.cl | 2 ++ libclc/clc/lib/generic/math/clc_exp.inc | 15 ++++++++++++++- libclc/clc/lib/generic/math/clc_exp2.cl | 2 ++ libclc/clc/lib/generic/math/clc_exp2.inc | 15 ++++++++++++++- 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/libclc/clc/lib/generic/math/clc_exp.cl b/libclc/clc/lib/generic/math/clc_exp.cl index 1e7174c7541c5..452e85ac23930 100644 --- a/libclc/clc/lib/generic/math/clc_exp.cl +++ b/libclc/clc/lib/generic/math/clc_exp.cl @@ -11,7 +11,9 @@ #include "clc/internal/clc.h" #include "clc/math/clc_exp_helper.h" #include "clc/math/clc_fma.h" +#include "clc/math/clc_ldexp.h" #include "clc/math/clc_mad.h" +#include "clc/math/clc_subnormal_config.h" #include "clc/math/math.h" #include "clc/relational/clc_isnan.h" diff --git a/libclc/clc/lib/generic/math/clc_exp.inc b/libclc/clc/lib/generic/math/clc_exp.inc index 5057bf8034e92..aa20412880e2e 100644 --- a/libclc/clc/lib/generic/math/clc_exp.inc +++ b/libclc/clc/lib/generic/math/clc_exp.inc @@ -44,8 +44,21 @@ _CLC_OVERLOAD _CLC_DEF __CLC_GENTYPE __clc_exp(__CLC_GENTYPE x) { const __CLC_GENTYPE ulim = 0x1.62e430p+6f; // ln(smallest_normal) = -87.33654475055310898657 const __CLC_GENTYPE llim = -0x1.5d589ep+6f; + // ln(smallest_subnormal) = ln(2^-149) = -103.27892990343184 + const __CLC_GENTYPE subnorm_llim = -0x1.9d1dap+6f; - r = x < llim ? 0.0f : r; + // The integer scaling above (as_int(y) + (p << 23)) cannot represent + // subnormal results, so inputs in [subnorm_llim, llim) whose result is a + // subnormal are flushed to zero by the plain "x < llim ? 0" path below. + // When subnormals are supported, recompute those via __clc_ldexp so the + // subnormal result is preserved. Inputs below subnorm_llim (including -inf) + // genuinely underflow to zero and must not go through ldexp, whose exponent + // argument would be ill-formed for such extreme inputs. + r = x < llim ? (__CLC_GENTYPE)0.0f : r; + if (__clc_fp32_subnormals_supported()) { + __CLC_GENTYPE sub = __clc_ldexp(y, p); + r = (x < llim & x >= subnorm_llim) ? sub : r; + } r = x < ulim ? r : __CLC_AS_GENTYPE((__CLC_UINTN)0x7f800000); return __clc_isnan(x) ? x : r; } diff --git a/libclc/clc/lib/generic/math/clc_exp2.cl b/libclc/clc/lib/generic/math/clc_exp2.cl index 0516963ab620f..baa3beb2ef3f2 100644 --- a/libclc/clc/lib/generic/math/clc_exp2.cl +++ b/libclc/clc/lib/generic/math/clc_exp2.cl @@ -11,8 +11,10 @@ #include "clc/internal/clc.h" #include "clc/math/clc_exp_helper.h" #include "clc/math/clc_fma.h" +#include "clc/math/clc_ldexp.h" #include "clc/math/clc_mad.h" #include "clc/math/clc_rint.h" +#include "clc/math/clc_subnormal_config.h" #include "clc/math/math.h" #include "clc/relational/clc_isnan.h" diff --git a/libclc/clc/lib/generic/math/clc_exp2.inc b/libclc/clc/lib/generic/math/clc_exp2.inc index 6da361a43ed4c..906386189f316 100644 --- a/libclc/clc/lib/generic/math/clc_exp2.inc +++ b/libclc/clc/lib/generic/math/clc_exp2.inc @@ -41,8 +41,21 @@ _CLC_OVERLOAD _CLC_DEF __CLC_GENTYPE __clc_exp2(__CLC_GENTYPE x) { const __CLC_GENTYPE ulim = 128.0f; const __CLC_GENTYPE llim = -126.0f; + // smallest subnormal is 2^-149 + const __CLC_GENTYPE subnorm_llim = -149.0f; - r = x < llim ? 0.0f : r; + // The integer scaling above (as_int(y) + (p << 23)) cannot represent + // subnormal results, so inputs in [subnorm_llim, llim) whose result is a + // subnormal are flushed to zero by the plain "x < llim ? 0" path below. + // When subnormals are supported, recompute those via __clc_ldexp so the + // subnormal result is preserved. Inputs below subnorm_llim (including -inf) + // genuinely underflow to zero and must not go through ldexp, whose exponent + // argument would be ill-formed for such extreme inputs. + r = x < llim ? (__CLC_GENTYPE)0.0f : r; + if (__clc_fp32_subnormals_supported()) { + __CLC_GENTYPE sub = __clc_ldexp(y, p); + r = (x < llim & x >= subnorm_llim) ? sub : r; + } r = x < ulim ? r : __CLC_AS_FLOATN((__CLC_UINTN)0x7f800000); return __clc_isnan(x) ? x : r; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
