| Message ID | 20260824221612.4132669-1-mattst88@gmail.com (mailing list archive) |
|---|---|
| Headers |
Return-Path: <libc-alpha-bounces~patchwork=sourceware.org@sourceware.org> 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 AAC804BA79BD for <patchwork@sourceware.org>; Mon, 24 Aug 2026 22:16:48 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AAC804BA79BD 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=UhIt0/k1 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-yx1-xb132.google.com (mail-yx1-xb132.google.com [IPv6:2607:f8b0:4864:20::b132]) by sourceware.org (Postfix) with ESMTPS id E9EC84BA798C for <libc-alpha@sourceware.org>; Mon, 24 Aug 2026 22:16:15 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org E9EC84BA798C 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 E9EC84BA798C Authentication-Results: sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::b132 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787609776; cv=none; b=a7hKt3iGTiXhiy/WqVYPbYn07HeJEq4EojTFtWUmxfKB4PZulyBQPxcsMdiJY0SAgNvpHzEyCV7DNQTFW9ij6srGoGyinzhmGWT2xlSVUAGEgpzzZ4OjF2rsBS+e+fv4oo3MrTLhQxAy4ISoy9zDe+HoN38qaeAwrQ9ihfDZlII= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1787609776; c=relaxed/simple; bh=b7E3vOdR7Usd2iTWWkBW64fTb0eM7nm2+Lc/xB1JJTQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=tXUpbJ3RPwlQKUP0qYxttAcOLhcbtcurGngRTHPc0t6RuTkuJ5aWq2WZdQfUtjRoJmfVxbx5flTyf4s1mJzXhTFHpYxQxnqdOIoKpr62Pr4iYqflfDY4BldzKA3+UEyDp2r40Tn7rIm7qNisVick2268kHlQjofCoUg9BbmHT7o= 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=UhIt0/k1 DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org E9EC84BA798C Received: by mail-yx1-xb132.google.com with SMTP id 956f58d0204a3-66d1442b24eso741909d50.1 for <libc-alpha@sourceware.org>; Mon, 24 Aug 2026 15:16:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787609775; x=1788214575; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to:content-type; bh=z+rJmx7i+rZXdoJwTDM60rpLLIGv4zr3FrEwz/kBqBo=; b=UhIt0/k1gVqpPRNjYsSwzOb1RR5makAMCuPByCFnTOzTsfQi+MwUjD4oWS8oIKZDPS Y+1TtynHYyd2U2yJy27CEGnzV4gi7nBepYOoapEcGhLogYbj3I7MK+FahQSunxI5h9i/ z+ZzDFUNrIcoBHYz1lv2q+GIsca+S9oYvUmTHbtlMVPJMpouwWqWfhtIPwR1tAMfu4jx 2ZzwfNhT/bZ3I4XPYtyVsucKZ1tl7YAGu9V6WP/UU9sFdjblv6KDI9YiH3c79FrEqApv W6fwNQ2OzggFDAr+uagWAE3ykOY80IAHrJ1s1AP6DprGvCshJPBABxzE+FwayGwUjDmn tVJA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787609775; x=1788214575; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to:content-type; bh=z+rJmx7i+rZXdoJwTDM60rpLLIGv4zr3FrEwz/kBqBo=; b=FG3QMn+pbCih8/b01h1rvX0OeckqKa53THBT7NuvTit0TtwX4bE9kC15LEYsAJcobd fIbvYlcc298jVXxhXyBxN1k5+NVqYtlWtdP5q79bwzwnC11bFdhBc9qflvHBPYZPsUVa y3lVSqBFCPPy8fnENhguK7E0abdgNUPiR6jD4WdSJylfEMzHcaPg+qX3c2k+LJBpVr8r 0BKxtQKD7DM9yCK76Ot2gikL/JO5yj28N49u/1xuHY84ouVrZzSR+dDPBfholPQfcJQ7 br9j1/Gyg7R5aPXFs3o15Fp4JdA/lw89KCwJVvf7h7amB6UZILeiQTFv5U/04K/2IHkw 5WNw== X-Gm-Message-State: AFuF++k+loDqh3t41im4ogyK2XokBnV4HSbEzqoHHa5qvHvlsy2OqM/R fmFm1XlJZnRMQ3c9yirAoCGJ3qau9LTP96UAS7bsw7dBlggQmUpBKc53RUm85A== X-Gm-Gg: AR+sD10NqcgrjZzh/zDbuH8vfTXVMDKtcFIUS+IUj6khee91LeKUyv5RUeN6obapI31 0rZRCobqPoDoODIWXThdHxfmUyIS4zbfeDUtWId7MW52gpRZlJ0UC4QZkaAJZJV91wfgkwPUPCn ahIanNA8dY9cVH2uJ60ZdfG/d+MQIzLR+d2Si9mxFWMnnbxZs3t6opE6TFIo7YuDzto/nx7kk3k jNFyX3K+PC1YhirpPuxXUTvzEmcukSvZ6JhPzF6y8vw+KpN3LsOrmVSUiyBAAUVXGDX5+YkrLfK ya9B1hiH6LsYmYNTkBOMYR7KICGLwLOZX3XJ5OvdiCkkEo7zgGYthzhML3j94ede/e04jPY5hTM 46CBtqArsSdxs3bWNFqkhZJ9AXLo9V68MtBr53DdXa3xIYEayv6TjyJyCQd7neFjBq1OZwL3VQR Yew9CNQuRE+CJmd6TNSsFVj2RD7hw6f/7BufBafG+RHWKmJcd7Wg7SAVFT X-Received: by 2002:a05:690e:4849:b0:668:db:8c35 with SMTP id 956f58d0204a3-66ce4a326c5mr8574690d50.20.1787609775099; Mon, 24 Aug 2026 15:16:15 -0700 (PDT) Received: from localhost ([67.197.117.177]) by smtp.gmail.com with ESMTPSA id 956f58d0204a3-66cf4624e9esm4500405d50.2.2026.08.24.15.16.13 for <libc-alpha@sourceware.org> (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 24 Aug 2026 15:16:14 -0700 (PDT) From: Matt Turner <mattst88@gmail.com> To: libc-alpha@sourceware.org Subject: [PATCH 0/7] stdio-common: Verify printf format tests with Python rather than AWK Date: Mon, 24 Aug 2026 18:16:05 -0400 Message-ID: <20260824221612.4132669-1-mattst88@gmail.com> X-Mailer: git-send-email 2.54.0 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-3.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, RCVD_IN_DNSWL_NONE, RCVD_IN_PBL, 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 <libc-alpha.sourceware.org> List-Unsubscribe: <https://sourceware.org/mailman/options/libc-alpha>, <mailto:libc-alpha-request@sourceware.org?subject=unsubscribe> List-Archive: <https://sourceware.org/pipermail/libc-alpha/> List-Post: <mailto:libc-alpha@sourceware.org> List-Help: <mailto:libc-alpha-request@sourceware.org?subject=help> List-Subscribe: <https://sourceware.org/mailman/listinfo/libc-alpha>, <mailto:libc-alpha-request@sourceware.org?subject=subscribe> Errors-To: libc-alpha-bounces~patchwork=sourceware.org@sourceware.org |
| Series |
stdio-common: Verify printf format tests with Python rather than AWK
|
|
Message
Matt Turner
Aug. 24, 2026, 10:16 p.m. UTC
The formatted printf output tests verify their records against GNU AWK, on
the grounds that it supplies an implementation of format processing that is
independent from ours. It does not do that as cleanly as one would like.
AWK has to run in the bignum mode for the floating-point conversions,
because otherwise it uses the system sprintf(3) internally and we end up
verifying our code against itself. That in turn makes gawk compiled with
MPFR support a requirement for testing the library at all. Beyond that
gawk mishandles a number of cases which the AWK script then has to undo by
hand: the extraneous leading 0 produced for the alternative form with the
octal conversion, the 0 produced where no characters are expected for the
hexadecimal conversions, the missing + and space characters for a zero
value with the precision of zero, and a collection of sign, flag and field
width anomalies for Inf and NaN values. Each such workaround suppresses
whatever we might get wrong in the same place. The a, A, b, and B
conversions cannot be verified at all, because gawk either does not handle
them or produces different output. And where gawk was built without the f
and F conversions the tests reported unsupported rather than checking
anything.
The first patch replaces the AWK script with an equivalent one written in
Python, which is already a requirement for building the library. Rather
than calling into any formatting routine it computes the reference output
directly, using exact integer and rational arithmetic. Working exactly
means the result does not depend on the range or precision of any host
floating-point type, so the wider types are handled without
arbitrary-precision arithmetic having to be built into the interpreter, and
none of the workarounds listed above are needed: the corner cases they
cover are computed correctly. The capability probes go away along with the
AWK script, so f and F are now always verified, and the note on MPFR is
dropped from the installation instructions.
The rest of the series spends the freedom that buys:
2 stops iterating over the huge width for eleven of the twelve printf
family functions -- see below;
3 adds the b and B conversions, which gawk does not handle;
4 adds the a and A conversions, which gawk renders differently;
5 adds subnormal values, which needed the minimum exponent for the type
to be reported to the verification;
6 fixes a printf_fp bug the added coverage exposed: with the alternative
form, a g or G conversion that rounds into a new decade dropped the
trailing zeros it is required to keep, so that printf ("%#.2g", 99.9)
gave "1.e+02" rather than "1.0e+02";
7 marks a and A unsupported where long double has the IBM extended
format, which is a pair of doubles rather than a significand of a
single fixed width and so cannot be modelled the way the verification
does it.
This changes both what the tests cover and what they cost, so here are the
numbers. Measured on x86_64-linux-gnu with the tests run serially,
processor time for the tst-printf-format-* tests, which varies by under 2%
between runs:
tests results records CPU
before the series 288 576 32,588,304 935s
1 verify with Python 288 576 32,588,304 684s
2 huge width for printf only 288 576 27,563,064 375s
3 add b and B 288 576 33,879,864 392s
4 add a and A 336 672 37,258,224 402s
5 add subnormal values 336 672 38,609,568 411s
6 keep %#g trailing zeros 336 672 39,960,912 416s
7 skip a and A for IBM 336 672 39,960,912 416s
So the suite ends up checking 23% more records, spread over 48 more tests,
in 2.25 times less processor time. All 672 results pass, as do all 576
before the series.
Two things account for the time. Python turns out to be faster than gawk
in the bignum mode to begin with, once rendered digits are memoized per
value, which is what patch 1 alone buys; without the memoization the exact
arithmetic makes the long double conversions slower than AWK instead. The
rest is patch 2. HUGE_WIDTH is chosen so that none of the strings produced
are truncated, which for the floating-point types means every record it
takes part in carries hundreds or thousands of digits, and those records
dominate the cost of this whole family of tests. The digits are produced
by the same conversion code whichever of the printf family of functions is
used; what differs between the twelve of them is the sink the result is
written to, which the smaller widths cover already. Iterating over the
huge width for printf alone therefore gives up no coverage worth having.
Records are not equal in cost, which is why the two columns move
independently: the 15% of records patch 2 drops are the most expensive in
the suite and take 45% of the run time with them, while the b and B records
patch 3 adds are integer ones, and the a and A records patch 4 adds are
short as well, the hexadecimal significand having a fixed width. What
remains is mostly intrinsic -- the f and F conversions print the whole
integer part regardless of the precision requested, so LDBL_MAX runs to
some 4932 digits even at MID_WIDTH.
Patch 7 costs nothing here, as expected: powerpc64le is the one target with
the IBM extended format and it builds everything with the IEEE format
instead, so the conversions are only skipped where shared libraries have
been turned off.
Tested on x86_64-linux-gnu and powerpc64le-linux-gnu, and with the long
double conversions forced to the IBM extended format, where the a and A
conversions report unsupported and the rest continue to pass.
Comments
On 24/08/26 19:16, Matt Turner wrote: > The formatted printf output tests verify their records against GNU AWK, on > the grounds that it supplies an implementation of format processing that is > independent from ours. It does not do that as cleanly as one would like. > > AWK has to run in the bignum mode for the floating-point conversions, > because otherwise it uses the system sprintf(3) internally and we end up > verifying our code against itself. That in turn makes gawk compiled with > MPFR support a requirement for testing the library at all. Beyond that > gawk mishandles a number of cases which the AWK script then has to undo by > hand: the extraneous leading 0 produced for the alternative form with the > octal conversion, the 0 produced where no characters are expected for the > hexadecimal conversions, the missing + and space characters for a zero > value with the precision of zero, and a collection of sign, flag and field > width anomalies for Inf and NaN values. Each such workaround suppresses > whatever we might get wrong in the same place. The a, A, b, and B > conversions cannot be verified at all, because gawk either does not handle > them or produces different output. And where gawk was built without the f > and F conversions the tests reported unsupported rather than checking > anything. Hi Matt, Thanks for working on this. It turned out that using awk as a printf oracle was not the best approach, and the -M being on parole is not a good indication to keep relying on it (even though there is no actual plan to remove it). The Python multi-precision is also builtin, so one less conditional knob to kind in mind during testing. But I think the main advantage of this approach is it increases coverage (stdio-common goes from 1515 tests to 1611) and it is way faster (on my box stdio-common tests time is cut in half). > > The first patch replaces the AWK script with an equivalent one written in > Python, which is already a requirement for building the library. Rather > than calling into any formatting routine it computes the reference output > directly, using exact integer and rational arithmetic. Working exactly > means the result does not depend on the range or precision of any host > floating-point type, so the wider types are handled without > arbitrary-precision arithmetic having to be built into the interpreter, and > none of the workarounds listed above are needed: the corner cases they > cover are computed correctly. The capability probes go away along with the > AWK script, so f and F are now always verified, and the note on MPFR is > dropped from the installation instructions. I though we had Python as requirement only for testing, but we now do use for gen-as-const-headers since 7e1d42400c1b8f03316fe14176133c8853cd3bbe. > > The rest of the series spends the freedom that buys: > > 2 stops iterating over the huge width for eleven of the twelve printf > family functions -- see below; This can be pushed independently. > 3 adds the b and B conversions, which gawk does not handle; > 4 adds the a and A conversions, which gawk renders differently; > 5 adds subnormal values, which needed the minimum exponent for the type > to be reported to the verification; > 6 fixes a printf_fp bug the added coverage exposed: with the alternative > form, a g or G conversion that rounds into a new decade dropped the > trailing zeros it is required to keep, so that printf ("%#.2g", 99.9) > gave "1.e+02" rather than "1.0e+02"; This can done independently and I think it would be useful to open a bug report to track it. > 7 marks a and A unsupported where long double has the IBM extended > format, which is a pair of doubles rather than a significand of a > single fixed width and so cannot be modelled the way the verification > does it. > > This changes both what the tests cover and what they cost, so here are the > numbers. Measured on x86_64-linux-gnu with the tests run serially, > processor time for the tst-printf-format-* tests, which varies by under 2% > between runs: > > tests results records CPU > before the series 288 576 32,588,304 935s > 1 verify with Python 288 576 32,588,304 684s > 2 huge width for printf only 288 576 27,563,064 375s > 3 add b and B 288 576 33,879,864 392s > 4 add a and A 336 672 37,258,224 402s > 5 add subnormal values 336 672 38,609,568 411s > 6 keep %#g trailing zeros 336 672 39,960,912 416s > 7 skip a and A for IBM 336 672 39,960,912 416s > > So the suite ends up checking 23% more records, spread over 48 more tests, > in 2.25 times less processor time. All 672 results pass, as do all 576 > before the series. > > Two things account for the time. Python turns out to be faster than gawk > in the bignum mode to begin with, once rendered digits are memoized per > value, which is what patch 1 alone buys; without the memoization the exact > arithmetic makes the long double conversions slower than AWK instead. The > rest is patch 2. HUGE_WIDTH is chosen so that none of the strings produced > are truncated, which for the floating-point types means every record it > takes part in carries hundreds or thousands of digits, and those records > dominate the cost of this whole family of tests. The digits are produced > by the same conversion code whichever of the printf family of functions is > used; what differs between the twelve of them is the sink the result is > written to, which the smaller widths cover already. Iterating over the > huge width for printf alone therefore gives up no coverage worth having. On my box the main difference of stdio-common tests time while running in parallel was the 'stdio-common: Iterate over the huge width for one printf function only'. But at same time, with the new Python testing framework it does enable a bunch a more tests. > > Records are not equal in cost, which is why the two columns move > independently: the 15% of records patch 2 drops are the most expensive in > the suite and take 45% of the run time with them, while the b and B records > patch 3 adds are integer ones, and the a and A records patch 4 adds are > short as well, the hexadecimal significand having a fixed width. What > remains is mostly intrinsic -- the f and F conversions print the whole > integer part regardless of the precision requested, so LDBL_MAX runs to > some 4932 digits even at MID_WIDTH. > > Patch 7 costs nothing here, as expected: powerpc64le is the one target with > the IBM extended format and it builds everything with the IEEE format > instead, so the conversions are only skipped where shared libraries have > been turned off. > > Tested on x86_64-linux-gnu and powerpc64le-linux-gnu, and with the long > double conversions forced to the IBM extended format, where the a and A > conversions report unsupported and the rest continue to pass. So, as a side note, I started to check whether using mpfr/gmp directly would result in a simpler way to check this. The mpfr/gmp also had some corner cases that we need to handle (like no standard %a/%A support, missing sign for infinities and NaNs), and it required adding an additional build step in build-many-glibcs.py to enable it. It turned out to *not* be simpler than this approach. I will review the rest of this patchset.