2011-04-28 23 views
14

Tôi có một đoạn ngắn, instr, trông như thế này:Đăng mở rộng một số chín-bit trong C

1110xxx111111111 

tôi cần phải rút ra khỏi bit 0-9, mà tôi làm với (instr & 0x1FF). Số lượng này sau đó được lưu trữ trong một đoạn ngắn mới. Vấn đề là khi điều này xảy ra, nó sẽ trở thành 0x0000000111111111, không phải 0x1111111111111111 như tôi muốn. Làm thế nào tôi có thể sửa lỗi này? Cảm ơn!

EDIT

Dưới đây là các mã:

short instr = state->mem[state->pc]; 
unsigned int reg = instr >> 9 & 7; // 0b111 
state->regs[reg] = state->pc + (instr & 0x1FF); 

Đây là một mô phỏng mà đọc trong lắp ráp. state là máy, regs[] là các thanh ghi và pc là địa chỉ của lệnh hiện tại trong mem[]. Điều này là tốt nếu chín bit cuối cùng đại diện cho một số dương, nhưng nếu chúng đại diện cho -1, nó được lưu trữ như tất cả 1, được hiểu là một giá trị tích cực bởi mã của tôi.

+1

Không chắc tôi hiểu câu hỏi hoặc quá trình bạn đang sử dụng. Chăm sóc để chia sẻ mã bạn đang sử dụng để có được những kết quả này? – joce

+0

Điều này không rõ ràng, ai sẽ trở thành '0x1111111111111111'? – MByD

Trả lời

19

Bạn có thể thực hiện thủ công: (instr & 0x1FF) | ((instr & 0x100) ? 0xFE00 : 0). Điều này kiểm tra bit dấu (bit trên cùng bạn đang giữ lại, 0x100) và đặt tất cả các bit ở trên nó nếu bit dấu được thiết lập. Bạn có thể mở rộng này đến 5 bit bằng cách điều chỉnh mặt nạ thành 0x1F, 0x100xFFE0, là 5 bit thấp hơn, bit thứ 5 và tất cả các bit 5-16 tương ứng. Hoặc bạn có thể tìm thấy một số lý do để chỉ định các bit cho phần trên của một chữ ký ngắn và chuyển chúng xuống (nhận được một dấu mở rộng trong quá trình): short x = (instr & 0x1FF) << 7; x >>= 7; Sau này thực sự có thể kết thúc đơn giản hơn trong lắp ráp và sẽ không liên quan đến chi nhánh. Nếu instr, hãy ký việc này có thể được thực hiện bằng một biểu thức duy nhất: (instr & 0x1FF) <<7>> 7. Kể từ đó đã loại bỏ các bit trên nó đơn giản hóa để instr <<7>> 7. Thay thế 7 bằng 11 cho 5 bit (16-5).

+0

Làm thế nào điều này có thể được điều chỉnh cho một số 5-bit? Tôi cần phải xử lý cả hai loại trong chương trình này. –

+3

Về sau, bạn không thể chỉ làm '(instr & 0x1FF << 7) >> 7'? –

+1

Tôi đã cập nhật câu trả lời để giải quyết các nhận xét ... –

2

Tôi không chắc chắn cách bạn sẽ nhận được 13 1 bit sau khi mặt nạ với 0x1ff, nhưng điều này sẽ ký gia hạn số 9 bit thành một đoạn ngắn 16 bit. Không đẹp (hoặc đặc biệt hiệu quả), nhưng nó hoạt động:

 
(instr & 0x1ff) | (0xfe00 * ((instr & 0x100) >> 8)) 

Mặt nạ ra bit dấu, chuyển sang vị trí 1 để lấy 0/1. Nhân số này với các bit trên, nếu ký hiệu là 1, thì số 9 bit sẽ là OR'ed với 0xfe, sẽ đặt tất cả các bit phía trên thành 1.

+0

Chưa thử nghiệm điều này, nhưng làm thế nào điều này có thể được điều chỉnh cho một số 5-bit? Tôi cần có cả hai loại trong chương trình này. Và bạn đúng về kết quả; Tôi đã làm việc hàng giờ, nên tôi kiệt sức. : D –

+0

Mặt nạ ra bit dấu và chuyển bit đó sang vị trí của 1 (vì vậy bạn nhận được 1 nếu bạn có số âm và 0 nếu không). Sau đó tạo một mặt nạ OR cho các bit mở rộng và nhân nó bằng dấu đã dịch chuyển. Nếu số gốc của bạn là số âm, mặt nạ này sẽ giữ nguyên tất cả, và nếu không sẽ là tất cả các số không. HOẶC điều này với số mặt nạ của bạn để điền vào các bit trên với 1's. –

4
(instr & 0x1FF) * (1 - ((unsigned short)(instr & 0x100) >> 7)) 

Cách hoạt động? Nó chọn bit dấu của bạn và chuyển nó sang vị trí của 2. Điều này được sử dụng để tạo ra một trong hai giá trị 1 (nếu bit dấu hiệu của bạn vắng mặt) hoặc -1 (nếu bit dấu hiệu của bạn đã có mặt).

Giải pháp này không có nhánh và không phụ thuộc vào hành vi không xác định.

+3

Trên một vi điều khiển không xác định tôi có lẽ sẽ sai ở phía bên của nhánh hơn là nhân. Các hình phạt chi nhánh trên các CPU như vậy là nhỏ và hình phạt nếu không có hệ số phần cứng (hoặc thậm chí nếu có!) Là cao hơn nhiều. Trong thực tế, với nguy cơ không có * thùng shifter * có lẽ là đơn giản chi nhánh/thử nghiệm là tốt nhất. –

2

Chỉ cần chạm vào điều này để tìm kiếm thứ gì đó khác, có thể hơi muộn, nhưng có thể nó sẽ hữu ích cho người khác. AFAIAC tất cả các lập trình viên C nên bắt đầu lập trình assembler.

Mở rộng ký hiệu dù sao cũng dễ dàng hơn nhiều so với 2 đề xuất khác. Chỉ cần chắc chắn rằng bạn đang sử dụng các biến đã ký và sau đó sử dụng 2 ca.

short instr = state->mem[state->pc]; 
unsigned int reg = (instr >> 9) & 7; // 0b111 
instr &= 0x1ff; // get lower 9 bits 
instr = ((instr << 7) >> 7); // sign extend 
state->regs[reg] = state->pc + instr; 

Nếu biến được ký kết sau đó trình biên dịch C dịch >> để Arithmetic phím Shift phải nơi còn lưu giữ dấu. Hành vi này là nền tảng độc lập. Vì vậy, giả sử rằng instr bắt đầu với 0x1ff thì chúng ta có, < < 7 sẽ SL (Shift Left) giá trị để instr bây giờ là 0xff80, sau đó >> 7 sẽ ASR giá trị để instr bây giờ là 0xffff.

+0

Có phải "instr & = 0x1ff; // nhận được 9 bit thấp hơn" cần thiết trong ngữ cảnh này không? Những bit được đẩy đi anyway :) –

8

* Không lập chi nhánh yêu cầu *

Xem http://graphics.stanford.edu/~seander/bithacks.html#FixedSignExtend cho một danh sách các hacks chút rất hữu ích. Cụ thể, đăng ký mở rộng một số đơn giản như:

/* generate the sign bit mask. 'b' is the extracted number of bits */ 
int m = 1U << (b - 1); 

/* Transform a 'b' bits unsigned number 'x' into a signed number 'r' */ 
int r = (x^m) - m; 

Bạn có thể cần để xóa các bit trên cùng của 'x' nếu họ không zero (x = x & ((1U << b) - 1);) trước khi sử dụng các thủ tục trên.

Nếu số bit 'b' được biết tại thời gian biên dịch (ví dụ, 5 bit trong trường hợp của bạn) thì thậm chí còn có một giải pháp đơn giản hơn (điều này có thể kích hoạt lệnh mở rộng ký hiệu cụ thể nếu bộ xử lý hỗ trợ nó và trình biên dịch là đủ thông minh):

struct {signed int x:5;} s; 
r = s.x = x; 
1

Đây là cách tinh chỉnh các câu trả lời trước nhưng chưa có giải pháp hoàn toàn chung nào được trình bày cho đến nay. Macro này sẽ mở rộng một giá trị v với sb cho biết số bit bit dựa trên 0 của bit dấu.

#define SIGNEX(v, sb) ((v) | (((v) & (1 << (sb))) ? ~((1 << (sb))-1) : 0)) 

int32_t x; 

SIGNEX(x, 15); // Sign bit is bit-15 (16th from the right) 
SIGNEX(x, 23); // Sign bit is bit-23 (24th from the right) 

Nó sử dụng phân nhánh để tối đa hóa tính di động trên nền tảng thiếu một nhân phần cứng hoặc shifter thùng.

0

Một giải pháp dễ dàng hơn là điều này, cho x là một số bổ sung 5-bit 2 's, nhìn:

z = (x^16)-16 
Các vấn đề liên quan