2012-01-29 15 views
12

Tôi có một mảng trong C mà tôi muốn xử lý theo cách tương tự như bộ đệm tròn, ví dụ: a[-1] sẽ trả về phần tử cuối cùng của mảng.Kết quả kỳ lạ của số học modulo với số âm và mẫu số chưa ký

Để làm được điều mà tôi cố gắng sử dụng số học modulo (rõ ràng), vấn đề là, tôi nhận được kết quả khá lạ khi số âm có liên quan đến:

-1 % 4 = -1 
-1 % 4U = 3 

Cho đến nay, như vậy tốt.

-1 % 4000 = -1 
(-1+4000U) % 4000U = 3999 
(-1) % 4000U = 3295 

Câu hỏi: Giá trị (3295) không giữ cho (a/b)*b + a%b shall equal a, truncated towards zero (ví a=-1, b=4000) từ tiêu chuẩn C (6.5.5 # 6) do đó, nó không phải là một lỗi cho mỗi gia nhập, nhưng tại sao là tiêu chuẩn được định nghĩa theo cách này ?! Chắc chắn, phải có một số logic trong việc này ...

Làm cách nào để viết a%b để nhận kết quả hợp lý cho số âm a (như (a+b)%b ngừng hoạt động khi abs(a)>b)?

thử nghiệm ứng dụng:

#include <stdio.h> 
int main(int argc, char **argv) { 
    int i=0; 
#define MAX_NUM 4000U 
    int weird = (i-1)%MAX_NUM; 
    printf("%i\n", weird); 
    printf("%i\n", (i-1+MAX_NUM))%MAX_NUM); 
    printf("a: %i, b: %i, a from equation: %i\n", i-1, MAX_NUM, 
    ((i-1)/MAX_NUM)*MAX_NUM + weird); 
    return 0; 
} 

Trả lời

9

Arithmetic trong C luôn (viết tắt của một số oddities với những hoạt động chút thay đổi) thúc đẩy tất cả các toán hạng để một loại phổ biến trước khi thực hiện phẫu thuật. Như vậy:

(-1) % 4000U 

được quảng cáo là (giả sử ints 32 bit):

0xffffffffu % 4000u 

trong đó sản lượng 3295.

Nếu bạn muốn sử dụng số học modula cho offsets mảng đó có thể là tiêu cực, bạn đầu tiên cần phải từ bỏ bằng cách sử dụng số học unsigned trên offsets. Như vậy, kết quả của bạn giờ đây sẽ nằm trong phạm vi -MAX_NUM+1 đến MAX_NUM-1, do định nghĩa xấu xí của C về phân chia số nguyên đã ký và phần còn lại. Nếu mã không phải là hiệu suất quan trọng, chỉ cần thêm if (result<0) result+=MAX_NUM; và được thực hiện với nó. Nếu bạn thực sự cần tránh chi nhánh (và bạn đã đo để xác định rằng bạn cần tránh nó) thì hãy hỏi lại cách tối ưu hóa tính toán này và bản thân tôi hoặc ai đó sáng hơn tôi trên SO chắc chắn sẽ có thể trợ giúp. :-)

5

Như 6.5.3 cho biết, "Chuyển đổi số học thông thường được thực hiện trên các toán hạng." Trong trường hợp ví dụ của bạn:

(-1) % 4000U 

điều đó có nghĩa là chuyển đổi -1 thành unsigned int. Do đó, -1 của bạn thực sự được hiểu là 4294967295 ... mà phần còn lại chính xác là những gì bạn đang thấy: 3295.

"Chuyển đổi số học thông thường" được mô tả trong 6.3.1.8.

Các vấn đề liên quan