【问题标题】:How to properly check for overflow when down casting to uint32_t向下转换为 uint32_t 时如何正确检查溢出
【发布时间】:2019-05-15 07:32:33
【问题描述】:

当我们投射到uint32_t 时,我们如何正确地检查和处理溢出,例如:

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

如果我尝试上述方法,我会收到预期的转换错误:

错误:从“long int”转换为“uint32_t {aka unsigned int}”可能会改变其值 [-Werror=conversion]

正如here 所见,我需要将valINT_MAXINT_MIN 进行比较,但为了让我理解和了解这一点,我希望简要说明什么是检查溢出的最佳方法在上述情况下。

【问题讨论】:

  • bool overflow = val &gt; std::numeric_limits&lt;uint32_t&gt;::max() || val &lt; std::numeric_limits&lt;uint32_t&gt;::min();
  • @George 非常感谢您对您的评论进行非常简短的解释(可能以答案的形式) - 特别是如果您能解释这是如何以及如果这是对 unsigned int 的强制转换这一事实使任何区别。
  • @skratchi.at 请在我的问题中更新

标签: c++ c++14 overflow static-cast


【解决方案1】:

如果使用, 你可以使用中的boost::numeric_cast

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

链接到reference page


如果没有,您将不得不自己实施检查:

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)
}

如果需要更普遍的话,这个想法当然可以变成(ala.boost::numeric_cast)。

【讨论】:

  • 抱歉,我应该明确表示我没有使用 boost。您有非提升建议吗?
  • @Karim 没关系,那么这个答案可能会服务于未来的用户(有提升)。
  • @Karim 我已经更新了一个没有提升的解决方案,这有意义吗?
【解决方案2】:

在 C++20 中,您可以为此使用 std::in_range

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
}

您也可以使用intcmp自己进行比较

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);
}

【讨论】:

    【解决方案3】:

    检查溢出确实是通过比较源值与目标类型的最小/最大值来完成的 (std::uint32_t)。但还有更多——你打算如何处理源值在目标值域之外的情况?在您的情况下,否定的val 已经提出了这个问题,并且这不会涉及溢出情况,因为分配给无符号整数类型的负值是明确定义的(尽管结果值可能不是您预期的值)。

    这是一个针对long intstd::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);
    

    这是安全的,不应产生编译器警告 (-Wconversion),因为强制转换是显式的。问题是生成的0 值代表两种状态——当源值val 为零时转换失败和成功转换。为了缓解这种情况,C++17 为您提供了例如std::optional。在此之前,您需要对 val == 0 案例进行一些额外的调度。

    【讨论】:

    • 建议的“simpel”解决方案并不理想 - 因为负值被截断为零。在某些需要的情况下,这可能很好,但在大多数其他情况下,它会默默地隐藏一个错误——而且是一个非常难以追踪的错误。在不确定上下文的情况下,最好抛出或中止 IMO 程序。
    • @darune 我同意,但是由于 OP 没有描述应该如何处理这种情况,我没有解决这个问题。正如我在回答中所建议的那样,std::optional 也可能是一个解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    相关资源
    最近更新 更多