Salah satu hewan peliharaan saya yang membenci bahasa turunan C (sebagai ahli matematika) adalah itu
(-1) % 8 // comes out as -1, and not 7
fmodf(-1,8) // fails similarly
Apa solusi terbaiknya?
C ++ memungkinkan kemungkinan templat dan operator kelebihan beban, tetapi keduanya adalah air keruh bagi saya. contoh diterima dengan rasa syukur.
%
dikatakan sebagai modulo ... itu sisanya .%
masalah ini .(-1) & 8 == 7
Jawaban:
Pertama-tama saya ingin mencatat bahwa Anda bahkan tidak dapat mengandalkan fakta itu
(-1) % 8 == -1
. satu-satunya hal yang dapat Anda andalkan adalah itu(x / y) * y + ( x % y) == x
. Namun apakah sisanya negatif atau tidak adalah definisi implementasi .Sekarang mengapa menggunakan template di sini? Overload untuk int dan long akan dilakukan.
int mod (int a, int b) { int ret = a % b; if(ret < 0) ret+=b; return ret; }
dan sekarang Anda dapat menyebutnya seperti mod (-1,8) dan akan tampak seperti 7.
Edit: Saya menemukan bug dalam kode saya. Ini tidak akan berhasil jika b negatif. Jadi saya pikir ini lebih baik:
int mod (int a, int b) { if(b < 0) //you can check for b == 0 separately and do what you want return -mod(-a, -b); int ret = a % b; if(ret < 0) ret+=b; return ret; }
Referensi: C ++ 03 paragraf 5.6 klausul 4:
sumber
INT_MIN / -1
(pada implementasi komplemen dua). Di bawah spesifikasi lama,-32768 % -1
mungkin harus mengevaluasi ke-65536
(yang juga tidak dalam kisaran tipe 16-bit, yuck!) Agar identitasnya dipegang.Berikut adalah fungsi C yang menangani bilangan bulat positif ATAU negatif ATAU nilai pecahan untuk KEDUA OPERAND
#include <math.h> float mod(float a, float N) {return a - N*floor(a/N);} //return in range [0, N)
Ini pasti solusi paling elegan dari sudut pandang matematika. Namun, saya tidak yakin apakah itu kuat dalam menangani bilangan bulat. Terkadang kesalahan floating point merayap saat mengonversi int -> fp -> int.
Saya menggunakan kode ini untuk non-int s, dan fungsi terpisah untuk int.
CATATAN: perlu menjebak N = 0!
Kode penguji:
#include <math.h> #include <stdio.h> float mod(float a, float N) { float ret = a - N * floor (a / N); printf("%f.1 mod %f.1 = %f.1 \n", a, N, ret); return ret; } int main (char* argc, char** argv) { printf ("fmodf(-10.2, 2.0) = %f.1 == FAIL! \n\n", fmodf(-10.2, 2.0)); float x; x = mod(10.2f, 2.0f); x = mod(10.2f, -2.0f); x = mod(-10.2f, 2.0f); x = mod(-10.2f, -2.0f); return 0; }
(Catatan: Anda dapat mengkompilasi dan menjalankannya langsung dari CodePad: http://codepad.org/UOgEqAMA )
Keluaran:
sumber
floor()
. Selain itu, Anda mungkin kehilangan ketepatan saat mengonversi ke float: Coba(float)1000000001/3
, Anda akan terkejut dengan hasilnya!Saya baru saja memperhatikan bahwa label Bjarne Stroustrup
%
sebagai operator sisa , bukan operator modulo.Saya berani bertaruh bahwa ini adalah nama resminya dalam spesifikasi ANSI C & C ++, dan penyalahgunaan terminologi telah merayap masuk. Adakah yang mengetahui fakta ini?
Tetapi jika ini masalahnya maka fungsi fmodf () C (dan mungkin yang lain) sangat menyesatkan. mereka harus diberi label fremf (), dll
sumber
%
).Fungsi umum paling sederhana untuk mencari modulo positif adalah sebagai berikut- Ini akan bekerja pada nilai x positif dan negatif.
int modulo(int x,int N){ return (x % N + N) %N; }
sumber
Untuk bilangan bulat, ini sederhana. Kerjakan saja
(((x < 0) ? ((x % N) + N) : x) % N)
di mana saya mengandaikan itu
N
positif dan terwakili dalam jenisx
. Kompiler favorit Anda harus bisa mengoptimalkannya, sehingga hanya berakhir dalam satu operasi mod di assembler.sumber
int x=-9001; unsigned int N=2000;
itu memberikan 2295, bukan 999.(x < 0) ? (x % N + N) : (x % N)
.Solusi terbaik ¹untuk ahli matematika adalah menggunakan Python.
C ++ operator overloading tidak ada hubungannya dengan itu. Anda tidak dapat membebani operator untuk tipe bawaan. Yang Anda inginkan hanyalah sebuah fungsi. Tentu saja Anda dapat menggunakan template C ++ untuk mengimplementasikan fungsi itu untuk semua jenis yang relevan hanya dengan 1 bagian kode.
Pustaka C standar menyediakan
fmod
, jika saya mengingat namanya dengan benar, untuk tipe titik mengambang.Untuk integer, Anda dapat menentukan template fungsi C ++ yang selalu mengembalikan sisa non-negatif (sesuai dengan divisi Euclidian) sebagai ...
#include <stdlib.h> // abs template< class Integer > auto mod( Integer a, Integer b ) -> Integer { Integer const r = a%b; return (r < 0? r + abs( b ) : r); }
... dan tulis saja,
mod(a, b)
bukana%b
.Di sini tipe
Integer
harus tipe integer yang ditandatangani.Jika Anda menginginkan perilaku matematika umum di mana tanda sisa sama dengan tanda pembagi, maka Anda dapat melakukannya misalnya
template< class Integer > auto floor_div( Integer const a, Integer const b ) -> Integer { bool const a_is_negative = (a < 0); bool const b_is_negative = (b < 0); bool const change_sign = (a_is_negative != b_is_negative); Integer const abs_b = abs( b ); Integer const abs_a_plus = abs( a ) + (change_sign? abs_b - 1 : 0); Integer const quot = abs_a_plus / abs_b; return (change_sign? -quot : quot); } template< class Integer > auto floor_mod( Integer const a, Integer const b ) -> Integer { return a - b*floor_div( a, b ); }
… Dengan batasan yang sama
Integer
, bahwa itu adalah tipe yang ditandatangani.¹ Karena pembagian integer Python membulatkan ke arah negatif tak hingga.
sumber
r
hasil harus makea
=r + b*(a/b)
true. tidak peduli bagaimana pembagian integer diimplementasikanb*something
adalah kelipatanb
. ini membuatr
hasil modulo valid meskipun negatif. Anda dapat menambahkan abs (b
) ke dalamnya dan itu akan tetap menjadi hasil modulo yang valid.Oh, aku juga benci% mendesain untuk ini ....
Anda dapat mengubah dividen menjadi unsigned dengan cara seperti:
unsigned int offset = (-INT_MIN) - (-INT_MIN)%divider result = (offset + dividend) % divider
di mana offset paling dekat dengan (-INT_MIN) kelipatan modul, jadi menambahkan dan menguranginya tidak akan mengubah modulo. Perhatikan bahwa itu memiliki tipe unsigned dan hasilnya akan berupa integer. Sayangnya itu tidak dapat dengan benar mengubah nilai INT_MIN ... (- offset-1) karena menyebabkan arifmetic overflow. Tetapi metode ini memiliki keunggulan hanya aritmatika tambahan tunggal per operasi (dan tidak ada persyaratan) saat bekerja dengan pembagi konstan, sehingga dapat digunakan dalam aplikasi seperti DSP.
Ada kasus khusus, di mana pembagi adalah 2 N (bilangan bulat pangkat dua), di mana modulo dapat dihitung menggunakan aritmatika sederhana dan logika bitwise sebagai
dividend&(divider-1)
sebagai contoh
x mod 2 = x & 1 x mod 4 = x & 3 x mod 8 = x & 7 x mod 16 = x & 15
Cara yang lebih umum dan tidak terlalu rumit adalah dengan mendapatkan modulo menggunakan fungsi ini (hanya berfungsi dengan pembagi positif):
int mod(int x, int y) { int r = x%y; return r<0?r+y:r; }
Ini hanya hasil yang benar jika negatif.
Anda juga bisa menipu:
(p% q + q)% q
Ini sangat singkat tetapi gunakan dua% -s yang biasanya lambat.
sumber
Saya percaya solusi lain untuk masalah ini akan digunakan untuk variabel tipe long, bukan int.
Saya baru saja mengerjakan beberapa kode di mana% operator mengembalikan nilai negatif yang menyebabkan beberapa masalah (untuk menghasilkan variabel acak seragam pada [0,1] Anda tidak benar-benar menginginkan angka negatif :)), tetapi setelah mengalihkan variabel ke ketik panjang, semuanya berjalan lancar dan hasilnya cocok dengan yang saya dapatkan saat menjalankan kode yang sama dengan python (penting bagi saya karena saya ingin dapat menghasilkan angka "acak" yang sama di beberapa platform.
sumber
Berikut adalah jawaban baru untuk pertanyaan lama, berdasarkan makalah Riset Microsoft ini dan referensi di dalamnya.
Perhatikan bahwa dari C11 dan C ++ 11 dan seterusnya, semantik
div
telah dipotong ke arah nol (lihat[expr.mul]/4
). Selanjutnya, untukD
dibagi dengand
, C ++ 11 menjamin hal-hal berikut tentang hasil bagiqT
dan sisarT
auto const qT = D / d; auto const rT = D % d; assert(D == d * qT + rT); assert(abs(rT) < abs(d)); assert(signum(rT) == signum(D));
di mana
signum
dipetakan ke -1, 0, +1, tergantung pada apakah argumennya <, ==,> dari 0 (lihat Tanya Jawab ini untuk kode sumber).Dengan pembagian terpotong, tanda sisa sama dengan tanda pembagian
D
, yaitu-1 % 8 == -1
. C ++ 11 juga menyediakanstd::div
fungsi yang mengembalikan struct dengan anggotaquot
danrem
menurut pembagian terpotong.Ada definisi lain yang mungkin, misalnya yang disebut divisi berlantai dapat didefinisikan dalam istilah divisi terpotong bawaan
auto const I = signum(rT) == -signum(d) ? 1 : 0; auto const qF = qT - I; auto const rF = rT + I * d; assert(D == d * qF + rF); assert(abs(rF) < abs(d)); assert(signum(rF) == signum(d));
Dengan pembagian berlantai, tanda sisa sama dengan tanda pembagi
d
. Dalam bahasa seperti Haskell dan Oberon, terdapat operator builtin untuk divisi lantai. Di C ++, Anda perlu menulis fungsi menggunakan definisi di atas.Namun cara lain adalah divisi Euclidean , yang juga dapat didefinisikan dalam istilah divisi terpotong bawaan
auto const I = rT >= 0 ? 0 : (d > 0 ? 1 : -1); auto const qE = qT - I; auto const rE = rT + I * d; assert(D == d * qE + rE); assert(abs(rE) < abs(d)); assert(signum(rE) != -1);
Dengan pembagian Euclidean, tanda sisanya selalu positif .
sumber
Untuk solusi yang tidak menggunakan cabang dan hanya 1 mod, Anda dapat melakukan hal berikut
// Works for other sizes too, // assuming you change 63 to the appropriate value int64_t mod(int64_t x, int64_t div) { return (x % div) + (((x >> 63) ^ (div >> 63)) & div); }
sumber
... atau biasakan mendapatkan perwakilan untuk kelas kesetaraan.
sumber
r
. The%
Operator tidak ada hubungannya dengan kelas kesetaraan. Ini adalah operator sisa dan sisanya didefinisikan dengan baik secara aljabar menjadi nonnegatif dan kurang dari pembagi. Sayangnya C mendefinisikannya dengan cara yang salah. Tetap saja, +1 karena memiliki salah satu jawaban terbaik.Contoh template untuk C ++
template< class T > T mod( T a, T b ) { T const r = a%b; return ((r!=0)&&((r^b)<0) ? r + b : r); }
Dengan templat ini, sisa yang dikembalikan akan menjadi nol atau memiliki tanda yang sama dengan penyebut (penyebut) (ekuivalen dengan pembulatan menuju tak terhingga negatif), alih-alih perilaku C ++ dari sisanya menjadi nol atau memiliki tanda yang sama dengan pembagi ( pembilang) (setara dengan pembulatan menuju nol).
sumber
define MOD(a, b) ((((a)%(b))+(b))%(b))
sumber
unsigned mod(int a, unsigned b) { return (a >= 0 ? a % b : b - (-a) % b); }
sumber
Solusi ini (untuk digunakan ketika
mod
positif) menghindari pembagian negatif atau operasi sisa semuanya bersama-sama:int core_modulus(int val, int mod) { if(val>=0) return val % mod; else return val + mod * ((mod - val - 1)/mod); }
sumber
Saya akan melakukan:
((-1)+8) % 8
Ini menambahkan angka terakhir ke yang pertama sebelum melakukan modulo memberikan 7 seperti yang diinginkan. Ini harus bekerja untuk nomor apa pun hingga -8. Untuk -9 tambahkan 2 * 8.
sumber
-99999
?