Monday, April 9, 2012

Hàm cộng hai số nguyên không dấu 32-bit viết bằng JavaScript

// Đây là hàm cộng 2 words lX và lY. Mỗi word được biểu diễn bằng 32 bits.
// Basics:
// Trước tiên ta cần hiểu bản chất của phép cộng 2 words. Bản chất của phép cộng là gồm phép XOR và phép AND các bits.
// Phép XOR tạo ra tổng 1. Phép AND tạo ra carry out (phần nhớ).
// Sau khi tính được carry out thì ta dịch trái 1 bit carry out này rồi lại đem cộng với tổng 1.
// Sở dĩ ta phải dịch trái 1 bit vì theo quy tắc thì phần carry out phải được cộng vào phần đứng trước.
// Việc cộng này lại quay về phép XOR và phép AND. Cứ lặp đi lặp lại XOR rồi AND đến khi carry out gồm toàn bit 0.
// Đến đây phép cộng hoàn tất. Kết quả của phép cộng là kết quả của phép XOR cuối cùng.

// Gọi r là kết quả của phép cộng.
// Nếu r >= (2 mũ 32) thì kết quả trả về là 1 số được biểu diễn bằng 32 bits thấp của r.
// Công việc chủ yếu của hàm này là đi cắt ra 32 bits thấp của r.
// Ban đầu sẽ lấy 30 bits thấp của 2 words đem cộng với nhau tạo ra số lResult.
// Lúc này ta đem lResult cộng với bit thứ 31 và 32 của lX và lY.
// Dựa vào bit thứ 31 của các số lX, lY và lResult mà tạo ra kết quả đúng đắn.
// Ta có trường hợp bit thứ 31 của lX và lY đều là bit 1. Khi đó ta ko cần care đến bit thứ 31 của lResult.
// Trường hợp bit thứ 31 của lX hoặc lY là bit 1. Khi đó ta lại xét tiếp hai trường hợp của bit thứ 31 của lResult.
// Trường hợp bit thứ 31 của lX và lY đều là bit 0. Khi đó ta cũng ko cần care đến bit thứ 31 của lResult.
function AddUnsigned(lX,lY) {
var lX4,lY4,lX8,lY8,lResult;
lX8 = (lX & 0x80000000); // Lấy bit thứ 32 của lX
lY8 = (lY & 0x80000000); // Lấy bit thứ 32 của lY
lX4 = (lX & 0x40000000); // Lấy bit thứ 31 của lX
lY4 = (lY & 0x40000000); // Lấy bit thứ 31 của lY

// Ta có 2 tables sau:
//   lX8|0 0 1 1  lX4|0 0 1 1
//   ---|------- (1)  ---|------- (2)
//   lY8|0 1 0 1      lY4|0 1 0 1

lResult = (lX & 0x3FFFFFFF)+(lY & 0x3FFFFFFF); // Lấy 30 bits thấp của lX + 30 bits thấp của lY

if (lX4 & lY4) { // Nếu cả lX và lY đều có bit thứ 31 là 1
// lResult có dạng
// lResult = 0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (x là 0 hoặc 1)
// lResult ^ 0x80000000 = 1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Lý do mà ta lấy lResult XOR với 0x80000000 là vì cả lX và lY đều có bit thứ 31 là 1 thế nên
// lấy bit thứ 31 của lResult (là 1 hoặc 0) đem cộng với bit thứ 31 của lX và lY thì đều carry out bit 1.
// Bây giờ ta có bit thứ 32 của lResult là 1.
// Việc còn lại phải làm là đem bit thứ 32 của lResult cộng với bit thứ 32 của lX và lY tức là cộng với
// lX8 và lY8. Phép cộng 3 bits này với nhau thực ra là đem 3 bits này XOR với nhau, lúc này ta không quan
// tâm đến bit carry out của phép cộng này vì chúng ta đang tiến hành phép cộng của 2 số được biểu diễn bằng
// 32 bit. Nếu phép cộng mà tạo ra kết quả lớn hơn hoặc bằng (2 mũ 32) thì ta chỉ lấy 32 bit thấp của kết quả
// này làm kết quả của phép cộng.
return (lResult ^ 0x80000000 ^ lX8 ^ lY8);
}

if (lX4 | lY4) { // Nếu một trong hai số lX4 và lY4 có bit thứ 31 là bit 1
if (lResult & 0x40000000) { // Nếu bit thứ 31 của lResult là bit 1

// lResult = 01xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx       lResult = 01xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// lX4     = 01000000000000000000000000000000  hoặc lX4     = 00000000000000000000000000000000
// lY4     = 00000000000000000000000000000000       lY4     = 01000000000000000000000000000000
// lResult ^ 0xC0000000 = 10xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
return (lResult ^ 0xC0000000 ^ lX8 ^ lY8);

} else { // Nếu bit thứ 31 của lResult là bit 0
return (lResult ^ 0x40000000 ^ lX8 ^ lY8);
}
} else {
return (lResult ^ lX8 ^ lY8);
}
}

No comments: