下一个最大的整数,有一些中间位与模式匹配?
Next largest integer with some middle bits matching a pattern?
我的输入是:
- 宽度
n
的位掩码mask
和一些偏移k
>=0 - 位模式在某些(但不一定是全部)位掩码具有 1 的位置
pattern
1。 - 整数
val
我想找到下一个最大的整数result
这样:
result > val
result & mask == pattern
例如,假设mask = 0xFF00
和pattern = 0x0100
。然后我们期待以下结果:
NextLargest(mask, pattern, 0x00000) => 0x00100
NextLargest(mask, pattern, 0x000FF) => 0x00100
NextLargest(mask, pattern, 0x010FE) => 0x001FF
NextLargest(mask, pattern, 0x010FF) => 0x10100
另一个例子 - 说mask = 0xF
和pattern = 0xF
。然后我们期望:
NextLargest(mask, pattern, 0x20) => 0x2F.
我尝试过诸如"去除mask
关心的部分,增加它,或者回到pattern
并返回"之类的方法,但我不断遇到边缘情况。这个问题有点像找到某个整数的下一个最大倍数的泛化。
这是我到目前为止的尝试(可运行的链接:https://ideone.com/AhXG5M):
#include <iostream>
using namespace std;
using uint32 = unsigned long;
uint32 NextLargest(int width, int offset, uint32 mask, uint32 pattern, uint32 val) {
unsigned long long ret = (val + 1) & ~mask;
if ((ret & ((1 << (offset + 1)) - 1)) == 0) {
// "carry" across the mask
ret += 1 << (offset + width);
}
return ret | pattern;
}
int main() {
// your code goes here
int width = 12;
int offset = 4;
uint32 significant_bits = (1 << (width + 1) - 1) << offset;
uint32 wanted_bits = 0xFFF << offset;
cout << hex;
// want 0xFFF1 -- correct
cout << NextLargest(width, offset, significant_bits, wanted_bits, 0) << endl;
// want 0xFFF2 -- correct
cout << NextLargest(width, offset, significant_bits, wanted_bits, 1) << endl;
// want 0x1FFFF0 -- incorrect, get 0xFFF0
cout << NextLargest(width, offset, significant_bits, wanted_bits, 0xF) << endl;
return 0;
}
我没有测试这个,但以下算法应该可以工作(伪代码):
let mask, pattern, and val be inputs
let fls be function that finds last bit set in word
let ffs be function that finds first bit set in a word
let applied be (val & ~mask) | pattern
if applied is greater than val then
return applied
let low_order_mask be (1 << ffs(mask)) - 1
if applied == val then
let flipped_low be (~value & low_order_mask)
if not flipped_low then
return applied + 1 // no need to carry
// need to carry
let set_low_zero be applied & ~low_order_mask
let carry be 1 << (fls(mask) + 1)
return set_low_zero + carry
fls
和ffs
由 POSIX 提供,但其他系统可能不会这样做。如果需要,SO 上有关于如何实现这些的答案。
想想value
分成3。 掩码上方、掩码中和掩码下方的位。H(value)
、M(value)
、L(value)
。
我们知道M(result)==pattern
. 我们有三名候选人。
C1 是H(value)+pattern+0
。
C2H(value)+pattern+L(value)+1
C3 是H(value)+pattern+X
X==(mask<<1)&~mask
.这是高于mask
的最低位。
如果pattern>M(value)
我们可以使用 C1。 减少高位将得到一个数字<value
,设置任何低位将增加数字。
如果pattern==M(value)
那么我们可以尝试 C2 这实际上是value+1
. 如果添加一个溢出到模式位,则会失败。
这意味着所有低位都已设置,要添加的下一个最低位置是掩码上方的第一个位。
unsigned next_masked(unsigned mask,unsigned pattern,unsigned value){
unsigned reduced_pattern=(mask&pattern);//May not be required...
unsigned over_add=(mask<<1)&~mask;
unsigned upper_mask=~(over_add-1);
unsigned cand=(value&upper_mask)|reduced_pattern;
if(cand>value){
return cand;
}
if((value&mask)==reduced_pattern){
unsigned scand=value+1;
if((scand&mask)==reduced_pattern){
return scand;
}
}
return cand + over_add;
}
这里再次进行一些单元测试:
#include <iostream>
unsigned next_masked(unsigned mask,unsigned pattern,unsigned value){
unsigned reduced_pattern=(mask&pattern);//May not be required...
unsigned over_add=(mask<<1)&~mask;
unsigned upper_mask=~(over_add-1);
unsigned cand=(value&upper_mask)|reduced_pattern;
if(cand>value){
return cand;
}
if((value&mask)==reduced_pattern){
unsigned scand=value+1;
if((scand&mask)==reduced_pattern){
return scand;
}
}
return cand + over_add;
}
bool invariant_next_masked(unsigned mask,unsigned pattern,unsigned value,unsigned result){
if((result&mask)!=(pattern&mask)){
return false;
}
if(result<=value){
return false;
}
for(unsigned test=result-1;test>value;--test){
if((test&mask)==(pattern&mask)){
return false;
}
}
return true;
}
int check_next_masked(unsigned mask,unsigned pattern,unsigned value,unsigned expect){
unsigned result=next_masked(mask,pattern,value);
if(result!=expect){
std::cout << std::hex << mask << ' ' << std::hex << pattern << ' ' << std::hex <<value << "==" << std::hex <<result << "!=" << std::hex <<expect <<'n';
return 1;
}
if(!invariant_next_masked(mask,pattern,value,result)){
return 1;
}
return 0;
}
int main() {
int errors=0;
errors+=check_next_masked(0xFF00,0x0100,0x0000,0x00100);
errors+=check_next_masked(0xFF00,0x0100,0x00FF,0x00100);
errors+=check_next_masked(0xFF00,0x0100,0x10FE,0x10100);
errors+=check_next_masked(0xFF00,0x0100,0x1067,0x10100);
errors+=check_next_masked(0xFF00,0x0100,0x10123,0x10124);
errors+=check_next_masked(0xFF00,0x0100,0x110FF,0x20100);
errors+=check_next_masked(0xFF00,0x0100,0x102FF,0x20100);
errors+=check_next_masked(0xFF00,0x0100,0x101FF,0x20100);
errors+=check_next_masked(0x000F,0x0007,0x10123,0x10127);
errors+=check_next_masked(0x000F,0x0007,0x10128,0x10137);
errors+=check_next_masked(0x0FF0,0x0230,0x10128,0x10230);
errors+=check_next_masked(0x0FFF0,0x01230,0x01231,0x01232);
errors+=check_next_masked(0x0FFF0,0x01230,0x41237,0x41238);
errors+=check_next_masked(0x0FFF0,0x01230,0x4123F,0x51230);
if(errors>0){
std::cout << "Errors "<< errors << 'n';
return 1;
}
std::cout << "Successn";
return 0;
}
问题是计算最大或最小的下一个值吗? 最大值似乎很奇怪。 如果要求是计算最小值,我认为这段代码应该可以工作: (在 GCC 7.1 上测试,假设目标是 64 位,sizeof(void *) == sizeof(size_t) == sizeof(uint64_t))
size_t next_smallest_value (size_t mask, size_t pattern, size_t x) {
assert(pattern & mask == pattern);
// only change bits within mask range to meet the requirement
auto y = x & ~mask | pattern;
if (y > x) {
// if the operation increased the value
// mask off all the lower bits
auto lsb_mask = __builtin_ctzll(mask);
return y & ~ones(lsb_mask);
} else {
// otherwise, the operation decreased or didn't change the value
// need to increase the fraction higher than the mask
auto msb_mask = 63 - __builtin_clzll(mask);
// higher part cannot be empty if the masked part decrease
assert(msb_mask < 63);
auto higher = ((y >> msb_mask) + 1) << msb_mask;
// also higher part cannot overflow
assert(higher != 0);
return y & mask | higher;
}
}
这个想法很简单:将位分成3个部分:上部,屏蔽部分,下部。 屏蔽部分可以直接导出并由mask
和pattern
确定,它不能是其他值。
计算屏蔽位后,如果值增加,只需屏蔽下部的所有位。 否则,将较高部分增加 1(并屏蔽所有较低位)。
上面的代码不处理格式错误的输入,它会触发断言,但检查并没有用尽。
这是这个有点令人困惑的问题的两个函数 第一个函数给出满足结果要求的最大下一个整数。 第二个给出最小的下一个值。
1:得到满足result & mask == pattern
的最大整数和result > val
:
unsigned NextLargest (unsigned mask, unsigned pattern, unsigned val) {
// zero "mask" bits and set "pattern" bits in largest (unsigned) int
unsigned const x = ~mask | pattern;
// if result is not greater than val, we can't satisfy requirements
if (x <= val) {
... report error, return error code or throw something
}
return x;
}
显然,这只返回满足要求的最高(无符号)整数值result & mask == pattern
和result > val
.if 子句检查结果是否不会大于val
,并且函数将失败。
2:获取满足要求的val
后最小的下一个值:
unsigned NextSmallest (unsigned mask, unsigned pattern, unsigned val) {
unsigned const x = (val + mask + 1) & ~mask | pattern;
if (x <= val) {
... increment wrapped, can't give greater value
}
return x;
}
编辑:将(val|mask)
更改为val+mask
,因为结果必须仍大于val
。
此函数计算val + 1
,并将溢出位携带超过mask
位。 以下是该函数的几个示例,如果mask = 0x0ff00
和pattern = 0x00500
:
val +mask +1 &~mask |pattern == result
0x00000 0x0ff00 0x0ff01 0x00001 0x00501
0x00001 0x0ff01 0x0ff02 0x00002 0x00502
0x000fe 0x0fffe 0x0ffff 0x000ff 0x005ff
0x000ff 0x0ffff 0x10000 0x10000 0x10500
0x00100 0x10000 0x10001 0x10001 0x10501
0x0f000 0x1ef00 0x1ef01 0x10001 0x10501
0x0ff00 0x1fe00 0x1fe01 0x10001 0x10501
0x0ffff 0x1feff 0x1ff00 0x10000 0x10500
0x10000 0x1ff00 0x1ff01 0x10001 0x10501
0x10001 0x1ff01 0x1ff02 0x10002 0x10502
0x100ff 0x1ffff 0x20000 0x20000 0x20500
经过长时间的编辑和重写,我仍然无法为这个问题给出足够好的答案。它的例子有奇怪的结果。如果有人发现这些功能或其部分有用,我仍然将其留在这里。我也没有实际测试计算机上的功能。
- 所有可能的链接生成器与64位密钥
- OpenCV矩阵奇怪的加法,乘法与浮点数和8位值
- 24 位地址和 24 位算术与 24 位地址与 16 位地址算术之间的区别?
- 完美的广场与位明智和
- 位移位与前导 1.
- 与位字段的并集为位字段成员提供了意外值
- 是否与赢得64位有关?错误:STATUS_ACCESS_VIOLATION
- 如何将32位字符与内联assembelyc++中的32位字符进行比较
- 与 32 位共享对象的链接问题
- 在早期的 C 和 C++ 编译器中,手动位移位与乘法和除法的相关性如何?
- EDSDK 3.6 macOS - 32 位与 64 位和链接错误
- 将 64 位整数中的每个其他位与 32 位整数进行比较
- 下一个最大的整数,有一些中间位与模式匹配?
- C++32位与64位浮动限制
- 32 位与 64 位:巨大的运行时差异
- std::cout 可以设置坏位或故障位与操作员<<吗?如果是,什么时候?
- c++中的位与
- 如何对两个大整数进行位与运算
- g++32位与64位汇编中的动态与静态链接差异
- C++:32位与64位流操作