From patchwork Fri Aug 21 04:26:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Matt Turner X-Patchwork-Id: 141876 X-Patchwork-Delegate: azanella@linux.vnet.ibm.com Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from vm01.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 323A14BAE7D7 for ; Fri, 21 Aug 2026 04:27:35 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 323A14BAE7D7 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=pkxQxppX X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-yx1-xb131.google.com (mail-yx1-xb131.google.com [IPv6:2607:f8b0:4864:20::b131]) by sourceware.org (Postfix) with ESMTPS id AAA894BAE7D4 for ; Fri, 21 Aug 2026 04:27:00 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org AAA894BAE7D4 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org AAA894BAE7D4 Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::b131 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787286420; cv=none; b=g5j8NXOFECST+3tCOFkXECkhjCnIY5Jh9SdJlPhB9w7/yW/g0iKwGmtyT7NO0gMJavDAQx/x5zVaZgXKj1+PbtwYWce7cr2y70MncpWnAMCiQg9/NnD3Eyddr9Wd64MOFzgxuFqRW8nkvB/FzDVenTJRsW8OyLSrufrdDezamgc= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787286420; c=relaxed/simple; bh=dhsshgxNpOiErWWzJKEz9tnQu5LhwK0qq1BSp/7a53U=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=iPCjxfhU9GiaV1RHI9+cDkRpTpVInQKwh2mjiufDqF7HA37HAa/B0MSvcVCkD1Y+pW2ehTX3zCqb1IbtVg6DMKkdoOVORaFLiGphKKS20zClNu4GLfDArXvar/NqDyJQ/OaQC4a5hhx1FAABkRzripyYb2PT6qAnQvRSPQIVy8w= ARC-Authentication-Results: i=1; sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20251104 header.b=pkxQxppX DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AAA894BAE7D4 Received: by mail-yx1-xb131.google.com with SMTP id 956f58d0204a3-66c7264d87dso904648d50.1 for ; Thu, 20 Aug 2026 21:27:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787286420; x=1787891220; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=5iuJLOUIfvTU6iktW1mFYr7oUWtn+96dYYQdBgY9bbs=; b=pkxQxppX4ZDynyT0latExaTun1/0vpl+x1YXKwY2qeh86RbVg2P3jPU7R2bdpR1IF8 yGalSLvbq+cyFU/1Yc78mU8TeKozuoLKl5O5+7jAyC6l7XLAZqYgtyRr12dhgjyp0P4R JnmtJ9xrf31ZZgwzk892dDmG+ybRWF2+FTl+zbwrV+kyLI61cw+kHFgRGOAEDVH0f3JD eiFRAuWz9BLZdt/SJCRPp2Nu2MNCJs+wguSQgPPVUrcKBZCQUOrN6esQaaTSO7vLUvET xXNOSafvTAIkenVypjAE+7BAwj3L1CRDBZ5XKdSHZb+BPpscCM8P9czHx1MC5u60sIXm cDQw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787286420; x=1787891220; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to:content-type; bh=5iuJLOUIfvTU6iktW1mFYr7oUWtn+96dYYQdBgY9bbs=; b=Cg6Pja+3MxlR9u0OEyn214sIj34JPCxdhIL1fbfXMRkNb/hCFox4peAAjFh3QmKN2i cjKqiZ0cJ45ytSwHCwRCOLUE/RHtwVSrj6qKzFEsdD6a0Bq3yrVnj2Q6jB9FP59xGz4Q gyLOQ8ZqyoOtzoLmexrBUED8RPvQL0oKX2c4xwg7Zq5pStau3GZVg8rI4d3AZ+Uq5KJ7 Dasmw/xLEh40xBHuakW2yY5npEiRQ+0w3yWthtoMY+TKTw8YNxDbnLJ3i6IiStMerFbF dqklv5/R49p5b16sDQplirPN+9Q3pZYtuoGBWf+pdKuO1JEN9m1kn1ZrFbSTPiMbB7SB WIhQ== X-Gm-Message-State: AFuF++kPQ0vrTlV/mY4Cq6tzifG+ygj6iInVI0+fG1VLzpvQwR2xNfPU 2o+GuDMv0F/T2k+VJA2axbPdQ0o6uewFa/v4oiBQRf5/EAgxEdXCYbAlmlDzbbCnvBo= X-Gm-Gg: AR+sD13aJLow8uT8CSNRmoFh7kXpLIidCbORyBQxJNXpu2OT7Vq1vD3wFUjbryzq1O5 7UqB2ASc+omLC9KNxLEiFHr8oDtn0d1C15z81iFbpKGB2QcY9lFeYNhDXTbDF2FjNFJHYEFWAS2 IgcqD1IzlFLlYJdNNDvY087hGDNSwZ6v+eaFBK8MRoAT9CLqx1YlOTzAXFYkW2NRpJCSRjieFJF ezeim3s7k6TFYDCuP3R2Oar2rrJvCBGuMFvNVc86drpQnXEEtfEZmXFO89pjpRSIIq+3QTrYOM/ 4nQj5nhEU8ir+68vVY/drbVm6aN8bydcpwj2mj2xuk66SE3NE3Fz8qsKE3syO3b+/Xf6qukIssw /8dF8nbhBjzDrpIAgYGYnoh+NdU22zEJ/eqy31NhqLYw9eDLA89+AIXoLuFnjWtRHwLj7YMMZun hME+fuVZ6UNKTxU/g/cQFlyGSLUT9ViPBcDuB9k6CyA4FuOupcHonAWkQ2c8ht41V0NZszkgoTa eq9sIICbqhFuoc1ALT5CkSX8GX8pnJAXi2RDiJxDywE3gxgeds= X-Received: by 2002:a53:bd04:0:b0:668:1e79:116e with SMTP id 956f58d0204a3-66ce5bb3350mr780223d50.27.1787286419746; Thu, 20 Aug 2026 21:26:59 -0700 (PDT) Received: from localhost (107-220-129-194.lightspeed.chrlnc.sbcglobal.net. [107.220.129.194]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-66ccb14494asm3710937d50.11.2026.08.20.21.26.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 20 Aug 2026 21:26:59 -0700 (PDT) From: Matt Turner To: libc-alpha@sourceware.org Cc: Joseph Myers , Matt Turner Subject: [PATCH] math: let architectures signal underflow after narrowing Date: Fri, 21 Aug 2026 00:26:54 -0400 Message-ID: <20260821042654.3650063-1-mattst88@gmail.com> X-Mailer: git-send-email 2.54.0 MIME-Version: 1.0 X-Spam-Status: No, score=-11.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP shortcircuit=no autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org IEEE 754 determines tininess after rounding from the result rounded to the precision of the destination with an unbounded exponent range. Alpha determines it from the delivered result instead, and the two differ where a value is tiny but reaches the smallest normal once rounded, since the spacing below the smallest normal is twice that of the binade the unbounded rounding lands in. Alpha signals no underflow for such a result, and the narrowing functions cannot recover it: no trap is taken, so the kernel emulation never sees the operation, and the final narrowing is a single conversion instruction that has already decided the question. Add a CHECK_NARROW_TINY hook, in a sysdeps header that generic code defines to do nothing, and use it in the round-to-odd narrowing macros and in fmaf. Round-to-odd already gives the right answer wherever tininess follows IEEE 754, so nothing changes for architectures that do. The narrowing macros bound the round-to-odd value only as a temporary, so add, subtract, multiply and divide now name it; fused multiply-add already did. NARROW_MIN_NORMAL selects the smallest normal of the result type rather than taking it as a macro argument, which would have to be threaded through every narrowing function. On architectures where the hook is empty the preprocessor discards both arguments, so neither is expanded. fmaf is not a narrowing function but converts its double result to float on return, and so has the same problem; route its returns through one helper. On x86_64 the generated code is unchanged: s_fdiv and s_fmaf are identical with and without this, in both the static and shared builds, once debug information is stripped. On Alpha this fixes the twenty remaining math failures, all of the form Exception "Underflow" not set, leaving math with no failures. Reviewed-by: Adhemerval Zanella --- math/math-narrow.h | 49 ++++++++++++++++----- sysdeps/alpha/math-narrow-tininess.h | 61 ++++++++++++++++++++++++++ sysdeps/generic/math-narrow-tininess.h | 29 ++++++++++++ sysdeps/ieee754/dbl-64/s_fmaf.c | 24 ++++++++-- 4 files changed, 148 insertions(+), 15 deletions(-) create mode 100644 sysdeps/alpha/math-narrow-tininess.h create mode 100644 sysdeps/generic/math-narrow-tininess.h diff --git ./math/math-narrow.h ./math/math-narrow.h index bb319cc2ca..ef79ef2380 100644 --- ./math/math-narrow.h +++ ./math/math-narrow.h @@ -28,8 +28,20 @@ #include #include #include +#include +#include #include +/* The smallest positive normal value of TYPE, for CHECK_NARROW_TINY. + Selected at compile time; the generic CHECK_NARROW_TINY ignores its + arguments, so this expands to nothing on architectures that determine + tininess as IEEE 754 describes it. */ +#define NARROW_MIN_NORMAL(TYPE) \ + __builtin_choose_expr \ + (__builtin_types_compatible_p (TYPE, float), FLT_MIN, \ + __builtin_choose_expr \ + (__builtin_types_compatible_p (TYPE, double), DBL_MIN, LDBL_MIN)) + /* Carry out a computation using round-to-odd. The computation is EXPR; the union type in which to store the result is UNION and the subfield of the "ieee" field of that union with the low part of the @@ -97,8 +109,12 @@ if ((X) == -(Y)) \ ret = (TYPE) ((X) + (Y)); \ else \ - ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) + (Y), \ - UNION, SUFFIX, MANTISSA, false); \ + { \ + __typeof (X) w = ROUND_TO_ODD (math_opt_barrier (X) + (Y), \ + UNION, SUFFIX, MANTISSA, false); \ + ret = (TYPE) w; \ + CHECK_NARROW_TINY (ret, w, NARROW_MIN_NORMAL (TYPE)); \ + } \ \ CHECK_NARROW_ADD (ret, (X), (Y)); \ return ret; \ @@ -155,8 +171,12 @@ if ((X) == (Y)) \ ret = (TYPE) ((X) - (Y)); \ else \ - ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) - (Y), \ - UNION, SUFFIX, MANTISSA, false); \ + { \ + __typeof (X) w = ROUND_TO_ODD (math_opt_barrier (X) - (Y), \ + UNION, SUFFIX, MANTISSA, false); \ + ret = (TYPE) w; \ + CHECK_NARROW_TINY (ret, w, NARROW_MIN_NORMAL (TYPE)); \ + } \ \ CHECK_NARROW_SUB (ret, (X), (Y)); \ return ret; \ @@ -209,9 +229,11 @@ { \ TYPE ret; \ \ - ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) * (Y), \ - UNION, SUFFIX, MANTISSA, \ - CLEAR_UNDERFLOW); \ + __typeof (X) w = ROUND_TO_ODD (math_opt_barrier (X) * (Y), \ + UNION, SUFFIX, MANTISSA, \ + CLEAR_UNDERFLOW); \ + ret = (TYPE) w; \ + CHECK_NARROW_TINY (ret, w, NARROW_MIN_NORMAL (TYPE)); \ \ CHECK_NARROW_MUL (ret, (X), (Y)); \ return ret; \ @@ -264,9 +286,11 @@ { \ TYPE ret; \ \ - ret = (TYPE) ROUND_TO_ODD (math_opt_barrier (X) / (Y), \ - UNION, SUFFIX, MANTISSA, \ - CLEAR_UNDERFLOW); \ + __typeof (X) w = ROUND_TO_ODD (math_opt_barrier (X) / (Y), \ + UNION, SUFFIX, MANTISSA, \ + CLEAR_UNDERFLOW); \ + ret = (TYPE) w; \ + CHECK_NARROW_TINY (ret, w, NARROW_MIN_NORMAL (TYPE)); \ \ CHECK_NARROW_DIV (ret, (X), (Y)); \ return ret; \ @@ -371,7 +395,10 @@ if (tmp == 0) \ ret = (TYPE) (math_opt_barrier (X) * (Y) + (Z)); \ else \ - ret = (TYPE) tmp; \ + { \ + ret = (TYPE) tmp; \ + CHECK_NARROW_TINY (ret, tmp, NARROW_MIN_NORMAL (TYPE)); \ + } \ \ CHECK_NARROW_FMA (ret, (X), (Y), (Z)); \ return ret; \ diff --git ./sysdeps/alpha/math-narrow-tininess.h ./sysdeps/alpha/math-narrow-tininess.h new file mode 100644 index 0000000000..66dbda9688 --- /dev/null +++ ./sysdeps/alpha/math-narrow-tininess.h @@ -0,0 +1,61 @@ +/* Underflow signalling for narrowing functions. Alpha version. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _MATH_NARROW_TININESS_H +#define _MATH_NARROW_TININESS_H 1 + +#include + +/* IEEE 754 determines tininess after rounding from the result rounded to + the precision of the destination with an unbounded exponent range, but + Alpha determines it from the delivered result. The two differ where + the value is tiny but reaches the smallest normal once rounded, since + the spacing below the smallest normal is twice that of the binade the + unbounded rounding lands in. Alpha signals no underflow for such a + result. For example fdiv (-0x4p-128, 0x1.000002p+0) in FE_DOWNWARD has + the exact quotient -0x1.fffffc0000080p-127, which rounds with an + unbounded exponent range to -0x1.fffffep-127 and so is tiny, but is + delivered as -0x1p-126. + + Signal underflow for that case. RET is the narrowed result, W the + round-to-odd value it came from and MIN_NORMAL the smallest positive + normal value of the narrower type. Rounding W with the exponent range + unbounded is what decides whether underflow is due: dividing by + MIN_NORMAL is exact and moves the value into the normal range, so the + conversion that follows rounds the significand alone. + + The comparisons are the quiet ones, as RET is a NaN whenever an + argument was. */ +#ifdef FE_UNDERFLOW +# define CHECK_NARROW_TINY(RET, W, MIN_NORMAL) \ + do \ + { \ + if (((RET) == (MIN_NORMAL) || (RET) == -(MIN_NORMAL)) \ + && isless (W, MIN_NORMAL) && isgreater (W, -(MIN_NORMAL))) \ + { \ + __typeof (RET) __scaled = (__typeof (RET)) ((W) / (MIN_NORMAL)); \ + if (__scaled > -1 && __scaled < 1) \ + __feraiseexcept (FE_UNDERFLOW); \ + } \ + } \ + while (0) +#else +# define CHECK_NARROW_TINY(RET, W, MIN_NORMAL) do { } while (0) +#endif + +#endif /* math-narrow-tininess.h */ diff --git ./sysdeps/generic/math-narrow-tininess.h ./sysdeps/generic/math-narrow-tininess.h new file mode 100644 index 0000000000..9fc1e62a02 --- /dev/null +++ ./sysdeps/generic/math-narrow-tininess.h @@ -0,0 +1,29 @@ +/* Underflow signalling for narrowing functions. Generic version. + Copyright (C) 2026 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _MATH_NARROW_TININESS_H +#define _MATH_NARROW_TININESS_H 1 + +/* Narrowing a round-to-odd value signals underflow by itself wherever + tininess is determined as IEEE 754 describes it, so there is nothing + to do here. RET is the narrowed result, W the round-to-odd value it + came from and MIN_NORMAL the smallest positive normal value of the + narrower type. */ +#define CHECK_NARROW_TINY(RET, W, MIN_NORMAL) do { } while (0) + +#endif /* math-narrow-tininess.h */ diff --git ./sysdeps/ieee754/dbl-64/s_fmaf.c ./sysdeps/ieee754/dbl-64/s_fmaf.c index 639c273486..f3b206d40e 100644 --- ./sysdeps/ieee754/dbl-64/s_fmaf.c +++ ./sysdeps/ieee754/dbl-64/s_fmaf.c @@ -21,8 +21,24 @@ #include #include #include +#include +#include #include "math_config.h" +#if ! USE_FMAF_BUILTIN +/* Narrow the double result to float. The conversion is what raises + underflow, so architectures that do not determine tininess as IEEE 754 + describes it compensate here; elsewhere CHECK_NARROW_TINY does + nothing and this is a plain conversion. */ +static inline float +narrow_fmaf_result (double result) +{ + float ret = (float) result; + CHECK_NARROW_TINY (ret, result, FLT_MIN); + return ret; +} +#endif + float __fmaf (float x, float y, float z) { @@ -37,17 +53,17 @@ __fmaf (float x, float y, float z) /* If not exact or at round to even boundary, the result is correct in all rounding modes. */ if (__glibc_likely ((u & 0xfffffff) != 0)) - return result; + return narrow_fmaf_result (result); /* Also check if the double result appears exact when it might not be and thus it will not set the underflow flag if denormal. */ if ((u & 0x10000000) == 0 && ((u >> MANTISSA_WIDTH) & 0x7ff) > EXPONENT_BIAS - 126) - return result; + return narrow_fmaf_result (result); /* Return if result is exact in all rounding modes. */ if (result - xy == z && result - z == xy) - return result; + return narrow_fmaf_result (result); /* This is where 'double-rouding' might return a wrong value, and thus needs adjusting the low-order bits in the direction of the error. */ @@ -62,7 +78,7 @@ __fmaf (float x, float y, float z) u++; else u--; - return asdouble (u); + return narrow_fmaf_result (asdouble (u)); #endif /* ! USE_FMAF_BUILTIN */ } #ifndef __fmaf