Proposal for <numeric>: rounded-up integral division, rounding integers up and down to multiples

107 views
Skip to first unread message

Walt Karas

unread,
Dec 8, 2016, 1:39:54 PM12/8/16
to ISO C++ Standard - Future Proposals
#include <type_traits>


template <typename I>
struct mround_type
 
{
   
static_assert(
      std
::is_integral<I>::value, "mround_type requires integral type");


   
const I value;


   
constexpr explicit mround_type(I v) : value(v) { }
 
};


template <typename I>
constexpr auto mround(I i)
 
{
   
static_assert(
      std
::is_integral<I>::value,
     
"mround() parameter must be of integral type");


   
return(mround_type<I>(i));
 
}


// Rounded-up integral division.
template <typename I>
constexpr I operator / (I i, mround_type<I> factor)
 
{
   
return((i + factor.value - 1) / factor.value);
 
}


// Round up integer to multiple.
template <typename I>
constexpr I operator + (I i, mround_type<I> factor)
 
{
   
return((i / factor) * factor.value);
 
}


// Round down integer to multiple.
template <typename I>
constexpr I operator - (I i, mround_type<I> factor)
 
{
   
return(i - (i % factor.value));
 
}


static_assert(9 / mround(5) == 2, "");
static_assert(10 / mround(5) == 2, "");
static_assert(11 / mround(5) == 3, "");


static_assert(9 + mround(5) == 10, "");
static_assert(10 + mround(5) == 10, "");
static_assert(11 + mround(5) == 15, "");


static_assert(9 - mround(5) == 5, "");
static_assert(10 - mround(5) == 10, "");
static_assert(11 - mround(5) == 10, "");



Dawid Pilarski

unread,
Dec 9, 2016, 4:21:28 AM12/9/16
to std-pr...@isocpp.org
I do not quite like the idea of operator + and operator -. It's kind of misleading, that operator + and - has so strange meaning. Why not doing just simple function round_up_to_multiple, which would have cleaner interface?


--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1af197bf-4e85-4e4b-9486-dd7ca150c139%40isocpp.org.

Walt Karas

unread,
Dec 9, 2016, 11:30:40 AM12/9/16
to ISO C++ Standard - Future Proposals
I take your point, it has validity.  But round_up_to_multiple(Integral, Integral) has issues for people like me who don't have good memories.  Specifically, having two parameters of the same type (that are not commutative).  I would likely be constantly looking up the right parameter order.  And if I ddin't look it up and got it wrong, that would be a run-time bug, potentially one that was very hard to find.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.

Walt Karas

unread,
Dec 9, 2016, 11:44:56 AM12/9/16
to ISO C++ Standard - Future Proposals
A possible compromise would be:

Integral round_up_to_multiple(Integral i, mround_type<Integral> factor)

and also remove the explicit from mround_type's constructor.  Then people with bad memories could write round_up_to_multiple(9, mround(5)).  But for round_up_to_multiple(9, 5) to compile (for those with good memories), the compiler would have to search std for an implicit conversion for 5 based on the function being in std.  And, it would have to specialize templates as a part of the search.   Probably doesn't work that way.

Viacheslav Usov

unread,
Dec 9, 2016, 12:02:16 PM12/9/16
to ISO C++ Standard - Future Proposals
On Thu, Dec 8, 2016 at 7:39 PM, 'Walt Karas' via ISO C++ Standard - Future Proposals <std-pr...@isocpp.org> wrote:

There are two problems, which are related.

1. Your implementation of operator / is unsafe when the dividend is close to its max value, so that (+ factor - 1) would cause an overflow. I suppose it is for exposition only, and the real implementation should be safe, which can always be done.

2. The behaviour of "round up" ought to be specified under similar conditions, at least as UB; perhaps you could do better than that at least for some integral types.

Cheers,
V.

Walt Karas

unread,
Dec 10, 2016, 4:40:54 PM12/10/16
to ISO C++ Standard - Future Proposals
How about this alternative?

#include <type_traits>

template <typename I>
struct mround_type
  {
    static_assert(
      std::is_integral<I>::value, "mround_type requires integral type");

    const I value;

    constexpr explicit mround_type(I v) : value(v) { }

    // Rounded-up integral division.
    constexpr I div(I factor)
      {
        return((value + factor - 1) / factor);
      }

    // Round up integer to multiple.
    constexpr I up(I factor)
      {
        return(div(factor) * factor);
      }

    // Round down integer to multiple.
    constexpr I down(I factor)
      {
        return(value - (value % factor));
      }
  };

template <typename I>
constexpr auto mround(I i)
  {
    static_assert(
      std::is_integral<I>::value,
      "mround() parameter must be of integral type");

    return(mround_type<I>(i));
  }

static_assert(mround(9).div(5) == 2, "");
static_assert(mround(10).div(5) == 2, "");
static_assert(mround(11).div(5) == 3, "");

static_assert(mround(9).up(5) == 10, "");
static_assert(mround(10).up(5) == 10, "");
static_assert(mround(11).up(5) == 15, "");

static_assert(mround(9).down(5) == 5, "");
static_assert(mround(10).down(5) == 10, "");
static_assert(mround(11).down(5) == 10, "");




On Friday, December 9, 2016 at 11:30:40 AM UTC-5, Walt Karas wrote:

Dawid Pilarski

unread,
Dec 12, 2016, 3:51:08 AM12/12/16
to std-pr...@isocpp.org
How about interface like this:

Integral round_up_to_multiple(Value i, Factor factor);

With both Value and Factor having explicit constructors.
which should be called like this:
round_up_to_multiple(Value(x), Factor(y));

In fact there is no need here to introduce mround type. But I do not say it's bad here, although Value and Factor is IMO more expressive.
It's still just an idea.

On the other hand, It would be strange also to introduce double interface.
First for / and * operators with just mround type and add and subtract with functions. It's inconsistent IMO, however I myself gave this suggestion. I think the API should be considered harder.

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Walt Karas

unread,
Dec 12, 2016, 10:23:09 AM12/12/16
to ISO C++ Standard - Future Proposals
I don't see how that would work.  Are Value and Factor concepts rather than templates in your suggestion?  I only have a very rough idea of what's in the Concepts TS.

Dawid Pilarski

unread,
Dec 13, 2016, 3:58:18 AM12/13/16
to std-pr...@isocpp.org
It can be simple type like:

//using new class template deduction
template <typename T>
class Value{
public:
    explicit Value(T value);
    operator T (); //or simple T value() method
private
T storedValue;
};


same would be for Factor type.
Purpuse of this class would be only for syntax sugaring function call, so

round_up_multiple(2,3) // error, because constructor is explicit
round_up_multiple(Value(2), Factor(3)); // fine

also declaration of such a function will have to be templated, so

template <typename T>
T round_up_multiple(Value<T>, Factor<T>);

To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.

To post to this group, send email to std-pr...@isocpp.org.

Walt Karas

unread,
Dec 13, 2016, 10:44:27 AM12/13/16
to ISO C++ Standard - Future Proposals
Can you give a compiling example?

It seems to me that my helper function template in indispensible:

template <typename T>
struct X
{ explicit X(T t); };


template <typename T>
void foo(X<T> x);


void bar() { foo(X(5)); } // causes an error


template <typename T>
X
<T> x(T t) { return(X<T>(5)); }


void bar2() { foo(x(5)); }

Link to Compiler Explorer:

https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(j:1,options:(colouriseAsm:'0',compileOnChange:'0'),source:'template+%3Ctypename+T%3E%0Astruct+X%0A%7B+explicit+X(T+t)%3B+%7D%3B%0A%0Atemplate+%3Ctypename+T%3E%0Avoid+foo(X%3CT%3E+x)%3B%0A%0Avoid+bar()+%7B+foo(X(5))%3B+%7D%0A%0Atemplate+%3Ctypename+T%3E%0AX%3CT%3E+x(T+t)+%7B+return(X%3CT%3E(5))%3B+%7D%0A%0Avoid+bar2()+%7B+foo(x(5))%3B+%7D'),l:'5',n:'1',o:'C%2B%2B+source+%231',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:g62,filters:(b:'0',commentOnly:'0',directives:'0',intel:'0'),options:'--std%3Dc%2B%2B14+-c'),l:'5',n:'0',o:'%231+with+x86-64+gcc+6.2',t:'0')),k:50,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4
Reply all
Reply to author
Forward
0 new messages