tôi muốn để xem nếu GCC sẽ làm giảm a - (b - c)
-(a + c) - b
với số nguyên có chữ ký và unsigned vì vậy tôi tạo ra hai bài kiểm trađại số giảm các biểu thức số nguyên ký trong C/C++
//test1.c
unsigned fooau(unsigned a, unsigned b, unsigned c) { return a - (b - c); }
signed fooas(signed a, signed b, signed c) { return a - (b - c); }
signed fooms(signed a) { return a*a*a*a*a*a; }
unsigned foomu(unsigned a) { return a*a*a*a*a*a; }
//test2.c
unsigned fooau(unsigned a, unsigned b, unsigned c) { return (a + c) - b; }
signed fooas(signed a, signed b, signed c) { return (a + c) - b; }
signed fooms(signed a) { return (a*a*a)*(a*a*a); }
unsigned foomu(unsigned a) { return (a*a*a)*(a*a*a); }
tôi biên soạn đầu tiên với gcc -O3 test1.c test2.c -S
và nhìn vào hội,, tổ hợp. Đối với cả hai thử nghiệm fooau
giống hệt nhau tuy nhiên fooas
thì không.
Theo như tôi hiểu số học unsigned thể được bắt nguồn từ the following formula
(a%n + b%n)%n = (a+b)%n
mà có thể được sử dụng để chứng minh rằng số học unsigned là liên kết. Nhưng kể từ khi signed overflow is undefined behavior sự bình đẳng này không nhất thiết giữ cho việc bổ sung có chữ ký (tức là bổ sung đã ký không phải là kết hợp), giải thích tại sao GCC không giảm a - (b - c)
thành (a + c) - b
đối với các số nguyên đã ký. Nhưng chúng tôi có thể yêu cầu GCC sử dụng công thức này bằng cách sử dụng -fwrapv
. Sử dụng tùy chọn này fooas
cho cả hai bài kiểm tra là giống hệt nhau.
Nhưng còn về phép nhân thì sao? Đối với cả hai thử nghiệm fooms
và foomu
được đơn giản hóa thành ba phép nhân (a*a*a*a*a*a to (a*a*a)*(a*a*a)
). Nhưng nhân có thể được viết như sau Ngoài ra lặp đi lặp lại nên sử dụng công thức trên, chúng tôi nghĩ rằng nó có thể được hiển thị mà
((a%n)*(b%n))%n = (a*b)%n
mà tôi nghĩ cũng có thể cho thấy rằng unsigned nhân mô-đun là kết hợp là tốt. Nhưng kể từ khi GCC chỉ sử dụng ba phép nhân cho foomu
điều này cho thấy rằng GCC giả định ký số nguyên nhân là kết hợp.
Điều này có vẻ như mâu thuẫn với tôi. Đối với số học đã ký không phải là kết hợp nhưng đối với phép nhân thì nó là.
Hai câu hỏi:
Có đúng là bổ sung không được kết hợp với số nguyên ký nhưng nhân là trong C/C++?
Nếu tràn đăng nhập được sử dụng để tối ưu hóa không phải là thực tế rằng GCC không làm giảm biểu thức đại số thất bại trong việc tối ưu hóa? Sẽ tốt hơn nếu tối ưu hóa để sử dụng
-fwrapv
(Tôi hiểu rằnga - (b - c)
đến(a + c) - b
không giảm nhiều nhưng tôi lo lắng về các trường hợp phức tạp hơn)? Điều này có nghĩa là tối ưu hóa đôi khi sử dụng-fwrapv
có hiệu quả hơn và đôi khi không?
Điều gì sẽ xảy ra với 'fooms' và' foomu' nếu bạn làm cho cơ thể 'a * a * a * a * a' - nghĩa là. một số ** số nhân ** lẻ? Họ vẫn tối ưu hóa giống nhau không? Với số chẵn, ký hiệu không liên quan vì kết quả sẽ luôn dương. – kdopen
@kdopen, nó giống nhau: 'fooms' và' foomu' tạo cùng một mã và sử dụng 3 phép nhân cho 'a * a * a * a * a'. –