[英]Right shift with zeros at the beginning
我正在嘗試做一種左移,它會在開頭添加零而不是一。 例如,如果我左移0xff
,我會得到這個:
0xff << 3 = 11111000
但是,如果我右移它,我會得到這個:
0xff >> 3 = 11111111
我可以使用任何操作來獲得相當於左移的效果嗎? 即我想得到這個:
00011111
有什么建議嗎?
編輯
要回答評論,這是我正在使用的代碼:
int number = ~0;
number = number << 4;
std::cout << std::hex << number << std::endl;
number = ~0;
number = number >> 4;
std::cout << std::hex << number << std::endl;
輸出:
fffffff0
ffffffff
由於它似乎總體上應該可以工作,因此我對為什么此特定代碼不起作用很感興趣。 任何的想法?
這就是 C 和二進制算術的工作方式:
如果你左移0xff << 3
,你得到二進制: 00000000 11111111 << 3 = 00000111 11111000
如果你右移0xff >> 3
,你得到二進制: 00000000 11111111 >> 3 = 00000000 00011111
0xff
是一個(有符號)整數,其值為正值255
。 因為它是正數,所以在 C 和 C++ 中轉換它的結果是明確定義的行為。 它不會進行任何算術移位,也不會進行任何種類或定義不明確的行為。
#include <stdio.h>
int main()
{
printf("%.4X %d\n", 0xff << 3, 0xff << 3);
printf("%.4X %d\n", 0xff >> 3, 0xff >> 3);
}
輸出:
07F8 2040
001F 31
所以你在你的程序中做了一些奇怪的事情,因為它沒有按預期工作。 也許您正在使用 char 變量或 C++ 字符文字。
來源:ISO 9899:2011 6.5.7。
問題更新后編輯
int number = ~0;
假設為二進制補碼,則為您提供一個等效於 -1 的負數。
number = number << 4;
調用未定義的行為,因為您左移了一個負數。 程序正確地實現了未定義的行為,因為它要么做某事,要么什么都不做。 它可能會打印 fffffff0 或者它可能會打印一個粉紅色的大象,或者它可能會格式化硬盤驅動器。
number = number >> 4;
調用實現定義的行為。 在您的情況下,您的編譯器會保留符號位。 這被稱為算術移位,算術右移的工作方式是用移位前的任何位值填充 MSB。 因此,如果您有一個負數,您將體驗到該程序正在“移入”。
在 99% 的現實世界案例中,對有符號數使用按位運算符是沒有意義的。 因此,始終確保您使用的是無符號數,並且 C/C++ 中沒有任何危險的隱式轉換規則將它們轉換為有符號數(有關危險轉換的更多信息,請參閱“整數提升規則”和“通常的算術轉換” ”,關於 SO 的大量信息)。
編輯 2 ,來自 C99 標准的基本原理文檔 V5.10 的一些信息:
6.5.7 按位移位運算符
K&R 中對移位運算符的描述表明,按 long 計數移位應該強制左操作數在移位之前加寬為 long。 C89 委員會認可的一種更直觀的做法是,班次計數的類型與結果的類型無關。
C89 中的安靜變化
移位 long 計數不再強制移位的操作數為 long。 C89 委員會確認了 K&R 授予的實現自由,不要求簽名右移操作進行符號擴展,因為這樣的要求可能會減慢快速代碼的速度,並且符號擴展移位的用處很小。 (將負二進制補碼整數在算術上右移一位與除以 2 不同!)
如果您明確地將 0xff 移動到您預期的那樣
cout << (0xff >> 3) << endl; // 31
只有當0xff
的類型為有符號寬度 8 (流行平台上的char
和signed char
)時,才應該是可能的。
所以,一般情況下:
您需要使用無符號整數
(unsigned type)0xff
右移作為除以 2(如果我理解正確的話,四舍五入)。
因此,當您將 1 作為第一位時,您的值為負值,除法后再次為負值。
您所說的兩種右移稱為Logical Shift和Arithmetic Shift 。 C 和 C++ 對無符號整數使用邏輯移位,大多數編譯器將對有符號整數使用算術移位,但這不是由標准保證的,這意味着右移負有符號 int 的值是實現定義的。
由於您想要邏輯移位,因此您需要切換到使用無符號整數。 您可以通過將常量替換為0xffU
。
要解釋您的真實代碼,您只需要 Lundin 在評論中給出的 C 標准中引用的 C++ 版本:
int number = ~0;
number = number << 4;
未定義的行為。 [expr.shift] 說
E1 << E2 的值是 E1 左移的 E2 位位置; 空出的位用零填充。 如果 E1 具有無符號類型,則結果的值為 E1 × 2 E2 ,比結果類型中可表示的最大值減少模 1。 否則,如果 E1 具有有符號類型和非負值,並且 E1×2 E2在結果類型中可表示,則為結果值; 否則,行為未定義。
number = ~0;
number = number >> 4;
實現定義的結果,在這種情況下,您的實現給了您一個算術移位:
E1 >> E2 的值是 E1 右移的 E2 位位置。 如果 E1 具有無符號類型或如果 E1 具有有符號類型和非負值,則結果的值是 E1/2 E2的商的整數部分。 如果 E1 具有有符號類型和負值,則結果值是實現定義的
您應該使用無符號類型:
unsigned int number = -1;
number = number >> 4;
std::cout << std::hex << number << std::endl;
輸出:
0x0fffffff
在這里添加我的 5 美分價值......我面臨與 this.lau 完全相同的問題! 我對此做了一些敷衍的研究,這些是我的結果:
typedef unsigned int Uint;
#define U31 0x7FFFFFFF
#define U32 0xFFFFFFFF
printf ("U31 right shifted: 0x%08x\n", (U31 >> 30));
printf ("U32 right shifted: 0x%08x\n", (U32 >> 30));
Output:
U31 right shifted: 0x00000001 (expected)
U32 right shifted: 0xffffffff (not expected)
看起來(在沒有任何人有詳細知識的情況下)Mac OS X v5.0.1 的 XCode 中的 C 編譯器將 MSB 保留為每次移位時都會拉動的進位位。
有點煩人,反過來不是真的:-
#define ST00 0x00000001
#define ST01 0x00000002
printf ("ST00 left shifted: 0x%08x\n", (ST00 << 30));
printf ("ST01 left shifted: 0x%08x\n", (ST01 << 30));
Output:
ST00 left shifted: 0x40000000
ST01 left shifted: 0x80000000
我完全同意上面那些斷言操作數的符號與移位運算符的行為無關的人。
任何人都可以闡明 C 的 Posix4 實現的規范嗎? 我覺得肯定的答案可能就在那里。
與此同時,似乎唯一的解決方法是按照以下方式構建;-
#define CARD2UNIVERSE(c) (((c) == 32) ? 0xFFFFFFFF : (U31 >> (31 - (c))))
這有效 - 令人惱火但必要。
以防萬一,如果您希望右移后負數的第一位為 0,我們可以做的是將該負數與 INT_MIN 進行異或,這將使其 msb 為零,我知道這不是適當的算術移位,但會完成工作
即使有幾個答案,我也會添加一個與整數中的位數及其符號無關的版本。
假設我們有一個n
位的整數,並且我們希望將前s
位清零。
// this could be any type of integer including long long
// e.g. typedef unsigned int MyInt;
typedef long long MyInt;
size_t n = sizeof(MyInt);
size_t s = 5; // say we want the first five bits set to zero
// get the most significant bit set to 1
#define MSB1 (((MyInt)1) << (n - 1)) // MSB1 = 10000000....00
// flip the bits
#define MSB0 (~MSB1) // MSB0 = 01111111....11
// Now shift right. The bits will fill with zero regardless of
// whether MyInt is signed or unsigned, because we have a leading 0,
// and the integer is not negative regardless of the signed-ness.
// but we will have to shift right 1 time less.
#define MASK (MSB0 >> (s - 1)) // MASK = 00000111....11
或者,
#define MASK ((~(((MyInt)1) << (n - 1))) >> (s - 1))
// Still, MASK = 00000111....11
如果你想先用零右移任意整數,那么你可以,
// Shift right once
MyInt x = 0xff..ff0;
MyInt result = x >> 1; // 0xff..ff8;
// Bitwise and with `MSB0`, that is `0111...11`, thereby flipping the MSB to 0.
result &= MSB0; // 0x7f..ff8
Shift right the rest of times.
result >>= (s - 1) // 0x0f..fff
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.