Kode Anda baik-baik saja
Anda benar sekali dan guru Anda salah. Sama sekali tidak ada alasan sama sekali untuk menambahkan kompleksitas ekstra, karena itu tidak mempengaruhi hasilnya sama sekali. Bahkan memperkenalkan bug. (Lihat di bawah)
Pertama, pemeriksaan terpisah apakah nnol jelas sama sekali tidak perlu dan ini sangat mudah untuk diwujudkan. Sejujurnya, saya benar-benar mempertanyakan kompetensi guru Anda jika dia keberatan dengan hal ini. Tetapi saya kira setiap orang dapat memiliki otak yang kentut dari waktu ke waktu. Namun, saya TIDAK berpikir itu while(n)harus diubah while(n != 0)karena menambah sedikit kejelasan ekstra bahkan tanpa biaya garis tambahan. Ini hal kecil.
Yang kedua sedikit lebih bisa dimengerti, tetapi dia masih salah.
Inilah yang dikatakan standar C11 6.5.5.p6 :
Jika hasil bagi a / b dapat diwakili, ungkapan (a / b) * b + a% b harus sama dengan a; jika tidak, perilaku a / b dan% b tidak ditentukan.
Catatan kaki mengatakan ini:
Ini sering disebut "pemotongan ke nol".
Pemotongan ke nol berarti bahwa nilai absolut untuk a/bsama dengan nilai absolut (-a)/buntuk semua adan b, yang pada gilirannya berarti bahwa kode Anda baik-baik saja.
Modulo adalah matematika yang mudah, tetapi mungkin berlawanan dengan intuisi
Namun, guru Anda memang memiliki poin bahwa Anda harus berhati-hati, karena fakta bahwa Anda mengkuadratkan hasilnya sebenarnya sangat penting di sini. Menghitung a%bmenurut definisi di atas adalah matematika mudah, tetapi itu mungkin bertentangan dengan intuisi Anda. Untuk perkalian dan pembagian, hasilnya positif jika operan memiliki tanda yang sama. Tetapi ketika datang ke modulo, hasilnya memiliki tanda yang sama dengan operan pertama . Operan kedua tidak mempengaruhi tanda sama sekali. Misalnya, 7%3==1tapi (-7)%(-3)==(-1).
Ini cuplikan yang menunjukkan:
$ cat > main.c
#include <stdio.h>
void f(int a, int b)
{
printf("a: %2d b: %2d a/b: %2d a\%b: %2d (a%b)^2: %2d (a/b)*b+a%b==a: %5s\n",
a, b ,a/b, a%b, (a%b)*(a%b), (a/b)*b+a%b == a ? "true" : "false");
}
int main(void)
{
int a=7, b=3;
f(a,b);
f(-a,b);
f(a,-b);
f(-a,-b);
}
$ gcc main.c -Wall -Wextra -pedantic -std=c99
$ ./a.out
a: 7 b: 3 a/b: 2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: 3 a/b: -2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: 7 b: -3 a/b: -2 a%b: 1 (a%b)^2: 1 (a/b)*b+a%b==a: true
a: -7 b: -3 a/b: 2 a%b: -1 (a%b)^2: 1 (a/b)*b+a%b==a: true
Jadi, ironisnya, gurumu membuktikan pendapatnya dengan salah.
Kode guru Anda cacat
Ya, sebenarnya. Jika inputnya INT_MINDAN arsitekturnya adalah pelengkap dua DAN pola bit di mana bit tanda adalah 1 dan semua nilai bit adalah 0 BUKAN nilai jebakan (menggunakan pelengkap dua tanpa nilai jebakan sangat umum) maka kode guru Anda akan menghasilkan perilaku yang tidak terdefinisi di telepon n = n * (-1). Kode Anda - jika sedikit - lebih baik dari miliknya. Dan mempertimbangkan memperkenalkan bug kecil dengan membuat kode tidak perlu rumit dan mendapatkan nilai nol, saya akan mengatakan bahwa kode Anda JAUH lebih baik.
Dengan kata lain, dalam kompilasi di mana INT_MIN = -32768 (meskipun fungsi yang dihasilkan tidak dapat menerima input yang <-32768 atau> 32767), input yang valid dari -32768 menyebabkan perilaku tidak terdefinisi, karena hasil dari - (- 32768i16) tidak dapat dinyatakan sebagai bilangan bulat 16-bit. (Sebenarnya, -32768 mungkin tidak akan menyebabkan hasil yang salah, karena - (- 32768i16) biasanya mengevaluasi ke -32768i16, dan program Anda menangani angka negatif dengan benar.) (SHRT_MIN bisa -32768 atau -32767, tergantung pada kompilernya)
Tetapi guru Anda secara eksplisit menyatakan bahwa nbisa berada dalam kisaran [-10 ^ 7; 10 ^ 7]. Bilangan bulat 16-bit terlalu kecil; Anda harus menggunakan [setidaknya] integer 32-bit. Penggunaannya intmungkin membuat kode-nya aman, kecuali itu intbelum tentu integer 32-bit. Jika Anda mengkompilasi untuk arsitektur 16-bit, kedua cuplikan kode Anda cacat. Tetapi kode Anda masih jauh lebih baik karena skenario ini memperkenalkan kembali bug dengan yang INT_MINdisebutkan di atas dengan versinya. Untuk menghindari ini, Anda bisa menulis longalih-alih int, yang merupakan bilangan bulat 32-bit pada kedua arsitektur. A longdijamin dapat memiliki nilai apa pun dalam kisaran [-2147483647; 2147483647]. C11 Standar 5.2.4.2.1 LONG_MIN sering-2147483648tetapi nilai maksimum yang diizinkan (ya, maksimum, ini adalah angka negatif) LONG_MINadalah 2147483647.
Perubahan apa yang akan saya lakukan pada kode Anda?
Kode Anda baik-baik saja, jadi ini bukan keluhan. Lebih seperti itu jika saya benar-benar perlu mengatakan apa pun tentang kode Anda, ada beberapa hal kecil yang dapat membuatnya sedikit lebih jelas.
- Nama-nama variabel bisa sedikit lebih baik, tetapi itu adalah fungsi pendek yang mudah dimengerti, jadi itu bukan masalah besar.
- Anda dapat mengubah kondisi dari
nmenjadi n!=0. Secara semantik, ini setara 100%, tetapi membuatnya sedikit lebih jelas.
- Pindahkan deklarasi
c(yang saya ganti namanya digit) ke dalam loop sementara karena itu hanya digunakan di sana.
- Ubah tipe argumen
longuntuk memastikannya dapat menangani seluruh rangkaian input.
int sum_of_digits_squared(long n)
{
long sum = 0;
while (n != 0) {
int digit = n % 10;
sum += (digit * digit);
n /= 10;
}
return sum;
}
Sebenarnya, ini bisa sedikit menyesatkan karena - seperti yang disebutkan di atas - variabel digitbisa mendapatkan nilai negatif, tetapi digit itu sendiri tidak pernah positif atau negatif. Ada beberapa cara untuk mengatasi hal ini, tetapi ini BENAR-BENAR membingungkan, dan saya tidak akan peduli dengan detail sekecil itu. Terutama fungsi terpisah untuk digit terakhir terlalu jauh. Ironisnya, ini adalah salah satu hal yang dipecahkan oleh kode guru Anda.
- Ubah
sum += (digit * digit)ke sum += ((n%10)*(n%10))dan lewati variabel digitsepenuhnya.
- Ubah tanda
digitjika negatif. Tapi saya akan sangat menyarankan agar kode tidak lebih kompleks hanya untuk membuat nama variabel masuk akal. Itu bau kode yang SANGAT kuat.
- Buat fungsi terpisah yang mengekstrak digit terakhir.
int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }Ini berguna jika Anda ingin menggunakan fungsi itu di tempat lain.
- Beri nama saja
cseperti yang Anda lakukan semula. Nama variabel itu tidak memberikan informasi yang berguna, tetapi di sisi lain, itu juga tidak menyesatkan.
Tetapi jujur saja, pada titik ini Anda harus beralih ke pekerjaan yang lebih penting. :)
n = n * (-1)adalah cara konyol untuk menulisn = -n; Hanya seorang akademisi yang akan memikirkannya. Apalagi menambahkan tanda kurung yang berlebihan.