Nếu bạn đang sử dụng gcc và phiên bản bạn có hỗ trợ số 128 bit (hãy thử sử dụng __uint128_t) so với thực hiện 128 nhân và giải nén 64 bit trên có khả năng là cách hiệu quả nhất để nhận kết quả.
Nếu trình biên dịch của bạn không hỗ trợ số 128 bit, thì câu trả lời của Yakk là chính xác. Tuy nhiên, nó có thể là quá ngắn cho tiêu thụ chung. Đặc biệt, việc triển khai thực tế phải cẩn thận với việc tích luỹ 64 bit tích phân.
Giải pháp đơn giản và di động mà ông đề xuất là chia từng a và b thành 2 số 32 bit và sau đó nhân các số 32 bit đó bằng hoạt động nhân 64 bit. Nếu chúng ta viết:
uint64_t a_lo = (uint32_t)a;
uint64_t a_hi = a >> 32;
uint64_t b_lo = (uint32_t)b;
uint64_t b_hi = b >> 32;
sau đó rõ ràng là:
a = (a_hi << 32) + a_lo;
b = (b_hi << 32) + b_lo;
và:
a * b = ((a_hi << 32) + a_lo) * ((b_hi << 32) + b_lo)
= ((a_hi * b_hi) << 64) +
((a_hi * b_lo) << 32) +
((b_hi * a_lo) << 32) +
a_lo * b_lo
cung cấp các tính toán được thực hiện bằng 128 bit (hoặc cao hơn) số học.
Nhưng vấn đề này yêu cầu chúng tôi thực hiện tất cả các phép tính bằng cách sử dụng số học 64 bit, vì vậy chúng tôi phải lo lắng về tình trạng tràn.
Vì a_hi, a_lo, b_hi và b_lo là tất cả các số 32 bit chưa ký, sản phẩm của chúng sẽ phù hợp với số 64 bit không dấu mà không bị tràn. Tuy nhiên, kết quả trung gian của phép tính trên sẽ không.
Đoạn mã dưới đây sẽ thực hiện mulhi (a, b) khi mathemetics phải được thực hiện theo modulo 2^64:
uint64_t a_lo = (uint32_t)a;
uint64_t a_hi = a >> 32;
uint64_t b_lo = (uint32_t)b;
uint64_t b_hi = b >> 32;
uint64_t a_x_b_hi = a_hi * b_hi;
uint64_t a_x_b_mid = a_hi * b_lo;
uint64_t b_x_a_mid = b_hi * a_lo;
uint64_t a_x_b_lo = a_lo * b_lo;
uint64_t carry_bit = ((uint64_t)(uint32_t)a_x_b_mid +
(uint64_t)(uint32_t)b_x_a_mid +
(a_x_b_lo >> 32)) >> 32;
uint64_t multhi = a_x_b_hi +
(a_x_b_mid >> 32) + (b_x_a_mid >> 32) +
carry_bit;
return multhi;
Như Yakk chỉ ra, nếu bạn không nhớ là tắt bởi 1 trong 64 bit trên, bạn có thể bỏ qua tính toán của bit mang.
Tham chiếu: http://blogs.msdn.com/b/oldnewthing/archive/2014/12/08/10578956.aspx –
GCC có 'uint128_t' cho mục đích này. Visual Studio không có tùy chọn như vậy mặc dù. –
@MooingDuck Có vẻ như uint128_t không tồn tại trong môi trường của tôi (Tôi đang sử dụng Xcode theo osx). Hơn nữa, điều đó sẽ tính toán rõ ràng cả phần cao hơn và thấp hơn của phép nhân đó, mà tôi muốn tránh. –