实施安全左移

时间:2017-12-22 11:23:32

标签: c++ c bit-shift integer-overflow

我想实现一个shift-left函数,它会在溢出时触发失败。

这是我的代码:

uint32_t safe_shl(uint32_t x, uint8_t y) {
    uint32_t z = x << y;
    assert((z >> y) == x);
    return z;
}

请假设assert函数在我的系统中注册了错误。

我想确保我的方法是防弹的(即,在每次错误输入时都会失败,并且仅在错误输入时失败)。

我还想问你是否知道一种更有效的方法来实现它(假设它确实是防弹的)。

非常感谢!!!

5 个答案:

答案 0 :(得分:4)

如果x << y未定义,则所有投注均已关闭 唯一安全的方法是在尝试之前检查它是否有效。

uint32_t safe_shl(uint32_t x, uint8_t y) {
    assert (y < 32);
    if (y < 32)
    {
        uint32_t z = x << y;
        assert((z >> y) == x);
        return z;
    }
    return 0;
}

请注意,您需要条件 - 无条件转换让编译器认为y < 32为真。

答案 1 :(得分:1)

您是否要求断言班次是否会导致进位?

在这种情况下,如果不诉诸内在函数或汇编程序,它在c ++中有点讨厌。

#include <cassert>
#include <cstdint>
#include <limits>

bool shl_would_carry(uint32_t x, uint8_t y)
{
    constexpr auto nof_bits = std::numeric_limits<decltype(x)>::digits;
    if (y >= nof_bits)
    {
        if (x != 0) return true;
    }
    else
    {
        auto limit = decltype(x)(1) << (nof_bits - y);
        if (x >= limit) return true;
    }
    return false;
}

uint32_t safe_shl(uint32_t x, uint8_t y) 
{
    assert(!shl_would_carry(x, y));
    return x << y;
}

我认为这是对的。

这可能会更好:

std::tuple<uint32_t, uint32_t> shl(uint32_t x, uint8_t y)
{
    uint32_t overflow, result;
    constexpr auto nof_bits = std::numeric_limits<decltype(x)>::digits;
    overflow = x >> (nof_bits - y); 
    result = x << y;
    return std::make_tuple(overflow, result);
}

uint32_t safe_shl(uint32_t x, uint8_t y) 
{
    auto t = shl(x, y);
    assert(!std::get<0>(t));
    return std::get<1>(t);
}

答案 2 :(得分:1)

在C中,x << y如果为uint32_t提供了y < 32 z。从6.57位移位运算符中的C11的n1570草案:

  

如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。

结果然后需要:x×2 y 减少模数 比结果类型

中可表示的最大值多一个

请在您提议的代码中调用该值z >> y。与使用无符号类型一样,z >> y的值必须是 z / 2 y 的组成部分。

这意味着提供了y&lt; 32 如果有溢出,uint32_t safe_shl(uint32_t x, uint8_t y) { assert(y<32); uint32_t z = x << y; assert((z >> y) == x); return z; } 的值将严格小于x,因为模数,如果没有溢出,则得到x

6.5.7按位移位运算符的完全引用:

  

...
4 E1的结果&lt;&lt; E2是E1左移E2位位置;腾出的位充满了   零。如果E1具有无符号类型,则结果的值为E1×2E2,模数减少   比结果类型中可表示的最大值多一个。如果E1有签名   类型和非负值,E1×2E2在结果类型中可表示,那就是   结果价值;否则,行为未定义。

     

5 E1&gt;的结果&gt; E2是E1右移E2位位置。如果E1具有无符号类型   或者如果E1具有带符号类型和非负值,则结果的值为积分   E1 / 2E2商的一部分。如果E1有签名类型和负值,则   结果值是实现定义的。

在C ++中,从5.8转换运算符[expr.shift]中的C ++ 14的n4296草案完全相同:

  

...如果右操作数,则行为未定义   是负的,或大于或等于提升的左操作数的位数。

     

2 E1的值<&lt; E2是E1左移E2位位置;空位是零填充的。如果E1有无符号   类型,结果的值是E1×2E2,减去模数比可表示的最大值多一个   在结果类型中。否则,如果E1具有带符号类型和非负值,则E1×2E2可表示   在结果类型的相应无符号类型中,那么转换为结果类型的值是   结果价值;否则,行为未定义。

     

3 E1的值&gt;&gt; E2是E1右移E2位位置。如果E1具有无符号类型或E1具有签名   类型和非负值,结果的值是E1 / 2E2的商的整数部分。如果E1   具有签名类型和负值,结果值是实现定义的。

所以在两种语言中,假设断言函数在[你的]系统中注册了一个错误,代码应为:

history.push

答案 3 :(得分:1)

为了编写安全功能,您必须首先确定哪些不安全。如果你不这样做,那么任务就是废话。你提到的那种“溢出”实际上是明确定义的。但存在以下危险行为案例:

  • 左移大于变量的大小,包括将数据移位到有符号变量的符号位。 (未定义的行为)
  • 右边的操作符是负数。 (未定义的行为)
  • 右移负数。 (Impl.-defined behavior)
  • 左操作数的隐式整数提升,导致它以静默方式更改签名,从而调用上述错误之一。

为避免这种情况,您需要确保:

  1. 左操作数必须是无符号的。
  2. 右操作数必须有效且在左操作数类型的范围内。
  3. 左操作数不能是小整数类型。
  4. 1)和3)通过使用uint32_t来解决。没有uint32_t小于int的系统。

    2)通过使用无符号类型并检查它是否太大来解决。

    此外,您似乎要求不允许超出左操作数的范围。这很奇怪,但是好吧,让我们实现它。可以通过检查MSB位位置加上移位数是否大于31来完成。

    uint8_t msb_pos32 (uint32_t data)
    {
      uint8_t result = 0;
      while(data>>=1 > 0)
      {
        result++;
      }
      return result;
    }
    
    uint32_t safe_LSL32 (uint32_t x, uint8_t y) 
    {
      if(y > 31 || y+msb_pos32(x) > 31)
      {
        __asm HCF;               // error handling here
      }
      return x << y;
    }
    

    请注意,此代码可以进一步优化。

答案 4 :(得分:1)

步骤1.如果int/unsigned和任何班次金额,结果在概念上仍为0并且不是问题。

步骤2.不要尝试过度轮班。

  

如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义。 C11§6.5.73

步骤3.确保移位时无符号数学运算。

如果uintN_t x宽于x << y,那么int将使用N==32数学完成。这种情况很少见1u*x。有符号数学溢出是可能的,并导致UB。通过(0u+x)unsigned,代码可以确保班次使用更宽的uintN_tuint32_t safe_shl(uint32_t x, uint8_t y) { if (x == 0) { return 0; } assert(y < 32); uint32_t z = (1u*x) << y; assert((z >> y) == x); return z; } 数学。好的编译器仍然可以制作最佳代码。

步骤4.检测是否发生了减少。

  

如果E1具有无符号类型,则结果的值为E1×2E2,模数减少   比结果类型§6.5.74

中可表示的最大值多一个
Sub automaticformfilling_ASDA()

    Dim ie As Object

    Set ie = CreateObject("internetexplorer.application")

    'to make sure that the website is properly loaded

    With ie
        .Visible = True
        .navigate "https://www.argos-pet-insurance.com/quoteAndBuy.do?e=e1s1&curPage=captureDetails&rakT=1510852044896.1391473424.994101.1731.881880349.846|tsid:9904"

        Do While .Busy
            DoEvents
        Loop

        Do While .readyState <> 4
            DoEvents
        Loop
    End With

    Set Title = ie.document.getElementById("yourDetailsPolicyHolderTitle")
    Title.selectedIndex = 4
End Sub