假設我有一個以不尋常位數編碼的有符號數字,例如 12。如何有效地將其轉換為標準 C 值?以下作業但需要一個中間變數:
#include <stdio.h>
int main() {
unsigned short U12=0xFFF; // 12-bit signed number, as coded in hex
unsigned short XT=U12<<4; // 16 bits minus 12 is 4...
short SX=(*(short*)&XT)>>4; // Signed shift. Is that standard C ?
printf("X %d\n", SX, SX);
return 0;
}
輸出:
U12=0x0: 00000000 0
U12=0x1: 00000001 1
U12=0x7FF: 000007FF 2047
U12=0x800: FFFFF800 -2048
U12=0x801: FFFFF801 -2047
U12=0xFFF: FFFFFFFF -1
有沒有更直接的方法可以在沒有中間變數的情況下做到這一點?
uj5u.com熱心網友回復:
這是一種便攜式方式(未經測驗):
short SX = (short)U12 - ((U12 & 0x800) << 1);
(將 0x800 和 0x1000 替換為 (1<<whatever) 以獲得不同的位數)。
使用位移操作直接運算子號位的方法往往會呼叫 UB。
uj5u.com熱心網友回復:
您的方法的一個問題是您似乎假設 ashort和的寬度unsigned short為 16 位。但是,ISO C 標準并不能保證這一點。該標準僅指定寬度必須至少為16 位。
如果要使用保證為 16 位寬的資料型別,則應使用資料型別uint16_t和int16_t代替。
另一個問題是您的代碼假設您的程式運行的平臺以與“12 位有符號數”相同的方式表示具有負值的整數。但是,與最新的 ISO C 標準相比,ISO C 標準允許平臺使用以下任何表示:
- 二進制補碼
- 一個的補碼
- 有符號的幅度
假設您的“12 位有符號數”具有二進制補碼表示,但您希望您的代碼在所有平臺上正確運行,而不管平臺內部使??用哪種表示,那么您將不得不撰寫如下代碼:
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
int main( void )
{
uint16_t U12=0xFFF;
int16_t converted;
//determine sign bit
bool sign = U12 & 0x800;
if ( sign )
{
//value is negative
converted = -2048 ( U12 & 0x7FF );
}
else
{
//value is positive
converted = U12;
}
printf( "The converted value is: %hd\n", converted );
}
該程式具有以下輸出:
The converted value is: -1
正如評論部分所指出的,這個程式可以簡化為以下內容:
#include <stdio.h>
#include <stdint.h>
int main( void )
{
uint16_t U12=0xFFF;
int16_t converted = U12 - ( (U12 & 0x800) << 1 );
printf( "The converted value is: %hd\n", converted );
}
uj5u.com熱心網友回復:
讓編譯器完成艱苦的作業:
#define TOINT(val, bits) (((struct {int val: bits;}){val}).val)
或更一般的
#define TOINT(type, v, bits) (((struct {type val: bits;}){v}).val)
用法:
int main(void)
{
int int12bit = 0xfff;
printf("%d\n", TOINT(int, int12bit, 12));
}
或更簡單的版本:
int main(void)
{
int int12bit = 0b100110110010;
printf("%d\n", TOINT(int12bit, 12));
}
編譯器將為您的目標平臺和目標型別選擇最有效的方法:
int convert12(int val)
{
return TOINT(val,12);
}
long long convert12ll(unsigned val)
{
return TOINT(long long, val,12);
}
https://godbolt.org/z/P4b4rM4TT
類似的方式,您可以將N位有符號整數轉換為“M”位有符號整數
#define TOINT(v, bits) (((struct {long long val: bits;}){v}).val)
#define FROM_N_TO_M(val, N, M) TOINT(TOINT(val, N), M)
uj5u.com熱心網友回復:
假設這是一個用二進制補碼0xFFF表示的 12 位數字(不一定是這種情況),這相當于并假設我們的 CPU 也使用 2 的補碼(極有可能),那么:-1
由于隱式型別提升,使用小整數型別(如(無符號)char或short內部按位運算是危險的。假設 32 位系統具有 16 位short,如果將這樣的變數(有符號或無符號)傳遞給移位的左運算元,它將始終被提升為 (有符號) int。
在上述假設下,那么:
U12<<4給出簽名0xFFF0型別的結果.int然后,您在分配時將其轉換為無符號短。- 這種轉換
*(short*)&XT很臭,但 C 中的指標別名規則允許。記憶體的內容現在被重新解釋為 CPU 的簽名格式。 the_signed_short >> 4當左運算子為負時呼叫實作定義的行為。它并不一定會導致您期望的算術移位。這也可能是一個合乎邏輯的轉變。%X和%d期望unsigned int和int分別,所以傳遞一個短路是錯誤的。在這里,您可以通過可變引數函式引數的強制默認提升來保存,在這種情況下int,再次提升到 。
所以總的來說這里有很多代碼氣味。
在上述 32 位系統上執行此操作的更好且大部分定義明確的方法是:
int32_t u12_to_i32 (uint32_t u12)
{
u12 &= 0xFFF; // optionally, mask out potential clutter in upper bytes
if(u12 & (1u<<11)) // if signed, bit 11 set?
{
u12 |= 0xFFFFFFu << 12; // "sign extend"
}
return u12; // unsigned to signed conversion, impl.defined
}
此處的所有位操作都是在無符號型別上完成的,不會在 32 位系統上靜默提升。這種方法還具有使用純十六進制位掩碼且沒有“幻數”的優點。
帶有測驗用例的完整示例:
#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>
int32_t u12_to_i32 (uint32_t u12)
{
u12 &= 0xFFF; // optionally, mask out potential clutter in upper bytes
if(u12 & (1u<<11)) // if signed, bit 11 set?
{
u12 |= 0xFFFFFFu << 12; // "sign extend"
}
return u12; // unsigned to signed conversion, impl.defined
}
int main (void)
{
uint32_t u12;
int32_t i32;
u12=0; i32 = u12_to_i32(u12);
printf(""PRIX32 "-> "PRIX32 " = %"PRIi32 "\n", u12, (uint32_t)i32, i32);
u12=0x7FF; i32 = u12_to_i32(u12);
printf(""PRIX32 "-> "PRIX32 " = %"PRIi32 "\n", u12, (uint32_t)i32, i32);
u12=0x800; i32 = u12_to_i32(u12);
printf(""PRIX32 "-> "PRIX32 " = %"PRIi32 "\n", u12, (uint32_t)i32, i32);
u12=0xFFF; i32 = u12_to_i32(u12);
printf(""PRIX32 "-> "PRIX32 " = %"PRIi32 "\n", u12, (uint32_t)i32, i32);
return 0;
}
輸出(gcc x86_64 Linux):
00000000-> 00000000 = 0
000007FF-> 000007FF = 2047
00000800-> FFFFF800 = -2048
00000FFF-> FFFFFFFF = -1
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/417881.html
標籤:
下一篇:在C中關閉檔案
