我想实现一个shift-left函数,它会在溢出时触发失败。
这是我的代码:
uint32_t safe_shl(uint32_t x, uint8_t y) {
uint32_t z = x << y;
assert((z >> y) == x);
return z;
}
请假设assert
函数在我的系统中注册了错误。
我想确保我的方法是防弹的(即,在每次错误输入时都会失败,并且仅在错误输入时失败)。
我还想问你是否知道一种更有效的方法来实现它(假设它确实是防弹的)。
非常感谢!!!
答案 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)
为了编写安全功能,您必须首先确定哪些不安全。如果你不这样做,那么任务就是废话。你提到的那种“溢出”实际上是明确定义的。但存在以下危险行为案例:
为避免这种情况,您需要确保:
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_t
和uint32_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