下一个最大整数,其中一些中间位与模式匹配?

时间:2017-09-05 23:14:38

标签: c++ bit-manipulation

我的意见是:

  • 宽度为mask的位掩码n和某些偏移k> = 0
  • 位模式pattern,其中一些(但不一定是全部)位掩码具有1的位置为1。
  • 一个整数val

我想找到下一个最大的整数result,以便:

  • result > val
  • result & mask == pattern

例如,假设mask = 0xFF00pattern = 0x0100。然后我们期望得到以下结果:

NextLargest(mask, pattern, 0x00000) => 0x00100
NextLargest(mask, pattern, 0x000FF) => 0x00100
NextLargest(mask, pattern, 0x010FE) => 0x001FF
NextLargest(mask, pattern, 0x010FF) => 0x10100

另一个例子 - 说mask = 0xFpattern = 0xF。然后我们期待:

NextLargest(mask, pattern, 0x20) => 0x2F.

我尝试过类似"删除mask关心的位,增加它,或者返回pattern并返回"但我一直在打边缘案件。这个问题类似于找到某个整数的下一个最大倍数的概括。

到目前为止我的尝试(runnable link: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;
}

4 个答案:

答案 0 :(得分:3)

我没有对此进行测试,但以下算法应该有效(伪代码):

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
POSIX提供了

flsffs,但其他系统可能不会这样做。如果需要,有关于如何实现这些的答案。

答案 1 :(得分:1)

想想value分为3。 掩码上方,掩码中和掩码下方的位。 H(value)M(value)L(value)

我们知道M(result)==pattern。 我们有三位候选人。

C1是H(value)+pattern+0

C2是H(value)+pattern+L(value)+1

C3为H(value)+pattern+X

X==(mask<<1)&~mask。这是mask以上的最低位。

如果pattern>M(value)我们可以使用C1。 减少高位将得到一个数字<value,设置任何低位都会增加数字。

如果pattern==M(value),我们可以尝试实际上value+1的C2。 如果向模式位添加一个溢出,则会失败。

这意味着所有低位都被设置,下一个要添加的最低位是掩码上方的第一位。

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 << "Success\n";
    return 0;
}

答案 2 :(得分:0)

是计算 LARGEST SMALLEST 下一个值的问题?最大的值似乎很奇怪。如果要求是计算最小值,我认为这段代码应该有效:(在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个部分:高部分,屏蔽部分,下部分。被遮罩的部分可以直接从maskpattern推导出来,并且由--- - name: sorting json hosts: localhost tasks: - name: uri: url: http://jsonplaceholder.typicode.com/users method: GET return_content: yes register: result ignore_errors: yes - debug: msg="{{result.content}}" { "address": { "city": "Gwenborough", "geo": { "lat": "-37.3159", "lng": "81.1496" }, "street": "Kulas Light", "suite": "Apt. 556", "zipcode": "92998-3874" }, "company": { "bs": "harness real-time e-markets", "catchPhrase": "Multi-layered client-server neural-net", "name": "Romaguera-Crona" }, "email": "Sincere@april.biz", "id": 1, "name": "Leanne Graham", "phone": "1-770-736-8031 x56442", "username": "Bret", "website": "hildegard.org" }, 确定,它不能是其他值。

在计算屏蔽位后,如果值增加,只需屏蔽下部的所有位。否则,将高位增加1(并且还屏蔽所有低位)。

上面的代码没有处理格式错误的输入,它会触发断言,但检查不会用尽。

答案 3 :(得分:0)

这个有点令人困惑的问题有两个功能 第一个函数提供满足结果要求的最大下一个整数。 第二个给出了下一个SMALLEST值。

1:获取满足result & mask == patternresult > 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 == patternresult > 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&bit; d位上传送溢出位。 以下是该函数的一些示例,如mask = 0x0ff00pattern = 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

经过长时间的编辑和重写,我仍然无法为这个问题提供足够好的答案。它的例子有很奇怪的结果。如果有人发现这些功能或部分功能有用,我仍然会留在这里。另外,我实际上没有测试过计算机上的功能。