Khi những người khác đã chỉ ra tất cả số học 64 bit trong ví dụ của bạn đã được tối ưu hóa. Câu trả lời này tập trung vào câu hỏi trong tiêu đề.
Về cơ bản, chúng tôi xử lý từng số 32 bit dưới dạng chữ số và làm việc trong cơ sở 4294967296.Theo cách này, chúng ta có thể làm việc với số lượng lớn.
Phép cộng và trừ dễ dàng nhất. Chúng tôi làm việc thông qua các chữ số một tại một thời điểm bắt đầu từ ít nhất có ý nghĩa và di chuyển đến quan trọng nhất. Nói chung chữ số đầu tiên được thực hiện với một lệnh thêm/trừ thông thường và các chữ số sau được thực hiện bằng cách sử dụng hướng dẫn cụ thể "thêm mang theo" hoặc "trừ đi vay". Cờ mang trong thanh ghi trạng thái được sử dụng để lấy bit mang/mượn từ một chữ số sang chữ số kế tiếp. Nhờ twos bổ sung có chữ ký và unsigned cộng và trừ là như nhau.
Phép nhân phức tạp hơn một chút, nhân hai số 32 bit có thể tạo ra kết quả 64 bit. Hầu hết các bộ xử lý 32 bit sẽ có hướng dẫn nhân hai số 32 bit và tạo kết quả 64 bit trong hai thanh ghi. Bổ sung sau đó sẽ là cần thiết để kết hợp các kết quả vào một câu trả lời cuối cùng. Nhờ twos bổ sung cho phép nhân đã ký và unsigned giống nhau, kích thước kết quả mong muốn giống như kích thước đối số. Nếu kết quả lớn hơn các đối số thì cần phải chăm sóc đặc biệt.
Để so sánh, chúng tôi bắt đầu từ chữ số quan trọng nhất. Nếu nó bằng nhau, chúng tôi chuyển xuống chữ số tiếp theo cho đến khi kết quả bằng nhau.
Phân vùng quá phức tạp để tôi mô tả trong bài đăng này, nhưng có rất nhiều ví dụ ngoài thuật toán. ví dụ. http://www.hackersdelight.org/hdcodetxt/divDouble.c.txt
Một số ví dụ thực tế từ gcc https://godbolt.org/g/NclqXC, lắp ráp là trong cú pháp intel.
Đầu tiên là sự bổ sung. thêm hai số 64 bit và tạo ra kết quả 64 bit. Asm là giống nhau cho cả hai phiên bản đã ký và chưa ký.
int64_t add64(int64_t a, int64_t b) { return a + b; }
add64:
mov eax, DWORD PTR [esp+12]
mov edx, DWORD PTR [esp+16]
add eax, DWORD PTR [esp+4]
adc edx, DWORD PTR [esp+8]
ret
Điều này khá đơn giản, tải một đối số vào eax và edx, sau đó thêm đối số vào eax và edx, sau đó thêm đối số khác bằng dấu cộng theo sau là dấu cộng. Kết quả còn lại trong eax và edx để trở về người gọi.
Bây giờ nhân số hai số 64 bit để tạo kết quả 64 bit. Một lần nữa mã không thay đổi từ đã ký thành unsigned. Tôi đã thêm một số nhận xét để giúp bạn dễ dàng theo dõi hơn.
Trước khi chúng tôi xem xét mã, hãy xem xét toán học. a và b là các số 64 bit, tôi sẽ sử dụng lo() để biểu thị các bit 32 bit thấp hơn của số 64 bit và hi() đại diện cho 32 bit trên của số 64 bit.
(a * b) = (lo (a) * lo (b)) + (hi (a) * lo (b) * 2^32) + (hi (b) * lo (a) * 2^32) + (hi (b) * hi (a) * 2^64)
(a * b) mod 2^64 = (lo (a) * lo (b)) + (lo (hi) a) * lo (b)) * 2^32) + (lo (hi (b) * lo (a)) * 2^32)
lo ((a * b) mod 2^64) = lo (lo (a) * lo (b))
hi ((a * b) mod 2^64) = hi (lo (a) * lo (b)) + lo (hi (a) * lo (b)) + lo (hi (b) * lo (a))
uint64_t mul64(uint64_t a, uint64_t b) { return a*b; }
mul64:
push ebx ;save ebx
mov eax, DWORD PTR [esp+8] ;load lo(a) into eax
mov ebx, DWORD PTR [esp+16] ;load lo(b) into ebx
mov ecx, DWORD PTR [esp+12] ;load hi(a) into ecx
mov edx, DWORD PTR [esp+20] ;load hi(b) into edx
imul ecx, ebx ;ecx = lo(hi(a) * lo(b))
imul edx, eax ;edx = lo(hi(b) * lo(a))
add ecx, edx ;ecx = lo(hi(a) * lo(b)) + lo(hi(b) * lo(a))
mul ebx ;eax = lo(low(a) * lo(b))
;edx = hi(low(a) * lo(b))
pop ebx ;restore ebx.
add edx, ecx ;edx = hi(low(a) * lo(b)) + lo(hi(a) * lo(b)) + lo(hi(b) * lo(a))
ret
Cuối cùng khi chúng ta thử một bộ phận chúng ta thấy.
int64_t div64(int64_t a, int64_t b) { return a/b; }
div64:
sub esp, 12
push DWORD PTR [esp+28]
push DWORD PTR [esp+28]
push DWORD PTR [esp+28]
push DWORD PTR [esp+28]
call __divdi3
add esp, 28
ret
Trình biên dịch đã quyết định phân chia quá phức tạp để thực hiện nội tuyến và thay vào đó gọi một thói quen thư viện.
Sử dụng 'pow' là cách rất khó chịu và dễ bị lỗi để tính toán số nguyên lũy của 2. Không sử dụng. Lưu ý rằng nếu phép trừ dấu chấm động của bạn không được thực hiện dưới dạng 'dài gấp đôi', 'pow (2,64) -1' 80 sẽ bằng' pow (2,64)' và sau đó đúc nó thành 'unsigned long long' sẽ không hoạt động đúng. –