0

It's very common to need to divide integers but get the result rounded up rather than down. For a while now, I've been using the following function for that mini-idiom:

template <typename S, typename T>
constexpr inline S div_rounding_up(const S& dividend, const T& divisor) 
{
    return (dividend + divisor - 1) / divisor;
}

This has (at least) the following deficiences, or what may be seen as deficiencies:

  • While it works "as promised" with negative operands - it's called div_rounding_up - it would probably make more sense to have this kind of function round away-from-zero, since x / y for negative x and positive y rounds towards zero. In other words, maybe what I should be implementing is div_rounding_away_from_zero, which would be commutative with inversion: Letting auto f = [&y](const S& x) { return div_rounding_away_from_zero(x,y); } we would have f(-x) == -f(x).
  • Overflow near the end of the domain of S.
  • Possibly weird behavior when sizeof(S) > sizeof(T).
  • long function name...

While you can easily think of ways of addressing each of these, they then result in other possible deficiencies, like conditionals in the code, or a reliance on calculating the modulus which may be expensive.

So is there a "right way" to implement this? By "right" I mean semantically pleasing, efficient, avoiding as many of the deficiencies above, and hopefully widely-used.

Notes:

  • If you think the function should be strictly constained to only work with non-negative arguments, say so. That would be a bit problematic IMHO since we don't want to constrain the types to be unsigned, and we don't want to check the operands' signs. That seems like something I would use a contact for - and C++ doesn't have them yet.
  • Is using std::div and variants a good idea here?
  • Performance is more important than readability. At worst one can add a comment.
  • Code should not be single-architecture-specific (but if you want to ifdef-else for different architectures then be my guest).
  • Code should not assume a specific compiler.
einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • There is no need to sum dividend and divisor: distribute the division across that sum. – Bathsheba Jan 26 '17 at 10:02
  • @Bathsheba If I understood you well, would your suggesstion work for integer division? – A.S.H Jan 26 '17 at 10:12
  • For the different types problem perhaps you could use `T` for both types and require the caller to pass the same type – M.M Jan 26 '17 at 10:13
  • @A.s.h. Yes it would, since you'd be bringing out an integer from the expression. – Bathsheba Jan 26 '17 at 10:17
  • @M.M: I didn't say that. It's just that it's extremely rare for me to want/have to do this for negative operands. – einpoklum Jan 26 '17 at 10:52
  • @M.M: Regarding `inline` for templated functions - I believe you're [mistaken](http://stackoverflow.com/a/10536588/1593077). – einpoklum Jan 26 '17 at 10:55
  • @Bathsheba: Are you suggesting I perform two divisions? – einpoklum Jan 26 '17 at 10:56
  • @M.M: Let me know if my edits are satisfactory in this respect. – einpoklum Jan 26 '17 at 11:12
  • @einpoklum `it rounds up, instead of rounding away-from-zero - which is what I would expect for negative numbers (since x / y for negative x and positive y rounds towards zero).` Why would you expect a function called "round up" to not round up, but away from zero? – eerorika Jan 26 '17 at 11:14
  • @M.M: How about now? – einpoklum Jan 26 '17 at 11:20
  • @user2079303: See edit. – einpoklum Jan 26 '17 at 11:20
  • I don't see what's so bad about a simple conditional.Do a normal division, do a multiplication to see if `result*divisor == dividend`, if not so, add 1 (or -1 if negative), return final value. – coyotte508 Jan 26 '17 at 11:36
  • No I'm not suggesting that. One of the terms will be 1. That, elegantly, reduces he possibility of overflow. – Bathsheba Jan 26 '17 at 11:40
  • @coyotte508: And introduce a highly unpredictable branch? I don't think so... – einpoklum Jan 26 '17 at 11:47
  • @Bathsheba: I'm not quite following... how about making your suggestin an answer and being more explicit? – einpoklum Jan 26 '17 at 11:49
  • Cuz I'm on a plane, innit. Safety demo. There must be a duplicate for this part of the question: this is a well-known problem. – Bathsheba Jan 26 '17 at 11:51
  • @Bathsheba: Original, and valid, excuse I suppose. Anyway, I would also think there must be a dupe for this, and yet - I couldn't find it. – einpoklum Jan 26 '17 at 12:11

1 Answers1

1

Possibly weird behavior when sizeof(S) > sizeof(T).

It would probably be better to use a single type argument, and let the user deal with the conversion that they want. This is the approach used by the standard library math functions.

Overflow near the end of the domain of S.

A remainder based rounding does not have this problem.

reliance on calculating the modulus which may be expensive.

You are already calculating a division, which is expensive. On x86 at least, the division instruction stores the remainder in a register, and a decent implementation of std::div will use it. A modern compiler will even be able to optimize explicit use of division and remainder operation as well.

Is using std::div and variants a good idea here?

Sure.

If you think the function should be strictly constained to only work with non-negative arguments, say so.

I think you should at least require that arguments must have the same sign. The direction of rounding of both division and remainder operator (also by extension std::div since C++11) is implementation defined. With this requirement, there is no difference between rounding away from zero, and rounding up, since no supported result is negative.

template <typename T> // single type argument
constexpr T           // constexpr implies inline
div_round_up
(const T& dividend, const T& divisor) 
{
    // no overflows, only 1 expensive operation
    std::div_t dv = std::div(dividend, divisor);
    return dv.quot + (!!dv.rem);
}
eerorika
  • 232,697
  • 12
  • 197
  • 326