2009-05-06 66 views
23

Trong Python và Ruby, chia số nguyên ký truncates về phía vô cực tiêu cực, và có chữ ký số nguyên mô đun có cùng ký vào toán hạng thứ hai:Python kiểu phân chia số nguyên & mô đun trong C

>>> (-41)/3 
-14 
>>> (-41) % 3 
1 

Tuy nhiên, trong C và Java , chia số nguyên ký truncates đối với 0, và ký số nguyên mô đun có dấu hiệu tương tự như các toán hạng đầu tiên:

printf("%d\n", (-41)/3); /* prints "-13" */ 
printf("%d\n", (-41) % 3); /* prints "-2" */ 

cách đơn giản nhất và hiệu quả nhất trong C để thực hiện cùng một loại bộ phận và mô đun như trong Python là gì và Ruby?

Trả lời

12

Hướng làm tròn với phân chia số nguyên đã ký không được chỉ định trong các tiêu chuẩn C cũ hơn. Tuy nhiên, trong C99 nó được xác định để quay về không.

Đây là mã di động mà làm việc với tất cả các phiên bản của các tiêu chuẩn C và kiến ​​trúc CPU:

int py_div(int a, int b) 
{ 
    if (a < 0) 
    if (b < 0) 
     return -a/-b; 
    else 
     return -(-a/b) - (-a % b != 0 ? 1 : 0); 
    else if (b < 0) 
     return -(a/-b) - (a % -b != 0 ? 1 : 0); 
    else 
     return a/b; 
} 

int py_mod(int a, int b) 
{ 
    if (a < 0) 
    if (b < 0) 
     return -(-a % -b); 
    else 
     return -a % b - (-a % -b != 0 ? 1 : 0); 
    else if (b < 0) 
     return -(a % -b) + (-a % -b != 0 ? 1 : 0); 
    else 
     return a % b; 
} 

tôi đã làm một số xét nghiệm hời hợt và nó xuất hiện để cung cấp cho các kết quả tương tự như Python. Mã này có thể không hiệu quả tối đa, nhưng trình biên dịch C tốt có thể tối ưu hóa nó một cách đầy đủ, đặc biệt nếu bạn đặt mã trong tiêu đề là các hàm tĩnh.

Bạn cũng có thể xem câu hỏi liên quan chặt chẽ này: Integer division rounding with negatives in C++.

+1

Nếu bạn muốn sử dụng hiệu quả bảng tra cứu. Nếu mã này là một vấn đề hiệu quả, sự thay thế thực sự duy nhất sẽ là sử dụng các toán tử thông dụng/và% và sống với phép làm tròn của chúng. –

+3

Điều này khá gọn gàng.Sẽ rất hữu ích khi có một số niềng răng trong mã này (với nhiều điều kiện làm tổ, thật khó để nói điều gì đang xảy ra ở đâu ...) –

+0

Có lẽ đây là vấn đề về hương vị, nhưng tôi không đồng ý rằng việc thêm niềng răng sẽ làm điều này dễ đọc hơn. Khi đọc mã, tôi nhìn vào thụt đầu dòng thay vì đếm niềng răng trong đầu. –

-1

Nó đào sâu vào thế giới xấu xí của phao, nhưng những cung cấp cho câu trả lời đúng trong Java:

public static int pythonDiv(int a, int b) { 
    if (!((a < 0)^(b < 0))) { 
     return a/b; 
    } 
    return (int)(Math.floor((double)a/(double)b)); 
} 

public static int pythonMod(int a, int b) { 
    return a - b * pythonDiv(a,b); 
} 

tôi làm không khẳng định về tính hiệu quả của họ.

+0

Trong trường hợp cụ thể này có khả năng sẽ tạo ra kết quả chính xác cho tất cả các đầu vào có thể, tôi vẫn tránh nó vì thực tế nó sử dụng một hàm dấu chấm động cho các hoạt động tách rời. Nếu bạn thay 'float' cho' double' hoặc 'long' cho' int', nó sẽ tạo ra kết quả sai cho một số đầu vào. Ngoài ra, nếu bạn chuyển trường hợp này sang C hoặc C++ trên nền tảng trong đó 'int' là 64 bit, nó cũng sẽ tạo ra kết quả sai cho một số đầu vào nhất định. – blubberdiblub

6

Đối với modulo, tôi thấy đơn giản nhất sau đây. Nó không quan trọng những gì ước dấu hiệu của việc thực hiện là, chúng ta chỉ cần ép buộc kết quả vào dấu hiệu chúng ta muốn:

r = n % a; 
if (r < 0) r += a; 

Rõ ràng đó là cho dương a. Đối với tiêu cực là bạn cần:

r = n % a; 
if (r > 0) r += a; 

nào (có lẽ một chút gây nhầm lẫn) kết hợp để cung cấp cho những điều sau đây (trong C++ Trong C làm điều tương tự với int, và sau đó tediously viết một bản sao cho lâu dài.):

template<typename T> T sign(T t) { return t > T(0) ? T(1) : T(-1); } 

template<typename T> T py_mod(T n, T a) { 
    T r = n % a; 
    if (r * sign(a) < T(0)) r += a; 
    return r; 
} 

Chúng tôi có thể sử dụng hàm "sign" có giá trị hai giá rẻ vì chúng tôi đã biết! = 0 hoặc% sẽ không được xác định.

Áp dụng nguyên tắc tương tự để phân chia (nhìn vào sản lượng chứ không phải là đầu vào):

q = n/a; 
// assuming round-toward-zero 
if ((q < 0) && (q * a != n)) --q; 

Các phép nhân cho là có thể là đắt hơn so với cần thiết, nhưng có thể vi được tối ưu hóa về sau một mỗi kiến ​​trúc cơ sở nếu cần thiết. Ví dụ, nếu bạn có một op phân chia cung cấp cho bạn thương và phần còn lại, sau đó bạn được sắp xếp để phân chia.

[Chỉnh sửa: có thể có một số trường hợp cạnh mà điều này không đúng, ví dụ: nếu thương hoặc phần còn lại là INT_MAX hoặc INT_MIN.Tuy nhiên, việc mô phỏng toán trăn cho các giá trị lớn là một câu hỏi hoàn toàn khác ;-)]

[Chỉnh sửa khác: không phải là triển khai python chuẩn được viết bằng C? Bạn có thể ghé qua những nguồn cho những gì họ làm]

2

Đây là một thực hiện đơn giản của bộ phận có sàn và mô đun trong C89:

#include <stdlib.h> 

div_t div_floor(int x, int y) 
{ 
    div_t r = div(x, y); 
    if (r.rem && (x < 0) != (y < 0)) { 
     r.quot -= 1; 
     r.rem += y; 
    } 
    return r; 
} 

Ở đây, div được sử dụng bởi vì nó có well-defined behavior.

Nếu bạn đang sử dụng C++ 11, đây là một thực hiện templated bộ phận có sàn và mô đun:

#include <tuple> 

template<class Integral> 
std::tuple<Integral, Integral> div_floor(Integral x, Integral y) 
{ 
    typedef std::tuple<Integral, Integral> result_type; 
    const Integral quot = x/y; 
    const Integral rem = x % y; 
    if (rem && (x < 0) != (y < 0)) 
     return result_type(quot - 1, rem + y); 
    return result_type(quot, rem); 
} 

Trong C99 và C++ 11, bạn có thể tránh sử dụng div kể từ khi hành vi của bộ phận và mô đun trong C không còn phụ thuộc vào việc thực hiện.

0

Có một giải pháp cho câu hỏi này ngắn hơn nhiều (trong mã) so với những câu đã được trình bày. Tôi sẽ sử dụng định dạng của câu trả lời của Ville Laurikari cho tôi:

int py_div(int a, int b) 
{ 
    return (a - (((a % b) + b) % b))/b); 
} 

int py_mod(int a, int b) 
{ 
    return ((a % b) + b) % b; 
} 

Thật không may, có vẻ như các giải pháp trên không hoạt động tốt. Khi đánh giá giải pháp này so với giải pháp của Ville Laurikari, nó trở nên rõ ràng rằng giải pháp này chỉ thực hiện một nửa nhanh.

Bài học là: Trong khi các hướng dẫn phân nhánh làm cho mã chậm, hướng dẫn phân chia kém hơn nhiều!

Tôi nghĩ rằng tôi vẫn đăng giải pháp này nếu chỉ cho sự thanh lịch của nó.

0

Câu hỏi được hỏi về cách mô phỏng phân chia số nguyên và modulo kiểu Python. Tất cả các câu trả lời được đưa ra ở đây cho rằng các toán hạng của hoạt động này là các số nguyên, nhưng Python cũng có thể sử dụng các phao cho hoạt động modulo của nó. Vì vậy, tôi nghĩ câu trả lời sau đây giải quyết vấn đề tốt hơn:

#include <stdlib.h> 
#include <stdio.h> 
#include <math.h> 

int pydiv(double a, double b) { 
    int q = a/b; 
    double r = fmod(a,b); 
    if ((r != 0) && ((r < 0) != (b < 0))) { 
     q -= 1; 
    } 
    return q; 
} 

int main(int argc, char* argv[]) 
{ 
    double a = atof(argv[1]); 
    double b = atof(argv[2]); 
    printf("%d\n", pydiv(a, b)); 
} 

Và đối với modulo:

#include <stdlib.h> 
#include <stdio.h> 
#include <math.h> 

double pymod(double a, double b) { 
    double r = fmod(a, b); 
    if (r!=0 && ((r<0) != (b<0))) { 
     r += b; 
    } 
    return r; 
} 

int main(int argc, char* argv[]) 
{ 
    double a = atof(argv[1]); 
    double b = atof(argv[2]); 
    printf("%f\n", pymod(a, b)); 
} 

Tôi đã thử nghiệm hai chương trình trên với cách Python cư xử bằng cách sử dụng mã kiểm tra sau:

#!/usr/bin/python3 
import subprocess 
subprocess.call(["cc", "pydiv.c", "-lm", "-o", "cdiv"]) 
subprocess.call(["cc", "pymod.c", "-lm", "-o", "cmod"]) 
def frange(start, stop, step=1): 
    for i in range(0, int((stop-start)/step)): 
     yield start + step*i 
for a in frange(-10.0, 10.0, 0.25): 
    for b in frange(-10.0, 10.0, 0.25): 
     if (b == 0.0): 
      continue 
     pydiv = a//b 
     pymod = a%b 
     cdiv = int(subprocess.check_output(["./cdiv", str(a), str(b)])) 
     cmod = float(subprocess.check_output(["./cmod", str(a), str(b)])) 
     if pydiv != cdiv: 
      exit(1) 
     if pymod != cmod: 
      exit(1) 

Ở trên sẽ so sánh hành vi của phân chia Python và modulo với triển khai C tôi đã trình bày trên 6320 testcases. Kể từ khi so sánh thành công, Tôi tin rằng giải pháp của tôi thực hiện chính xác hành vi của Python đối với các hoạt động tương ứng của .

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