1

How can we properly check and handle for an overflow when we cast to uint32_t, for example:

long int val = <some value>
uint32_t new_val = static_cast<uint32_t>(val);

If I try the above, I get an expected conversion error:

error: conversion to ‘uint32_t {aka unsigned int}’ from ‘long int’ may alter its value [-Werror=conversion]

As seen here, I need to compare val against INT_MAX and INT_MIN but to allow me to understand and learn about this I would appreciate a brief explanation of what is the best way to check for overflow in a case like the above.

phuclv
  • 37,963
  • 15
  • 156
  • 475
  • 2
    `bool overflow = val > std::numeric_limits::max() || val < std::numeric_limits::min();` – George May 15 '19 at 07:35
  • @George A very brief explanation of your comment (perhaps in the form of an answer) would be greatly appreciated - especially if you could explain how and if the fact this is a cast to an unsigned int makes any difference. –  May 15 '19 at 07:41
  • @skratchi.at please update in my question –  May 15 '19 at 07:45

3 Answers3

1

If using , you can use the boost::numeric_cast found in .

uint32_t new_val = boost::numeric_cast<uint32_t>(val)

Link to the reference page.


Without , you would have to implement the checking yourself:

uint32_t new_val{static_cast<uint32_t>(val)};//<-- optimistic conversion, check for overflow next

const bool overflow = val > std::numeric_limits<uint32_t>::max() || val < std::numeric_limits<uint32_t>::min();

if (overflow) {
  //handle overflow, this could involve calling std::terminate, throwing an exception, or a truncation of the value (to zero)
}

This idea, could of course be made into a (ala. boost::numeric_cast) if needed more generally.

darune
  • 10,480
  • 2
  • 24
  • 62
  • Apologies, I should have made it clear that I am not using boost. Do you have a non-boost suggestion? –  May 15 '19 at 07:54
  • 1
    @Karim that is ok, then this answer may serve for future users (that have boost). – darune May 15 '19 at 08:00
  • @Karim I have updated with a solution without boost, does it make sense ? – darune May 15 '19 at 08:39
0

In C++20 you can use std::in_range for this

long int val = <some value>;
if (std::in_range<uint32_t>(val))
{
    uint32_t new_val = static_cast<uint32_t>(val);
}
else
{
    // out of range, do something else
}

You can also use intcmp to do comparisons yourself

if (std::cmp_greater(val, std::numeric_limits<std::uint32_t>::max()))
{
    std::cout << "Overflow\n";
}
else if (std::cmp_less(val, std::numeric_limits<std::uint32_t>::min()))
{
    std::cout << "Underflow\n";
}
else
{
    uint32_t new_val = static_cast<uint32_t>(val);
}
phuclv
  • 37,963
  • 15
  • 156
  • 475
-1

Checking for an overflow is indeed done by comparing the source value against the min/max values of the target type (std::uint32_t). But there is more to it - how do you intend to handle the situation in which the source value is outside of the domain of the target value? In your case, a negative val already raises this question, and this doesn't touch the overflow scenario, as negative values assigned to unsigned integral types is well defined (though the resulting value might not be the one you expected).

Here is a simple solution specific to long int to std::uint32_t:

#include <limits>

std::uint32_t new_val = val < 0 || val > std::numeric_limits<std::uint32_t>::max() ?
    0 : static_cast<std::uint32_t>(val);

This is safe and should not yield compiler warnings (-Wconversion), as the cast is explicit. The issue is that a resulting 0 value represents two states - a failed conversion as well as a successful conversion when the source value val is zero. To mitigate this, C++17 gives you e.g. std::optional. Prior to that, you need some additional dispatching on the val == 0 case.

lubgr
  • 37,368
  • 3
  • 66
  • 117
  • The proposed 'simpel' solution is not ideal - since negative values gets truncated to zero. That may be fine for some situations when it is desired, but it in most other cases it will silently hide an error - and one that can be extremely difficult track down. Without being sure of the context it would be better to throw or abort the program IMO. – darune May 15 '19 at 08:13
  • @darune I agree, but as the OP didn't describe how this scenario should be handled, I left this issue unsolved. As suggested in my answer, `std::optional` could be a solution, too. – lubgr May 15 '19 at 09:13