Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions include/zephyr/sys/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,20 +339,30 @@ extern "C" {
#define WB_DN(x) ROUND_DOWN(x, sizeof(void *))

/**
* @brief Divide and round up.
* @brief Divide and round up towards infinity, akin to ceil().
*
* @note Works with negative operands, asserts if denominator is zero.
*
* Example:
* @code{.c}
* DIV_ROUND_UP(3, 0); // Asserts
* DIV_ROUND_UP(1, 2); // 1
* DIV_ROUND_UP(3, 2); // 2
* DIV_ROUND_UP(-3, -2); // 2
* DIV_ROUND_UP(3, -2); // 1
* DIV_ROUND_UP(-3, 2); // 1
* @endcode
*
* @param n Numerator.
* @param d Denominator.
*
* @return The result of @p n / @p d, rounded up.
*/
#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
#define DIV_ROUND_UP(n, d) ((d == 0) \
Copy link
Contributor

@pdgendt pdgendt Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:

#define DIV_ROUND_UP(n, d) ({                                                                      \
		__typeof__(n) un = (n);                                                            \
		__typeof__(d) ud = (d);                                                            \
		__ASSERT_NO_MSG(ud != 0);                                                          \
		((un + ud - 1) / ud);                                                              \
	})

Inspired by #96478

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

@fabiobaltieri fabiobaltieri Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah :-(

include/zephyr/drivers/can.h:           uint32_t data_32[DIV_ROUND_UP(CAN_MAX_DLEN, sizeof(uint32_t))];
include/zephyr/logging/log_msg.h:       long long _ll_buf[DIV_ROUND_UP(len, sizeof(long long))]; \
include/zephyr/logging/log_msg.h:       long double _ld_buf[DIV_ROUND_UP(len, sizeof(long double))]; \

and plenty more, I think we should have a DIV_ROUND_UP and div_round_up, once you do the ({ thing it effectively becomes a function

? __ASSERT_NO_MSG(false) \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking closer at this, this won't work. The ternary operator won't be able to expand the macro for asserts here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We will have to use a static inline function then, but that needs a fixed type for return and parameters.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might need to resort to _Generic to make it work.

: ((((n % d) == 0) || ((n < 0) ^ (d < 0))) \
? (n / d) \
: (n / d) + 1))

/**
* @brief Divide and round to the nearest integer.
Expand All @@ -369,10 +379,11 @@ extern "C" {
*
* @return The result of @p n / @p d, rounded to the nearest integer.
*/
#define DIV_ROUND_CLOSEST(n, d) \
(((((__typeof__(n))-1) < 0) && (((__typeof__(d))-1) < 0) && ((n) < 0) ^ ((d) < 0)) \
? ((n) - ((d) / 2)) / (d) \
: ((n) + ((d) / 2)) / (d))
#define DIV_ROUND_CLOSEST(n, d) ((d == 0) \
? __ASSERT_NO_MSG(false) \
: (((((__typeof__(n))-1) < 0) && (((__typeof__(d))-1) < 0) && ((n) < 0) ^ ((d) < 0)) \
? ((n) - ((d) / 2)) / (d) \
: ((n) + ((d) / 2)) / (d)))

#ifndef MAX
/**
Expand Down
10 changes: 10 additions & 0 deletions tests/unit/util/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,6 +681,16 @@ ZTEST(util, test_DIV_ROUND_UP)
zassert_equal(DIV_ROUND_UP(0, 1), 0);
zassert_equal(DIV_ROUND_UP(1, 2), 1);
zassert_equal(DIV_ROUND_UP(3, 2), 2);
zassert_equal(DIV_ROUND_UP(-3, -2), 2);
zassert_equal(DIV_ROUND_UP(3, -2), 1);
zassert_equal(DIV_ROUND_UP(-3, 2), 1);
}

ZTEST(util, test_assert_DIV_ROUND_UP_and_DIV_ROUND_CLOSEST)
{
ztest_set_assert_valid(true);
DIV_ROUND_UP(3, 0);
DIV_ROUND_CLOSEST(3, 0);
}

ZTEST(util, test_DIV_ROUND_CLOSEST)
Expand Down
Loading