Saya selalu berasumsi bahwa ketika melakukan (a % 256)
pengoptimal akan secara alami menggunakan operasi bitwise yang efisien, seolah-olah saya menulis (a & 0xFF)
.
Saat menguji pada compiler explorer gcc-6.2 (-O3):
// Type your code here, or load an example.
int mod(int num) {
return num % 256;
}
mod(int):
mov edx, edi
sar edx, 31
shr edx, 24
lea eax, [rdi+rdx]
movzx eax, al
sub eax, edx
ret
Dan ketika mencoba kode lainnya:
// Type your code here, or load an example.
int mod(int num) {
return num & 0xFF;
}
mod(int):
movzx eax, dil
ret
Sepertinya saya benar-benar kehilangan sesuatu. Ada ide?
c++
optimization
Elad Weiss
sumber
sumber
%
juga tidak&
.num
adalahunsigned
?Jawaban:
Ini tidak sama. Coba
num = -79
, dan Anda akan mendapatkan hasil yang berbeda dari kedua operasi.(-79) % 256 = -79
, sedangkan(-79) & 0xff
beberapa angka positif.Dengan menggunakan
unsigned int
, operasinya sama, dan kode kemungkinan akan sama.PS- Seseorang berkomentar
Itu bukan bagaimana itu didefinisikan dalam C, C ++, Objective-C (yaitu semua bahasa di mana kode dalam pertanyaan akan dikompilasi).
sumber
Jawaban singkat
-1 % 256
hasil-1
dan bukan255
yang mana-1 & 0xFF
. Karena itu, optimasi akan salah.Jawaban panjang
C ++ memiliki konvensi itu
(a/b)*b + a%b == a
, yang tampaknya cukup alami.a/b
selalu mengembalikan hasil aritmatika tanpa bagian fraksional (memotong menuju 0). Akibatnya,a%b
memiliki tanda yang sama dengana
atau adalah 0.Divisi
-1/256
menghasilkan0
dan karenanya-1%256
harus-1
memenuhi persyaratan di atas ((-1%256)*256 + -1%256 == -1
). Ini jelas berbeda dari-1&0xFF
yang mana0xFF
. Oleh karena itu, kompiler tidak dapat mengoptimalkan seperti yang Anda inginkan.Bagian yang relevan dalam standar C ++ [expr.mul §4] pada N4606 menyatakan:
Mengaktifkan pengoptimalan
Namun, menggunakan
unsigned
tipe, optimisasi akan sepenuhnya benar , memenuhi konvensi di atas:Lihat juga ini .
Bahasa lainnya
Ini ditangani sangat berbeda di berbagai bahasa pemrograman karena Anda dapat melihat di Wikipedia .
sumber
Sejak C ++ 11,
num % 256
harus non-positif jikanum
negatif.Jadi pola bit akan tergantung pada implementasi tipe yang ditandatangani pada sistem Anda: untuk argumen pertama yang negatif, hasilnya bukan ekstraksi dari 8 bit yang paling tidak signifikan.
Ini akan menjadi masalah yang berbeda jika
num
dalam kasus Anda adalahunsigned
: hari ini saya hampir berharap kompiler membuat optimasi yang Anda kutip.sumber
num
negatif, makanum % 256
nol atau negatif (alias tidak positif).(-250+256)%256==6
, tetapi(-250%256)+(256%256)
harus, menurut standar, "non-positif", dan karenanya tidak6
. Melanggar asosiatif seperti itu memiliki efek samping kehidupan nyata: misalnya saat menghitung "perkecil" yang menghasilkan koordinat bilangan bulat, seseorang harus menggeser gambar agar semua koordinat non-negatif.(128+128)%256==0
tetapi(128%256)+(128%256)==256
. Mungkin ada keberatan yang baik terhadap perilaku yang ditentukan, tetapi tidak jelas bagi saya bahwa itu yang Anda katakan.256==0
. Kuncinya adalah memilikiN
nilai yang mungkin tepat dalam moduloN
aritmatika, yang hanya mungkin jika semua hasilnya berada dalam kisaran0,...,(N-1)
, bukan-(N-1),...,(N-1)
.Saya tidak memiliki wawasan telepati ke dalam alasan kompiler, tetapi dalam kasus
%
ada perlunya berurusan dengan nilai-nilai negatif (dan putaran pembagian ke nol), sementara dengan&
hasilnya selalu 8 bit yang lebih rendah.The
sar
instruksi suara untuk saya seperti "bergeser ke kanan aritmatika", mengisi bit dikosongkan dengan nilai tanda bit.sumber
Secara matematis, modulo didefinisikan sebagai berikut:
a% b = a - b * lantai (a / b)
Ini di sini harus menjelaskannya untuk Anda. Kita dapat menghilangkan lantai untuk bilangan bulat karena pembagian bilangan bulat setara dengan lantai (a / b). Namun, jika kompiler menggunakan trik umum seperti yang Anda sarankan, itu perlu bekerja untuk semua dan semua b. Sayangnya, ini bukan masalahnya. Secara matematis, trik Anda 100% benar untuk bilangan bulat yang tidak ditandatangani (Saya melihat jawaban menyatakan bilangan bulat yang ditandatangani rusak, tetapi saya dapat mengonfirmasi atau menolak ini karena -a% b harus positif). Namun, dapatkah Anda melakukan trik ini untuk semua b? Mungkin tidak. Itu sebabnya kompiler tidak melakukannya. Lagi pula, jika modulo mudah ditulis sebagai satu operasi bitwise, maka kita hanya akan menambahkan rangkaian modulo seperti untuk penambahan dan operasi lainnya.
sumber
-2.3
adalah-3
, sedangkan jika Anda memotong-2.3
ke bilangan bulat yang Anda dapatkan-2
. Lihat en.wikipedia.org/wiki/Truncation . + msgstr "untuk pemotongan bilangan negatif tidak membulat ke arah yang sama dengan fungsi lantai". Dan perilaku%
untuk bilangan negatif justru merupakan alasan OP melihat perilaku yang dijelaskan.