Câu hỏi C ++ Cách tốt nhất để lấy phân chia số nguyên và phần còn lại


Tôi chỉ tự hỏi, nếu tôi muốn chia một b, và quan tâm cả trong kết quả c và phần còn lại (ví dụ như tôi có số giây và muốn chia nó thành phút và giây), cách tốt nhất để đi về nó?

Nó sẽ là

int c = (int)a / b;
int d = a % b;

hoặc là

int c = (int)a / b;
int d = a - b * c;

hoặc là

double tmp = a / b;
int c = (int)tmp;
int d = (int)(0.5+(tmp-c)*b);

hoặc là

có thể có một chức năng phép thuật cho cả hai cùng một lúc?


76
2017-08-15 20:21


gốc


tất cả các câu trả lời dưới đây có vẻ hợp lý, tôi chỉ muốn thêm rằng bất kỳ mucking với double (mục cuối cùng của bạn) dường như với tôi như một ý tưởng tồi, bạn sẽ kết thúc với những con số không xếp hàng, và có thể khiến bạn mất hiệu suất và kích thước thực thi (luôn là vấn đề đối với tôi trên một số hệ thống nhúng). - nhed
Thứ ba là tùy chọn BAD: nếu tmp = 54.999999999999943157 thì sao? Điều này nói rằng, phong cách đúc cũ không bao giờ là một điều thông minh để làm. - jimifiki


Các câu trả lời:


Trên x86 phần còn lại là một sản phẩm phụ của bộ phận chính nó vì vậy bất kỳ trình biên dịch nửa phong nha nào cũng có thể sử dụng nó (và không thực hiện div lần nữa). Điều này có thể được thực hiện trên các kiến ​​trúc khác.

Chỉ dẫn: DIV src

Lưu ý: Phân chia chưa được ký. Chia bộ tích lũy (AX) cho "src". Nếu ước số   là một giá trị byte, kết quả được đặt cho AL và còn lại cho AH. Nếu ước số   là một giá trị từ, sau đó DX: AX được chia cho "src" và kết quả được lưu trữ   trong AX và phần còn lại được lưu trữ trong DX.

int c = (int)a / b;
int d = a % b; /* Likely uses the result of the division. */

76
2017-08-15 20:23



Tôi nghĩ rằng nhiều người quen biết từ trường tiểu học khi làm một bộ phận bạn nhận được phần còn lại miễn phí. Câu hỏi thực sự là: các trình biên dịch của chúng ta có đủ thông minh để tận dụng điều này không?
@jdv: Tôi sẽ không ngạc nhiên. Đó là một tối ưu hóa rất đơn giản. - Jon Purdy
Tôi đã thử một cách nhanh chóng. Với g ++ 4.3.2 sử dụng -O2, đầu ra của bộ kết hợp hiển thị rõ ràng nó bằng cách sử dụng một idivl hướng dẫn và sử dụng kết quả trong eax và edx. Tôi đã bị sốc nếu không. - Fred Larson
@EuriPinhollow Đồng ý đây không phải là câu hỏi về x86. Tôi chỉ cho nó như một ví dụ, với giả định rất đáng ngờ rằng các kiến ​​trúc khác có thể làm một cái gì đó tương tự. - cnicutar
Bạn cần phải nói với trình biên dịch để tối ưu hóa, mặc dù, ít nhất là cho g + +. Một thử nghiệm đơn giản với g ++ 6.3.0 trên x86_64 cho thấy rằng không có tối ưu hóa nào cả, bạn nhận được hai idivl hướng dẫn, nhưng với -O1 hoặc cao hơn, bạn có được một. Như hướng dẫn nói, "Không có bất kỳ tùy chọn tối ưu hóa nào ... Các tuyên bố là độc lập". - Tom Zych


std::div trả về một cấu trúc với cả kết quả và phần dư.


59
2017-08-15 20:24



Tôi tò mò muốn biết nếu điều này thực sự hiệu quả hơn, nói, tùy chọn 1 trên một trình biên dịch hiện đại. - Greg Howell
Nice, tôi không biết. Nó có nhanh hơn không?
Tốt đẹp. Bạn sẽ xảy ra để biết nếu một trong những được thực hiện cho lâu dài ở đâu đó? - Cookie
@ Cookie: C ++ 03 không có khái niệm về long long, nhưng rất có khả năng trình biên dịch của bạn có long long quá tải của std::div như một phần mở rộng. - ildjarn
@Greg: Tôi không nghĩ rằng nó là, cho rằng nó rất có thể phải viết kết quả cho một cấu trúc trong bộ nhớ và trả lại nó, mà (trừ khi nó biên dịch như nội tuyến và truy cập struct được tối ưu hóa đi) là một hình phạt lớn hơn làm một hướng dẫn phân chia thêm. - pezcode


Trên x86 ít nhất, g ++ 4.6.1 chỉ sử dụng IDIVL và nhận cả hai từ lệnh đơn đó.

Mã C ++:

void foo(int a, int b, int* c, int* d)
{
  *c = a / b;
  *d = a % b;
}

mã x86:

__Z3fooiiPiS_:
LFB4:
    movq    %rdx, %r8
    movl    %edi, %edx
    movl    %edi, %eax
    sarl    $31, %edx
    idivl   %esi
    movl    %eax, (%r8)
    movl    %edx, (%rcx)
    ret

22
2017-08-15 20:41



Đơn đặt hàng có quan trọng không? Ví dụ: nếu bạn đang lặp qua một /= - bạn có thể cần sử dụng biến tạm thời để giữ phân chia trước. - Annan


Kiểm tra mã mẫu div () và phân chia kết hợp & mod. Tôi đã biên dịch chúng với gcc -O3, tôi đã phải thêm lệnh gọi doNothing để ngăn trình biên dịch tối ưu hóa mọi thứ (đầu ra sẽ là 0 cho giải pháp phân chia + mod).

Mang nó theo một hạt muối:

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>

extern doNothing(int,int); // Empty function in another compilation unit

int main() {
    int i;
    struct timeval timeval;
    struct timeval timeval2;
    div_t result;
    gettimeofday(&timeval,NULL);
    for (i = 0; i < 1000; ++i) {
        result = div(i,3);
        doNothing(result.quot,result.rem);
    }
    gettimeofday(&timeval2,NULL);
    printf("%d",timeval2.tv_usec - timeval.tv_usec);
}

Đầu ra: 150

#include <stdio.h>
#include <sys/time.h>
#include <stdlib.h>

extern doNothing(int,int); // Empty function in another compilation unit

int main() {
    int i;
    struct timeval timeval;
    struct timeval timeval2;
    int dividend;
    int rem;
    gettimeofday(&timeval,NULL);
    for (i = 0; i < 1000; ++i) {
        dividend = i / 3;
        rem = i % 3;
        doNothing(dividend,rem);
    }
    gettimeofday(&timeval2,NULL);
    printf("%d",timeval2.tv_usec - timeval.tv_usec);
}

Đầu ra: 25


8
2017-08-15 20:57





Ngoài những điều đã nói ở trên std :: div gia đình của các chức năng, đó cũng là std :: remquo gia đình của các chức năng, trả lại rem-ai và nhận được quo-tient thông qua một con trỏ được truyền vào.

[Chỉnh sửa:] Nó trông giống như std :: remquo không thực sự trả lại thương sau tất cả.


5
2017-09-19 18:56





Tất cả đều bình đẳng, giải pháp tốt nhất là giải pháp thể hiện rõ ràng ý định của bạn. Vì thế:

int totalSeconds = 453;
int minutes = totalSeconds / 60;
int remainingSeconds = totalSeconds % 60;

có lẽ là tốt nhất trong ba tùy chọn bạn đã trình bày. Tuy nhiên, như đã nêu trong các câu trả lời khác, div phương pháp sẽ tính cả hai giá trị cho bạn cùng một lúc.


3
2017-08-15 20:27



Rõ ràng câu hỏi đang hỏi về tốc độ ... - Pacerier


Bạn không thể tin tưởng g ++ 4.6.3 ở đây với số nguyên 64 bit trên nền tảng intel 32 bit. a / b được tính bằng một cuộc gọi tới divdi3 và% b được tính bằng một lệnh gọi tới moddi3. Tôi thậm chí có thể đưa ra một ví dụ tính a / b và a-b * (a / b) với các cuộc gọi này. Vì vậy, tôi sử dụng c = a / b và a-b * c.

Phương thức div cho phép gọi hàm có tính cấu trúc div, nhưng cuộc gọi hàm dường như không hiệu quả trên nền tảng có hỗ trợ phần cứng cho loại tích phân (tức là số nguyên 64 bit trên nền tảng bit / intel 64 bit).


3
2018-05-04 01:38





Bạn có thể sử dụng một mô-đun để nhận phần còn lại. Mặc dù câu trả lời của @ cnicutar có vẻ sạch hơn / trực tiếp hơn.


-4
2017-08-15 20:26



Có, áp phích ban đầu được sử dụng toán tử mô-đun, câu hỏi là làm thế nào để làm cho nó hiệu quả. - Mark Lakata