All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alejandro Colomar <alx.manpages@gmail.com>
To: GNU C Library <libc-alpha@sourceware.org>
Cc: linux-man <linux-man@vger.kernel.org>
Subject: bug in roundup(3) from <sys/param.h>
Date: Mon, 16 Jan 2023 21:46:55 +0100	[thread overview]
Message-ID: <1825384a-e02a-c19d-eb22-aedc749046bc@gmail.com> (raw)


[-- Attachment #1.1: Type: text/plain, Size: 4194 bytes --]

Hi!

I was trying to understand what roundup() is (defined in <sys/param,h>).

It seems to be kind of:

SYNOPSIS
        #include <sys/param.h>

        roundup(x, step);

DESCRIPTION
        This  macro  rounds  x to the nearest multiple of step that is not less
        than x.

I found that it doesn't work for negative numbers; but that's expected, and it 
could be documented as such.  However, it doesn't work nicely with unsigned 
integers either: for values close to zero, where wrap around happens, the result 
is also bogus.  See my experiments below.



$ sed -n 92,98p /usr/include/x86_64-linux-gnu/sys/param.h
#ifdef __GNUC__
# define roundup(x, y)  (__builtin_constant_p (y) && powerof2 (y)             \
                          ? (((x) + (y) - 1) & ~((y) - 1))                     \
                          : ((((x) + ((y) - 1)) / (y)) * (y)))
#else
# define roundup(x, y)  ((((x) + ((y) - 1)) / (y)) * (y))
#endif


$ cat roundup.c
#include <stdint.h>
#include <stdio.h>
#include <sys/param.h>

int
main(void)
{
	/* signed */
	{
		int32_t n, m;

		m = 3;
		n = 10;

		puts("signed:");
		for (int32_t x = -n; x < 0; x++)
			printf("roundup(%d, %d) == %d\n", x, m, roundup(x, m));

		puts("");
		for (int32_t x = 0; x < n; x++)
			printf("roundup(%d, %d) == %d\n", x, m, roundup(x, m));

		puts("");

		for (int32_t x = INT32_MIN; x < INT_MIN + n; x++)
			printf("roundup(%d, %d) == %d\n", x, m, roundup(x, m));

		puts("");
		for (int32_t x = INT32_MAX; x > INT32_MAX - n; x--)
			printf("roundup(%d, %d) == %d\n", x, m, roundup(x, m));
	}

	/* unsigned */
	{
		uint32_t n, m;

		m = 3;
		n = 10;

		puts("\nunsigned:");
		for (uint32_t x = 1; x < n; x++)
			printf("roundup(%u, %u) == %u\n", -x, m, roundup(-x, m));

		puts("");
		for (uint32_t x = 0; x < n; x++)
			printf("roundup(%u, %u) == %u\n", x, m, roundup(x, m));
	}
}

$ cc -Wall -Wextra -Werror roundup.c
$ ./a.out
signed:
roundup(-10, 3) == -6
roundup(-9, 3) == -6
roundup(-8, 3) == -6
roundup(-7, 3) == -3
roundup(-6, 3) == -3
roundup(-5, 3) == -3
roundup(-4, 3) == 0
roundup(-3, 3) == 0
roundup(-2, 3) == 0
roundup(-1, 3) == 0
/* These values are nonsense, but OK, let's ignore the negative */

roundup(0, 3) == 0
roundup(1, 3) == 3
roundup(2, 3) == 3
roundup(3, 3) == 3
roundup(4, 3) == 6
roundup(5, 3) == 6
roundup(6, 3) == 6
roundup(7, 3) == 9
roundup(8, 3) == 9
roundup(9, 3) == 9
/* These make sense */

roundup(-2147483648, 3) == -2147483646
roundup(-2147483647, 3) == -2147483643
roundup(-2147483646, 3) == -2147483643
roundup(-2147483645, 3) == -2147483643
roundup(-2147483644, 3) == -2147483640
roundup(-2147483643, 3) == -2147483640
roundup(-2147483642, 3) == -2147483640
roundup(-2147483641, 3) == -2147483637
roundup(-2147483640, 3) == -2147483637
roundup(-2147483639, 3) == -2147483637
/* Nonsense; ignore the negative */

roundup(2147483647, 3) == -2147483646  // UB; ignore
roundup(2147483646, 3) == -2147483646  // UB; ignore
roundup(2147483645, 3) == 2147483646
roundup(2147483644, 3) == 2147483646
roundup(2147483643, 3) == 2147483643
roundup(2147483642, 3) == 2147483643
roundup(2147483641, 3) == 2147483643
roundup(2147483640, 3) == 2147483640
roundup(2147483639, 3) == 2147483640
roundup(2147483638, 3) == 2147483640
/* These make sense */

unsigned:
roundup(4294967295, 3) == 0  // Wrong; should be: 4294967295
roundup(4294967294, 3) == 0  // Wrong; should be: 4294967295
roundup(4294967293, 3) == 4294967295
roundup(4294967292, 3) == 4294967292
roundup(4294967291, 3) == 4294967292
roundup(4294967290, 3) == 4294967292
roundup(4294967289, 3) == 4294967289
roundup(4294967288, 3) == 4294967289
roundup(4294967287, 3) == 4294967289

roundup(0, 3) == 0
roundup(1, 3) == 3
roundup(2, 3) == 3
roundup(3, 3) == 3
roundup(4, 3) == 6
roundup(5, 3) == 6
roundup(6, 3) == 6
roundup(7, 3) == 9
roundup(8, 3) == 9
roundup(9, 3) == 9


Do you think this is something to be fixed without important performance 
penalties, or should we just document the bug and live with it?


Cheers,

Alex



-- 
<http://www.alejandro-colomar.es/>

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

             reply	other threads:[~2023-01-16 20:47 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-01-16 20:46 Alejandro Colomar [this message]
2023-01-17  2:22 ` bug in roundup(3) from <sys/param.h> Alejandro Colomar
2023-01-17 14:55 ` Adhemerval Zanella Netto

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1825384a-e02a-c19d-eb22-aedc749046bc@gmail.com \
    --to=alx.manpages@gmail.com \
    --cc=libc-alpha@sourceware.org \
    --cc=linux-man@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.