From nobody Wed Dec 24 18:01:52 2025 Received: from casper.infradead.org (casper.infradead.org [90.155.50.34]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C04B1131E43 for ; Wed, 24 Jan 2024 18:58:50 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=90.155.50.34 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706122732; cv=none; b=WIwNvbUoP32Lr9UDWxKEM77qWOLhuYx+OM8F3BfAg09vikbWPS1TAwm0rlKxpgJMml5fgiK6sau8fXb4Kcx1AUC5rIXFoQ9oVaA74TXD6rtW8/o7lLEMs0fTLo4L0b/fjXAckXZ1O0rZKERYaJdGiYFA5ITu2Cdsh/MfN0jwy3M= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1706122732; c=relaxed/simple; bh=ounInQX1OopqHF9P5Jemi1qCSNZiwDnPodYtsMa6jN4=; h=Date:From:To:Cc:Subject:Message-ID:MIME-Version:Content-Type: Content-Disposition; b=PF1pzY3keQcgiTFSqWG/+ETyaPC9bZh2bMYxY+R+39e4rk2v/UXRXvX75KKUtiVV9r/e0wto48HdOZazsG4xQR2TpOXKLZY1q/Mrx+b6nbTvlsPe/1NZSr3S9YHrHXzlvy86TGtVs8y3mytk0OaS2VnqRyWRbBcc0ZHgHU4tN4M= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=infradead.org; spf=none smtp.mailfrom=infradead.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b=DZgi7HRl; arc=none smtp.client-ip=90.155.50.34 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=infradead.org Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=infradead.org Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=infradead.org header.i=@infradead.org header.b="DZgi7HRl" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=infradead.org; s=casper.20170209; h=Content-Type:MIME-Version:Message-ID: Subject:Cc:To:From:Date:Sender:Reply-To:Content-Transfer-Encoding:Content-ID: Content-Description:In-Reply-To:References; bh=5+inbcM5m5+QxrTHMiU6DwL2MPsH0wdVrLya+aRCkTM=; b=DZgi7HRlytRnzcaZMWtaFIXtdd N2hCVVQvCr8holm6KQcvvkjPAci5mpcNGISTSxF2NJO5XIZdAk4D3NzQAET32ndrWvkxwVcdhrmWp lArx/UM8r/TdvGdgppF7yCXZ1/NYTbnbCpLBBC8gvA5MMQRMGDAjxqfsTmIBYdYtNz28ME/QiosgM ce61m7k9uY5Nv+plKtUeb6PydlREEiXURyVNJgvmcLLuncLjgLgAxFAWPUBjqzlPDVGRLTV8gSloX ag1T3euN2ht3hd49uQpO/8HttfwKRfMq4bwMHSSdOiJbYMA0eH4swbGSRRk1J6xs++6hcIqqK6CoZ +5huGq1w==; Received: from willy by casper.infradead.org with local (Exim 4.97.1 #2 (Red Hat Linux)) id 1rSiST-00000007V0m-1ZqL; Wed, 24 Jan 2024 18:58:45 +0000 Date: Wed, 24 Jan 2024 18:58:45 +0000 From: Matthew Wilcox To: Petr Mladek , Steven Rostedt , Andy Shevchenko , Rasmus Villemoes , Sergey Senozhatsky Cc: Randy Dunlap , linux-kernel@vger.kernel.org Subject: [RFC] Printing numbers in SI units Message-ID: Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Disposition: inline Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" I was looking at hugetlbfs and it has several snippets of code like this: string_get_size(huge_page_size(h), 1, STRING_UNITS_2, buf, 32); pr_warn("HugeTLB: allocating %u of page size %s failed node%d. Onl= y allocated %lu hugepages.\n", h->max_huge_pages_node[nid], buf, nid, i); That's not terribly ergonomic, so I wondered if I could do better. Unfortunately, I decided to do it using the SPECIAL flag which GCC warns about. But I've written the code now, so I'm sending it out in case anybody has a better idea for how to incorporate it. diff --git a/lib/test_printf.c b/lib/test_printf.c index 69b6a5e177f2..69af4d24a814 100644 --- a/lib/test_printf.c +++ b/lib/test_printf.c @@ -178,6 +178,16 @@ test_number(void) * behaviour. */ test("00|0|0|0|0", "%.2d|%.1d|%.0d|%.*d|%1.0d", 0, 0, 0, 0, 0, 0); + + /* + * C23 does not define the effect of "alternative form". Indeed + * I think it actually defines it to be Undefined Behaviour which + * apparently lets the compiler delete your entire source code. + */ + test("2KiB", "%#d", 2048); + test("2MiB", "%#d", 2048 * 1024); + test("1GiB", "%#d", 1024 * 1024 * 1024); + test("1000MiB", "%#d", 1024 * 1024 * 1000); } =20 static void __init diff --git a/lib/vsprintf.c b/lib/vsprintf.c index 552738f14275..a702582c598c 100644 --- a/lib/vsprintf.c +++ b/lib/vsprintf.c @@ -370,6 +370,56 @@ char *put_dec(char *buf, unsigned long long n) =20 #endif =20 +/* + * precision is the number of digits after the decimal place. we limit + * it to two, because more would be complicated and unnecessary. + */ +static noinline_for_stack +char *put_si(char *buf, unsigned long long n, int precision) +{ + char *name =3D "KMGTPEZB"; + int unit =3D 0; + unsigned remainder =3D 0; + + if (precision > 2) + precision =3D 2; + + while (n >=3D 1024) { + remainder =3D n % 1024; + n /=3D 1024; + unit++; + } + + remainder *=3D 1000; + remainder /=3D 1024; + + /* Round up */ + if (precision =3D=3D 2) + remainder +=3D 500; + else if (precision =3D=3D 1) + remainder +=3D 50; + else + remainder +=3D 5; + if (remainder >=3D 1000) { + remainder -=3D 1000; + n +=3D 1; + } + + *buf++ =3D 'B'; + if (unit > 0) { + *buf++ =3D 'i'; + *buf++ =3D name[unit - 1]; + } + + if (precision > 0) { + *((u16 *)buf) =3D decpair[remainder / 10]; + buf +=3D precision; + *buf++ =3D '.'; + } + + return put_dec_trunc8(buf, n); +} + /* * Convert passed number to decimal string. * Returns the length of string. On buffer overflow, returns 0. @@ -507,6 +557,9 @@ char *number(char *buf, char *end, unsigned long long n= um, tmp[i++] =3D (hex_asc_upper[((unsigned char)num) & mask] | locase); num >>=3D shift; } while (num); + } else if (spec.flags & SPECIAL) { + i =3D put_si(tmp, num, precision) - tmp; + precision =3D i; } else { /* base 10 */ i =3D put_dec(tmp, num) - tmp; }