Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Clamping 16-Bit int's on a microcontroller

48 views
Skip to first unread message

Frederick Virchanza Gotham

unread,
Aug 19, 2022, 6:25:13 AM8/19/22
to
I'm programming a microcontroller in C++ (the Arduino Due sam3x8e).

I'm playing around with 16-Bit unsigned integers. Sometimes the additions and multiplications overflow, and sometimes subtraction underflows.

The best way to handle this for my particular project is:
(1) If addition or multiplication overflows, yield UINT_MAX
(2) If subtraction underflows, yield 0

This morning I wrote a class which is intended to be used as follows:

int main(void)
{
uint16_t i = 30000u;

Clamp(i) *= 3u;

cout << i << endl;
}

So the above program prints "65535".

Here's the header file I wrote this morning, please tell me how you'd improve upon it:

#ifndef HEADER_INCLUSION_GUARD_OVERFLOW_HPP
#define HEADER_INCLUSION_GUARD_OVERFLOW_HPP

#include <limits> // numeric_limits<T>::is_signed

template<typename T>
class ClampClass final {
public:

typedef T type;

static_assert( std::numeric_limits<type>::is_integer && (false == std::numeric_limits<type>::is_signed),
"The ClampClass template class can only be used"
" with an unsigned integer type" );

ClampClass( void ) = delete;
ClampClass(ClampClass const & ) = delete;
// ClampClass(ClampClass &&) = delete; -- Not deleted because defined at the bottom

ClampClass &operator=(ClampClass const & ) = delete;
ClampClass &operator=(ClampClass &&) = delete;

ClampClass *operator&(void) = delete;
ClampClass const *operator&(void) const = delete;

ClampClass *operator->(void) = delete;
ClampClass const *operator->(void) const = delete;

private:

type &obj;

public:

ClampClass(T &arg) : obj(arg) {}

static void Add_Or_Max(type &a, type const b)
{
if ( a > (std::numeric_limits<T>::max() - b) )
{
a = std::numeric_limits<T>::max();
}
else
{
a += b;
}
}

static void Subtract_Or_Zero(type &a, type const b)
{
if ( b > a ) a = 0;
else a -= b;
}

static void Multiply_Or_Max(type &a, type const b)
{
if ( !a || !b ) // Check if either of them is zero
{
a = 0;
return;
}

type const result = a * b;

if ( a == static_cast<type>(result/b) ) // division by zero is prevented
{
a = result;
}
else
{
a = std::numeric_limits<type>::max();
}
}

void operator+=(type const rhs) { Add_Or_Max(obj,rhs); }
void operator-=(type const rhs) { Subtract_Or_Zero(obj,rhs); }
void operator*=(type const rhs) { Multiply_Or_Max(obj,rhs); }

void operator++(void) { Add_Or_Max(obj,1); }
void operator--(void) { Subtract_Or_Zero(obj,1); }

private:

ClampClass(ClampClass &&arg) : obj(arg.obj)
{
// This R-value-ref constructor is here
// so that the 'Clamp' function
// can return a temporary
}

template<typename U>
friend ClampClass<U> Clamp(U&);
};

template<typename T>
ClampClass<T> Clamp(T &arg)
{
return ClampClass<T>(arg);
}

#endif

David Brown

unread,
Aug 19, 2022, 8:39:00 AM8/19/22
to
On 19/08/2022 12:25, Frederick Virchanza Gotham wrote:
> I'm programming a microcontroller in C++ (the Arduino Due sam3x8e).
>
> I'm playing around with 16-Bit unsigned integers. Sometimes the additions and multiplications overflow, and sometimes subtraction underflows.
>
> The best way to handle this for my particular project is:
> (1) If addition or multiplication overflows, yield UINT_MAX
> (2) If subtraction underflows, yield 0
>
> This morning I wrote a class which is intended to be used as follows:
>
> int main(void)
> {
> uint16_t i = 30000u;
>
> Clamp(i) *= 3u;
>
> cout << i << endl;
> }
>
> So the above program prints "65535".
>
> Here's the header file I wrote this morning, please tell me how you'd improve upon it:

That class and its usage does not feel right to me - you are making a
class that is a wrapper around a reference to an integer object, and
then performing arithmetic on the reference wrapper. I'd feel happier
with an interface like :

int main(void)
{
saturating<uint16_t> i = 30000;
i *= 3;
cout << i << endl;
}

That will also work far better when you have more complex expressions.

Also, your multiplication function is going to be painfully inefficient.
Avoid division whenever possible. It's better to use larger sized
multiplication and check for overflow, or __builtin_mul_overflow_p and
related functions if your compiler supports them. A division-based
solution might be okay as a fallback if nothing better is available, but
don't make it your first choice.
0 new messages