ฉันจำเป็นต้องจัดการกับจำนวนลบหรือศูนย์อย่างชัดเจนหรือไม่เมื่อรวมผลรวมของตัวเลขยกกำลังสอง?


220

ฉันเพิ่งผ่านการทดสอบในชั้นเรียนของฉัน หนึ่งในปัญหาคือต่อไปนี้:

รับจำนวนnเขียนฟังก์ชั่นใน C / C ++ ที่ส่งกลับผลรวมของตัวเลขของจำนวนที่ยกกำลังสอง (ต่อไปนี้เป็นสิ่งสำคัญ) ช่วงของnเป็น [- (10 ^ 7), 10 ^ 7] ตัวอย่าง: ถ้าn = 123 ฟังก์ชันของคุณควรกลับ 14 (1 ^ 2 + 2 ^ 2 + 3 ^ 2 = 14)

นี่คือฟังก์ชั่นที่ฉันเขียน:

int sum_of_digits_squared(int n) 
{
    int s = 0, c;

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

ดูถูกต้องสำหรับฉัน ตอนนี้การทดสอบกลับมาและฉันพบว่าครูไม่ได้ให้คะแนนทั้งหมดสำหรับฉันด้วยเหตุผลที่ฉันไม่เข้าใจ ตามที่เขาพูดเพื่อให้การทำงานของฉันเสร็จสมบูรณ์ฉันควรจะเพิ่มรายละเอียดต่อไปนี้:

int sum_of_digits_squared(int n) 
 {
    int s = 0, c;

    if (n == 0) {      //
        return 0;      //
    }                  //
                       // THIS APPARENTLY SHOULD'VE 
    if (n < 0) {       // BEEN IN THE FUNCTION FOR IT
        n = n * (-1);  // TO BE CORRECT
    }                  //

    while (n) {
        c = n % 10;
        s += (c * c);
        n /= 10;
    }

    return s;
}

อาร์กิวเมนต์สำหรับสิ่งนี้คือตัวเลขnอยู่ในช่วง [- (10 ^ 7), 10 ^ 7] ดังนั้นมันอาจเป็นจำนวนลบ แต่ฉันไม่เห็นว่าฟังก์ชั่นเวอร์ชั่นของตัวเองล้มเหลว ถ้าผมเข้าใจอย่างถูกต้องตามความหมายของwhile(n)คือwhile(n != 0), ไม่ได้ while (n > 0)ดังนั้นในรุ่นของฉันฟังก์ชั่นจำนวนnจะได้ล้มเหลวที่จะเข้าสู่วง มันจะทำงานได้เหมือนกัน

จากนั้นฉันลองใช้ฟังก์ชั่นทั้งสองรุ่นในคอมพิวเตอร์ที่บ้านและได้รับคำตอบเหมือนกันทุกตัวอย่างที่ฉันลอง ดังนั้นsum_of_digits_squared(-123)เท่ากับsum_of_digits_squared(123)(ซึ่งอีกครั้งเท่ากับ14) (แม้ไม่มีรายละเอียดที่ฉันควรจะเพิ่ม) แน่นอนถ้าฉันพยายามพิมพ์ตัวเลขบนหน้าจอ (อย่างน้อยก็สำคัญที่สุด) บนหน้าจอใน123กรณีที่ฉันได้รับ3 2 1และใน-123กรณีที่ฉันได้รับ-3 -2 -1(ซึ่งน่าสนใจจริง ๆ ) แต่ในปัญหานี้มันคงไม่สำคัญหรอก

ดังนั้นใครจะผิด

แก้ไข : ไม่ดีฉันลืมระบุและไม่รู้ว่ามันสำคัญ รุ่นของ C ที่ใช้ในการเรียนและการทดสอบของเราจะต้องมีความ C99 หรือใหม่กว่า ดังนั้นฉันเดา (โดยการอ่านความคิดเห็น) ว่ารุ่นของฉันจะได้รับคำตอบที่ถูกต้อง แต่อย่างใด


120
n = n * (-1)เป็นวิธีที่ไร้สาระในการเขียนn = -n; มีเพียงนักวิชาการเท่านั้นที่นึกถึง ปล่อยให้อยู่คนเดียวเพิ่มวงเล็บที่ซ้ำซ้อน
user207421

32
เขียนชุดการทดสอบหน่วยเพื่อตรวจสอบว่าการใช้งานที่กำหนดนั้นตรงกับข้อกำหนดหรือไม่ หากมีปัญหา (การทำงาน) ของชิ้นส่วนของรหัสควรเป็นไปได้ที่จะเขียนการทดสอบที่แสดงผลลัพธ์ที่ไม่ถูกต้องเมื่อป้อนข้อมูลเฉพาะ
Carl

94
ฉันคิดว่ามันน่าสนใจที่ "ผลรวมของตัวเลขของตัวเลขกำลังสอง" สามารถตีความได้ในสาม (3) วิธีที่แตกต่างอย่างสิ้นเชิง (ถ้าจำนวน 123 การตีความที่เป็นไปได้ให้ผลลัพธ์ 18, 14 และ 36)
Andreas Rejbrand

23
@ilkkachu: "ผลรวมของตัวเลขของตัวเลขกำลังสอง" ทีนี้ "จำนวนกำลังสอง" ชัดเจน 123 ^ 2 = 15129 ดังนั้น "ผลรวมของตัวเลขของจำนวนยกกำลังสอง" คือ "ผลรวมของตัวเลขของ 15129" ซึ่งเห็นได้ชัดคือ 1 + 5 + 1 + 2 + 9 = 18
Andreas Rejbrand

15
n = n * (-1)? wut ??? สิ่งที่ศาสตราจารย์ของคุณต้องการคือ: `n = -n ' ภาษา C มีโอเปอเรเตอร์ลบพร้อมกัน
Kaz

คำตอบ:


245

สรุปการอภิปรายที่แสดงความคิดเห็น:

  • n == 0ไม่มีเหตุผลที่ดีในการทดสอบล่วงหน้าสำหรับเป็น การwhile(n)ทดสอบจะจัดการกับกรณีนั้นอย่างสมบูรณ์
  • อาจเป็นไปได้ว่าครูของคุณยังคงคุ้นเคยกับครั้งก่อนหน้านี้เมื่อผลลัพธ์ของ%ตัวถูกดำเนินการเชิงลบถูกกำหนดแตกต่างกัน ในบางระบบเก่า (รวมถึงโดยเฉพาะอย่างยิ่ง Unix ในช่วงต้นของ PDP-11 ที่ Dennis Ritchie พัฒนาขึ้นมาเป็น C) ผลลัพธ์ของa % bการทดสอบอยู่ในช่วงเสมอ[0 .. b-1]ซึ่งหมายความว่า -123% 10 คือ 7 ในระบบดังกล่าวการทดสอบ ล่วงหน้าสำหรับn < 0ความจำเป็น

แต่กระสุนนัดที่สองใช้เฉพาะกับเวลาก่อนหน้า ในเวอร์ชันปัจจุบันของทั้งมาตรฐาน C และ C ++ การหารจำนวนเต็มถูกกำหนดให้ตัดปลายเป็น 0 ดังนั้นจึงปรากฎว่าn % 10มีการรับประกันว่าจะให้ตัวเลขสุดท้าย (อาจเป็นลบ) ของnแม้ว่าnจะเป็นลบ

ดังนั้นคำตอบของคำถาม"ความหมายของwhile(n)อะไร" เป็น"เหมือนกับwhile(n != 0)"และคำตอบ"จะทำงานรหัสนี้อย่างถูกต้องสำหรับเชิงลบเช่นเดียวกับในเชิงบวกn?" คือ"ใช่ภายใต้คอมไพเลอร์ที่ได้มาตรฐานและทันสมัย" คำตอบสำหรับคำถาม"แล้วทำไมอาจารย์ผู้สอนทำเครื่องหมายลง?" อาจเป็นไปได้ว่าพวกเขาไม่ได้ตระหนักถึงการกำหนดภาษาใหม่ที่สำคัญซึ่งเกิดขึ้นกับ C ในปี 1999 และ C ++ ในปี 2010 หรือมากกว่านั้น


39
"ไม่มีเหตุผลที่ดีที่จะทดสอบล่วงหน้าสำหรับ n == 0" - ในทางเทคนิคแล้วถูกต้อง แต่เนื่องจากเรากำลังพูดถึงอาจารย์ในสภาพแวดล้อมการสอนพวกเขาอาจชื่นชมความชัดเจนมากกว่าความกะทัดรัดมากกว่าที่เราทำ การเพิ่มการทดสอบพิเศษn == 0อย่างน้อยทำให้ทันทีและชัดเจนที่สุดสำหรับผู้อ่านว่าเกิดอะไรขึ้นในกรณีนั้น หากไม่มีผู้อ่านจะต้องทำให้ตนเองพึงพอใจว่าการวนซ้ำถูกข้ามไปและค่าเริ่มต้นของการsส่งคืนเป็นค่าที่ถูกต้อง
ilkkachu

22
นอกจากนี้อาจารย์อาจต้องการทราบว่านักเรียนรู้และคิดเกี่ยวกับสาเหตุและวิธีการทำงานของฟังก์ชั่นด้วยการป้อนค่าเป็นศูนย์ (เช่นว่ามันจะไม่คืนค่าที่ถูกต้องโดยอุบัติเหตุ ) พวกเขาอาจได้พบกับนักเรียนที่ไม่ทราบว่าจะเกิดอะไรขึ้นในกรณีนั้นลูปสามารถทำงานเป็นศูนย์ได้เวลา ฯลฯ เมื่อคุณจัดการกับสภาพแวดล้อมในการสอนสิ่งที่ดีที่สุดคือต้องมีความชัดเจนเกี่ยวกับสมมติฐานและกรณีศึกษา ..
ilkkachu

36
@ilkkachu ถ้าเป็นเช่นนั้นคุณครูควรแจกงานที่ต้องการให้การทดสอบดังกล่าวทำงานได้อย่างถูกต้อง
klutt

38
@ilkkachu เอาล่ะฉันเอาประเด็นของคุณไปใช้ในกรณีทั่วไปเพราะฉันซาบซึ้งกับความกระจ่างชัดมาก - และค่อนข้างมากตลอดเวลาไม่จำเป็นต้องเป็นแค่การสอน แต่ด้วยสิ่งที่กล่าวมาบางครั้งความกะทัดรัดคือความคมชัดและถ้าคุณสามารถจัดเรียงรหัสหลักเพื่อจัดการทั้งกรณีทั่วไปและกรณีขอบโดยไม่ต้องยุ่งรหัส (และการวิเคราะห์ความครอบคลุม) กับกรณีพิเศษสำหรับกรณีขอบ นั่นคือสิ่งที่สวยงาม! ฉันคิดว่ามันเป็นสิ่งที่น่าชื่นชมแม้ในระดับเริ่มต้น
Steve Summit

49
@ilkkachu โดยเหตุผลนั้นคุณควรเพิ่มการทดสอบสำหรับ n = 1 และต่อไปเรื่อย ๆ n=0ไม่มีอะไรที่เป็นพิเศษเกี่ยวกับ การแนะนำสาขาและภาวะแทรกซ้อนที่ไม่จำเป็นนั้นไม่ได้ทำให้รหัสง่ายขึ้นมันทำให้ยากขึ้นตั้งแต่ตอนนี้คุณไม่เพียง แต่ต้องแสดงให้เห็นว่าอัลกอริทึมทั่วไปนั้นถูกต้องคุณต้องคิดถึงกรณีพิเศษทั้งหมดแยกต่างหาก
Voo

107

รหัสของคุณใช้ได้อย่างสมบูรณ์แบบ

คุณถูกต้องอย่างแน่นอนและครูของคุณผิด ไม่มีเหตุผลใด ๆ เลยที่จะเพิ่มความซับซ้อนเป็นพิเศษเนื่องจากไม่มีผลต่อผลลัพธ์เลย มันยังแนะนำข้อผิดพลาด (ดูด้านล่าง)

ก่อนอื่นการตรวจสอบแยกnเป็นศูนย์เห็นได้ชัดว่าไม่จำเป็นอย่างสมบูรณ์และนี่เป็นเรื่องง่ายที่จะตระหนัก ความจริงแล้วฉันถามความสามารถของครูถ้าเขามีข้อคัดค้านเกี่ยวกับเรื่องนี้ แต่ฉันคิดว่าทุกคนสามารถมีผายลมในสมองเป็นครั้งคราว อย่างไรก็ตามฉันคิดว่าwhile(n)ควรเปลี่ยนเป็นwhile(n != 0)เพราะมันเพิ่มความชัดเจนพิเศษเล็กน้อยโดยไม่ต้องเสียค่าใช้จ่ายเพิ่มเติม มันเป็นเรื่องเล็กน้อย

คนที่สองเข้าใจได้มากกว่านี้เล็กน้อย แต่เขาก็ยังผิดอยู่

นี่คือสิ่งที่C11 มาตรฐาน 6.5.5.p6พูดว่า:

หากความฉลาดทาง a / b แสดงออกได้การแสดงออก (a / b) * b + a% b จะเท่ากับ a; มิฉะนั้นพฤติกรรมของทั้ง a / b และ a% b นั้นไม่ได้ถูกกำหนดไว้

เชิงอรรถกล่าวว่า:

สิ่งนี้มักเรียกว่า "การตัดทอนเข้าหาศูนย์"

การตัดให้เป็นศูนย์หมายความว่าค่าสัมบูรณ์สำหรับa/bเท่ากับค่าสัมบูรณ์(-a)/bสำหรับทุกคนaและbในทางกลับกันหมายความว่ารหัสของคุณนั้นสมบูรณ์แบบ

Modulo นั้นเป็นคณิตศาสตร์ที่ง่าย แต่อาจใช้งานง่าย

อย่างไรก็ตามคุณครูของคุณมีประเด็นที่คุณควรระวังเพราะความจริงที่ว่าคุณกำลังยกกำลังสองผลลัพธ์นั้นสำคัญมากที่นี่ การคำนวณa%bตามคำนิยามข้างต้นเป็นคณิตศาสตร์ง่าย ๆ แต่มันอาจขัดกับสัญชาตญาณของคุณ สำหรับการคูณและการหารผลลัพธ์จะเป็นค่าบวกหากตัวถูกดำเนินการมีเครื่องหมายเท่ากับ แต่เมื่อพูดถึง modulo ผลลัพธ์จะมีเครื่องหมายเหมือนกับตัวถูกดำเนินการแรก ตัวถูกดำเนินการตัวที่สองไม่ส่งผลกระทบต่อสัญลักษณ์เลย ตัวอย่างเช่น7%3==1แต่(-7)%(-3)==(-1)แต่

นี่เป็นตัวอย่างที่แสดงให้เห็น:

$ 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

ดังนั้นแดกดันครูของคุณพิสูจน์จุดของเขาโดยผิด

รหัสครูของคุณมีข้อบกพร่อง

ใช่มันเป็นจริง ถ้าอินพุตคือINT_MINAND สถาปัตยกรรมคือส่วนเติมเต็มสองและรูปแบบบิตโดยที่เครื่องหมายบิตคือ 1 และค่าบิตทั้งหมดเป็น 0 ไม่ใช่ค่าแทร็บn = n * (-1)บนเส้น รหัสของคุณคือ - ถ้าดีกว่าเขาเล็กน้อย และเมื่อพิจารณาถึงการแนะนำบั๊กเล็ก ๆ โดยการทำให้โค้ดไม่จำเป็นต้องมีความซับซ้อนและมีค่าเป็นศูนย์อย่างแน่นอนฉันจะบอกว่าโค้ดของคุณนั้นดีกว่ามาก

ในคำอื่น ๆ ในการรวบรวมที่ INT_MIN = -32768 (แม้ว่าฟังก์ชั่นส่งผลให้ไม่สามารถรับการป้อนข้อมูลที่เป็น <-32768 หรือ> 32767) ที่ถูกต้องอินพุตที่ของ -32768 ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดเนื่องจากผลลัพธ์ของ - (- 32768i16) ไม่สามารถแสดงเป็นจำนวนเต็ม 16 บิต (ที่จริงแล้ว -32768 อาจจะไม่ก่อให้เกิดผลลัพธ์ที่ไม่ถูกต้องเพราะ - (- - 32768i16) มักจะประเมินที่ -32768i16 และโปรแกรมของคุณจะจัดการกับจำนวนลบอย่างถูกต้อง) (SHRT_MIN อาจเป็น -32768 หรือ -32767 ขึ้นอยู่กับคอมไพเลอร์)

แต่ครูของคุณระบุไว้อย่างชัดเจนว่าnสามารถอยู่ในช่วง [-10 ^ 7; 10 ^ 7] จำนวนเต็ม 16 บิตน้อยเกินไป คุณจะต้องใช้ [อย่างน้อย] จำนวนเต็ม 32 บิต การใช้intอาจทำให้โค้ดของเขาปลอดภัยยกเว้นว่าintไม่จำเป็นต้องเป็นจำนวนเต็ม 32 บิต ถ้าคุณคอมไพล์สำหรับสถาปัตยกรรมแบบ 16 บิตข้อมูลโค้ดทั้งคู่ของคุณจะมีข้อบกพร่อง แต่รหัสของคุณยังดีกว่ามากเพราะสถานการณ์นี้แนะนำข้อผิดพลาดที่INT_MINกล่าวถึงข้างต้นกับรุ่นของเขาอีกครั้ง เพื่อหลีกเลี่ยงปัญหานี้คุณสามารถเขียนlongแทนintซึ่งเป็นจำนวนเต็ม 32 บิตในสถาปัตยกรรมใดสถาปัตยกรรมหนึ่ง A longรับประกันว่าจะสามารถเก็บค่าใด ๆ ในช่วง [-2147483647; 2147483647] C11 มาตรฐาน 5.2.4.2.1 LONG_MINมักจะเป็น-2147483648แต่ค่าสูงสุดที่อนุญาต (ใช่สูงสุดเป็นจำนวนลบ)LONG_MIN2147483647เป็น

ฉันจะเปลี่ยนแปลงรหัสของคุณอย่างไร

รหัสของคุณก็ดีเหมือนเดิมดังนั้นสิ่งเหล่านี้จึงไม่เป็นเรื่องร้องเรียน มันเป็นอย่างนั้นถ้าฉันจริง ๆ ต้องพูดอะไรเกี่ยวกับรหัสของคุณมีบางสิ่งเล็ก ๆ ที่สามารถทำให้ชัดเจนยิ่งขึ้น

  • ชื่อของตัวแปรอาจจะดีขึ้นเล็กน้อย แต่มันเป็นฟังก์ชั่นสั้น ๆ ที่เข้าใจง่ายดังนั้นจึงไม่ใช่เรื่องใหญ่
  • คุณสามารถเปลี่ยนสภาพจากการn n!=0ความหมายมันเทียบเท่า 100% แต่ทำให้ชัดเจนขึ้นเล็กน้อย
  • ย้ายการประกาศของc(ซึ่งฉันเปลี่ยนชื่อเป็นdigit) ไปไว้ในขณะที่ลูปเนื่องจากใช้เฉพาะที่นั่นเท่านั้น
  • เปลี่ยนประเภทอาร์กิวเมนต์เป็นlongเพื่อให้แน่ใจว่าสามารถจัดการกับชุดอินพุตทั้งหมดได้
int sum_of_digits_squared(long n) 
{
    long sum = 0;

    while (n != 0) {
        int digit = n % 10;
        sum += (digit * digit);
        n /= 10;
    }

    return sum;
}

ที่จริงแล้วสิ่งนี้อาจทำให้เข้าใจผิดได้นิดหน่อยเพราะ - ดังกล่าวข้างต้น - ตัวแปรdigitสามารถรับค่าลบได้ แต่ตัวเลขนั้นอยู่ในตัวของมันเองไม่ว่าจะเป็นบวกหรือลบ มีสองสามวิธีรอบ ๆ นี้ แต่นี่เป็น nitpicking จริง ๆ และฉันจะไม่สนใจรายละเอียดเล็ก ๆ น้อย ๆ โดยเฉพาะอย่างยิ่งฟังก์ชั่นที่แยกต่างหากสำหรับตัวเลขสุดท้ายคือมันไกลเกินไป นี่เป็นสิ่งหนึ่งที่รหัสครูของคุณแก้ไขได้จริง

  • เปลี่ยนsum += (digit * digit)ไปsum += ((n%10)*(n%10))และข้ามตัวแปรdigitสมบูรณ์
  • เปลี่ยนเครื่องหมายdigitถ้าเป็นลบ แต่ฉันขอแนะนำอย่างยิ่งว่าอย่าให้รหัสซับซ้อนขึ้นเพียงเพื่อให้ชื่อตัวแปรเหมาะสมแล้ว นั่นคือกลิ่นรหัสที่แข็งแกร่งมาก
  • สร้างฟังก์ชั่นแยกต่างหากที่แยกหลักสุดท้าย int last_digit(long n) { int digit=n%10; if (digit>=0) return digit; else return -digit; }สิ่งนี้มีประโยชน์หากคุณต้องการใช้ฟังก์ชั่นนั้นที่อื่น
  • เพียงตั้งชื่อcตามที่คุณทำ ชื่อตัวแปรนั้นไม่ได้ให้ข้อมูลที่เป็นประโยชน์ แต่ในทางกลับกันก็ไม่ทำให้เข้าใจผิดเช่นกัน

แต่เพื่อความซื่อสัตย์ ณ จุดนี้คุณควรย้ายไปทำงานที่สำคัญกว่า :)


19
หากอินพุตINT_MINและสถาปัตยกรรมใช้ส่วนประกอบสองอย่าง (ซึ่งเป็นเรื่องธรรมดามาก) รหัสครูของคุณจะให้ผลการทำงานที่ไม่ได้กำหนด อุ๊ยตาย ที่จะทิ้งร่องรอยไว้ ;-)
Andrew Henle

5
มันควรจะกล่าวถึงว่านอกจากนี้(a/b)*b + a%b ≡ aรหัสของ OP ยังขึ้นอยู่กับความจริงที่ว่า/ปัดไปทางศูนย์และนั่นก็(-c)*(-c) ≡ c*cคือ มันอาจจะแย้งว่าการตรวจสอบเป็นพิเศษจะมีความชอบธรรมแม้จะมีมาตรฐานรับประกันทุกที่เพราะมันเพียงพอที่ไม่ชัดเจน (แน่นอนว่ามันอาจเป็นที่ถกเถียงกันอยู่พอสมควรว่าควรมีความคิดเห็นเชื่อมโยงส่วนมาตรฐานที่เกี่ยวข้อง แต่แนวทางของสไตล์แตกต่างกันไป)
leftaroundabout

7
@MartinRosenau คุณพูดว่า "อาจ" คุณแน่ใจหรือว่านี่เป็นสิ่งที่เกิดขึ้นจริงหรืออนุญาตโดยมาตรฐานหรืออะไรบางอย่างหรือคุณแค่เก็งกำไร?
klutt

6
@MartinRosenau: ตกลง แต่การใช้สวิตช์เหล่านั้นจะทำให้มันไม่ใช่ C อีกต่อไป GCC / clang ไม่มีสวิตช์ใด ๆ ที่ทำลายการแบ่งจำนวนเต็มบน ISA ใด ๆ ที่ฉันรู้ แม้ว่าการเพิกเฉยเครื่องหมายบิตอาจให้ความเร็วโดยใช้อินเวอร์สการคูณแบบปกติสำหรับตัวหารคงที่ (แต่ ISAs ทั้งหมดที่ฉันคุ้นเคยที่มีคำสั่งการแบ่งฮาร์ดแวร์ใช้วิธี C99 ตัดให้เป็นศูนย์ดังนั้น C %และ/ผู้ประกอบการสามารถคอมไพล์เพียงidivบน x86 หรือsdivบน ARM หรืออะไรก็ตามยังคงไม่เกี่ยวข้องกับอะไรมาก รหัส-Gen เร็วขึ้นสำหรับตัวหารรวบรวมเวลาคงที่)
ปีเตอร์ Cordes

5
@TonyK AFIK นั่นเป็นวิธีที่ปกติแล้วจะแก้ไขได้ แต่ตามมาตรฐานคือ UB
klutt

20

ฉันไม่ชอบเวอร์ชั่นของคุณหรืออาจารย์ของคุณเลย รุ่นครูของคุณทำการทดสอบเพิ่มเติมที่คุณชี้ให้เห็นอย่างถูกต้องไม่จำเป็น ตัวดำเนินการตัวดัดแปลงของ C นั้นไม่ใช่ตัวดัดแปลงทางคณิตศาสตร์ที่เหมาะสม: ตัวเลขลบตัวที่ 10 จะสร้างผลลัพธ์เชิงลบ แต่เมื่อคุณกำลังสองมันก็ไม่ต่างกัน

แต่สิ่งนี้ยังไม่ชัดเจนดังนั้นฉันจะเพิ่มรหัสของคุณไม่ใช่การตรวจสอบจากอาจารย์ของคุณ แต่เป็นความคิดเห็นขนาดใหญ่ที่อธิบายว่าทำไมมันจึงใช้งานได้ เช่น:

/ * หมายเหตุ: สิ่งนี้ใช้ได้กับค่าลบเนื่องจากโมดูลัสรับกำลังสอง * /


9
C %คือส่วนที่เหลือที่ดีที่สุดเพราะมันเป็นอย่างนั้นแม้กระทั่งสำหรับประเภทที่เซ็นชื่อ
Peter Cordes

15
กำลังสองเป็นสิ่งสำคัญ แต่ฉันคิดว่านั่นเป็นส่วนที่ชัดเจน สิ่งที่ควรชี้ให้เห็นก็คือ (เช่น) -7 % 10 จริง ๆ แล้วจะ-7เป็นมากกว่า 3
Jacob Raihle

5
“ โมดูลัสทางคณิตศาสตร์ที่เหมาะสม” ไม่ได้มีความหมายอะไรเลย คำที่ถูกต้องคือ“ Euclidean modulo” (คำนึงถึงคำต่อท้าย!) และนั่นคือสิ่งที่%ผู้ประกอบการC ไม่ได้เป็น
Jan Hudec

ฉันชอบคำตอบนี้เพราะมันตั้งคำถามหลายวิธีในการตีความโมดูโล อย่าปล่อยให้สิ่งนั้นเป็นโอกาส / การตีความ นี่ไม่ใช่รหัสกอล์ฟ
ฮาร์เปอร์ - Reinstate Monica

1
"โมดูลัสทางคณิตศาสตร์ที่เหมาะสมนั้นไม่ใช่ค่าลบเสมอ" - ไม่จริง ผลลัพธ์ของการดำเนินการแบบโมดูโลเป็นคลาสที่เท่าเทียมกันแต่เป็นเรื่องปกติที่จะจัดการกับผลลัพธ์เป็นจำนวนที่ไม่เป็นลบที่เล็กที่สุดของคลาสนั้น
klutt

10

หมายเหตุ: ในขณะที่ฉันกำลังเขียนคำตอบนี้คุณได้ชี้แจงว่าคุณกำลังใช้ C. คำตอบส่วนใหญ่ของฉันคือ C ++ อย่างไรก็ตามเนื่องจากชื่อของคุณยังคงมี C ++ และคำถามยังคงติดแท็ก C ++ ฉันได้เลือกที่จะตอบอย่างไรก็ตามในกรณีนี้ยังมีประโยชน์กับคนอื่นโดยเฉพาะอย่างยิ่งเนื่องจากคำตอบส่วนใหญ่ที่ฉันเห็นจนถึงตอนนี้ไม่เป็นที่พอใจ

ในC ++สมัยใหม่(หมายเหตุ: ฉันไม่รู้จริงๆว่า C ยืนตรงนี้) อาจารย์ของคุณดูเหมือนจะผิดทั้งสองอย่าง

อย่างแรกคือส่วนนี้ที่นี่:

if (n == 0) {
        return 0;
}

ใน C ++ สิ่งนี้จะเหมือนกับ :

if (!n) {
        return 0;
}

นั่นหมายความว่าเวลาของคุณเทียบเท่ากับสิ่งนี้:

while(n != 0) {
    // some implementation
}

นั่นหมายความว่าเนื่องจากคุณกำลังออกจากตัวคุณหากเมื่อเวลาผ่านไปจะไม่ดำเนินการต่อไปไม่มีเหตุผลที่จะนำสิ่งนี้มาใช้ที่นี่เนื่องจากสิ่งที่คุณทำหลังจากวนรอบและถ้าเทียบกันแล้ว แม้ว่าฉันควรจะบอกว่าเป็นเพราะเหตุผลบางอย่างสิ่งเหล่านี้แตกต่างกันคุณจะต้องมีสิ่งนี้หาก

ดังนั้นถ้าคำสั่งนี้ไม่มีประโยชน์อย่างยิ่งถ้าฉันเข้าใจผิด

ส่วนที่สองคือสิ่งที่ขนดก:

if (n < 0) {
    n = n * (-1);
}  

หัวใจของปัญหาคือสิ่งที่เอาท์พุทของโมดูลัสของเอาท์พุทจำนวนลบ

ใน C ++ ที่ทันสมัยสิ่งนี้ดูเหมือนจะถูกกำหนดไว้เป็นอย่างดี :

ไบนารี / โอเปอเรเตอร์ให้ผลหารของผลหารและตัวดำเนินการไบนารี่% ให้ผลที่เหลือจากการหารของนิพจน์แรกทีละวินาที หากตัวถูกดำเนินการที่สองของ / หรือ% เป็นศูนย์พฤติกรรมจะไม่ได้กำหนด สำหรับโอเปอแรนด์หนึ่งตัวดำเนินการ / โอเปอเรเตอร์ให้ผลหารพีชคณิตด้วยส่วนเศษส่วนใด ๆ ที่ถูกทิ้ง หากผลหาร a / b สามารถแทนได้ในประเภทของผลลัพธ์ (a / b) * b + a% b เท่ากับ a

และหลังจากนั้น:

หากตัวถูกดำเนินการทั้งสองไม่เป็นค่าลบส่วนที่เหลือจะไม่เป็นค่าลบ ถ้าไม่ใช่เครื่องหมายของส่วนที่เหลือจะถูกกำหนดโดยการนำไปปฏิบัติ

เนื่องจากโปสเตอร์ของคำตอบที่ยกมานั้นชี้ให้เห็นอย่างถูกต้องส่วนสำคัญของสมการนี้ตรงนี้:

(a / b) * b + a% b

ยกตัวอย่างกรณีของคุณคุณจะได้รับสิ่งนี้:

-13/ 10 = -1 (integer truncation)
-1 * 10 = -10
-13 - (-10) = -13 + 10 = -3 

สิ่งเดียวที่จับได้คือบรรทัดสุดท้าย:

หากตัวถูกดำเนินการทั้งสองไม่เป็นค่าลบส่วนที่เหลือจะไม่เป็นค่าลบ ถ้าไม่ใช่เครื่องหมายของส่วนที่เหลือจะถูกกำหนดโดยการนำไปปฏิบัติ

นั่นหมายความว่าในกรณีเช่นนี้มีเพียงเครื่องหมายที่ดูเหมือนว่าจะถูกนำไปปฏิบัติ นั่นไม่ควรเป็นปัญหาในกรณีของคุณเพราะเพราะคุณกำลังสองค่านี้อยู่ดี

ที่กล่าวไว้โปรดจำไว้ว่าสิ่งนี้ไม่จำเป็นต้องใช้กับ C ++ รุ่นก่อนหน้าหรือ C99 ถ้านั่นคือสิ่งที่อาจารย์ของคุณใช้อยู่นั่นอาจเป็นสาเหตุ


แก้ไข:ไม่ฉันผิด นี่น่าจะเป็นกรณีของ C99 หรือใหม่กว่าเช่นกัน :

C99 ต้องการว่าเมื่อ a / b สามารถแทนได้:

(a / b) * b + a% b จะเท่ากับ a

และสถานที่อื่น :

เมื่อจำนวนเต็มถูกหารและการหารไม่แน่นอนถ้าตัวถูกดำเนินการทั้งสองเป็นบวกผลลัพธ์ของ / โอเปอเรเตอร์จะเป็นจำนวนเต็มที่มากที่สุดน้อยกว่าความฉลาดทางพีชคณิตและผลลัพธ์ของโอเปอเรเตอร์% เป็นบวก หากตัวถูกดำเนินการทั้งสองเป็นลบไม่ว่าผลลัพธ์ของตัวดำเนินการ / จะเป็นจำนวนเต็มที่มากที่สุดน้อยกว่าความฉลาดทางพีชคณิตหรือจำนวนเต็มที่น้อยที่สุดที่มากกว่าความฉลาดทางพีชคณิตเป็นการดำเนินการตามที่กำหนดเป็นสัญญาณของผลลัพธ์ของตัวดำเนินการ% หากสามารถหารผลหาร a / b ได้นิพจน์ (a / b) * b + a% b จะเท่ากับ a

ANSI C หรือ ISO C ระบุว่า -5% 10 ควรเป็นเท่าใด

ดังนั้นใช่ แม้แต่ใน C99 สิ่งนี้ดูเหมือนจะไม่ส่งผลกระทบต่อคุณ สมการนั้นเหมือนกัน


1
ส่วนที่คุณยกมาไม่สนับสนุนคำตอบนี้ "สัญลักษณ์ของส่วนที่เหลือคือการใช้งานที่กำหนดไว้" ไม่ได้หมายความว่า(-1)%10จะสร้าง-1หรือ1; มันหมายความว่ามันสามารถผลิต-1หรือ9และในกรณีหลัง(-1)/10จะผลิต-1และรหัสของ OP จะไม่สิ้นสุด
stewbasic

คุณสามารถชี้แหล่งที่มาสำหรับสิ่งนี้ได้ไหม ฉันมีเวลายากมากที่จะเชื่อ (-1) / 10 คือ -1 นั่นควรเป็น 0 และ (-1)% 10 = 9 ดูเหมือนจะละเมิดสมการที่บังคับใช้
Chipster

1
@Chipster เริ่มต้นด้วย(a/b)*b + a%b == aแล้วปล่อยให้a=-1; b=10 (-1/10)*10 + (-1)%10 == -1ทีนี้ถ้าเรา-1/10ถูกปัดเศษลง (ไปยัง -inf) เรามี(-1/10)*10 == -10และคุณต้องมี(-1)%10 == 9สมการแรกที่จะจับคู่ เช่นเดียวกับคำตอบอื่น ๆนี่ไม่ใช่วิธีการทำงานภายในมาตรฐานปัจจุบัน แต่เป็นวิธีที่ใช้ในการทำงาน มันไม่จริงเกี่ยวกับการเข้าสู่ระบบของส่วนที่เหลือดังกล่าว แต่อย่างไรส่วนรอบและสิ่งที่เหลือจากนั้นก็มีมาให้ตอบสนองความสม
ilkkachu

1
@Chipster แหล่งที่มาคือตัวอย่างข้อมูลที่คุณยกมา โปรดทราบว่า(-1)*10+9=-1ดังนั้นทางเลือก(-1)/10=-1และ(-1)%10=9ไม่ละเมิดสมการปกครอง ในทางกลับกันตัวเลือก(-1)%10=1ไม่สามารถตอบสนองสมการที่ควบคุมได้ไม่ว่า(-1)/10จะถูกเลือกอย่างไร ไม่มีจำนวนเต็มเช่นว่าq q*10+1=-1
stewbasic

8

ตามที่คนอื่น ๆ ชี้ให้เห็นการดูแลเป็นพิเศษสำหรับ n == 0 นั้นเป็นเรื่องไร้สาระเนื่องจากสำหรับโปรแกรมเมอร์ C ทุกคนที่เห็นได้ชัดว่า "ในขณะที่ (n)" ทำงาน

พฤติกรรมของ n <0 นั้นไม่ชัดเจนนั่นเป็นสาเหตุที่ฉันต้องการเห็นโค้ด 2 บรรทัดเหล่านี้:

if (n < 0) 
    n = -n;

หรืออย่างน้อยความคิดเห็น:

// don't worry, works for n < 0 as well

ตามจริงแล้วคุณเริ่มพิจารณาเมื่อใดว่าอาจเป็นลบ เมื่อเขียนรหัสหรือเมื่ออ่านข้อสังเกตของครูของคุณ?


1
ไม่ว่า N จะเป็นลบหรือไม่ N กำลังสองจะเป็นบวก ดังนั้นทำไมลบเครื่องหมายในตอนแรก? -3 * -3 = 9; 3 * 3 = 9. หรือคณิตศาสตร์เปลี่ยนแปลงใน 30 ปีนับตั้งแต่ฉันเรียนรู้สิ่งนี้?
Merovex

2
@CB เพื่อความยุติธรรมฉันไม่ได้สังเกตว่า n อาจเป็นค่าลบในขณะที่ฉันกำลังเขียนการทดสอบ แต่เมื่อมันกลับมาฉันรู้สึกว่าขณะที่ลูปจะไม่ถูกข้ามแม้ว่าจำนวนจะเป็นค่าลบ ฉันทำการทดสอบบางอย่างบนคอมพิวเตอร์และยืนยันความสงสัยของฉัน หลังจากนั้นฉันโพสต์คำถามนี้ ไม่เลยฉันไม่ได้คิดอย่างลึกซึ้งในขณะที่เขียนรหัส
user010517720

5

สิ่งนี้ทำให้ฉันนึกถึงการบ้านที่ฉันล้มเหลว

ย้อนกลับไปในยุค 90 อาจารย์บรรยายให้ฟังเกี่ยวกับลูปและเรื่องสั้นสั้น ๆ ที่ได้รับมอบหมายของเราคือการเขียนฟังก์ชั่นที่จะคืนจำนวนตัวเลขสำหรับจำนวนเต็มใด ๆ ที่กำหนด> 0

ดังนั้นสำหรับตัวอย่างเช่นจำนวนของตัวเลขในจะเป็น3213

แม้ว่าจะได้รับมอบหมายก็บอกว่าจะเขียนฟังก์ชันที่ส่งกลับจำนวนของตัวเลขที่คาดหวังคือการที่เราจะใช้ห่วงที่แบ่ง 10 จนกระทั่ง ... คุณได้รับมันเป็นปกคลุมด้วยการบรรยาย

แต่การใช้ลูปไม่ได้ระบุอย่างชัดเจนดังนั้นฉัน: took the log, stripped away the decimals, added 1และต่อมาก็ lambasted ต่อหน้าทั้งชั้น

จุดคือจุดประสงค์ของงานคือการทดสอบความเข้าใจในสิ่งที่เราได้เรียนรู้ระหว่างการบรรยายของเรา จากการบรรยายที่ฉันได้รับฉันได้เรียนรู้ว่าอาจารย์สอนคอมพิวเตอร์เป็นกระตุกเล็กน้อย


ในสถานการณ์ของคุณ:

เขียนฟังก์ชั่นใน C / C ++ ที่ส่งคืนผลรวมของตัวเลขของตัวเลขกำลังสอง

แน่นอนฉันจะให้คำตอบสองข้อ:

  • คำตอบที่ถูกต้อง (กำลังสองจำนวนแรก) และ
  • คำตอบที่ไม่ถูกต้องในการรักษาด้วยตัวอย่างเพียงเพื่อให้เขามีความสุข ;-)

5
และอีกอันที่สามกำลังสองรวมผลรวมของตัวเลข?
kriss

@kriss - ใช่ฉันไม่ใช่คนฉลาด :-(
SlowLearner

1
ฉันยังมีส่วนร่วมของฉัน assigments คลุมเครือเกินไปในเวลานักเรียนของฉัน ครูต้องการห้องสมุดจัดการนิดหน่อย แต่เขาแปลกใจกับรหัสของฉันและบอกว่ามันไม่ทำงาน ฉันต้องชี้ให้เขาเห็นว่าเขาไม่เคยนิยาม endianness ในงานมอบหมายของเขาและเขาเลือกบิตที่ต่ำกว่าเป็นบิต 0 ในขณะที่ฉันเลือกอีกทางเลือกหนึ่ง ส่วนที่น่ารำคาญเพียงอย่างเดียวคือเขาควรจะสามารถเข้าใจความแตกต่างได้โดยที่ฉันไม่บอกเขา
kriss

1

โดยทั่วไปในการมอบหมายไม่ใช่เครื่องหมายทั้งหมดที่ได้รับเพียงเพราะรหัสใช้งานได้ นอกจากนี้คุณยังได้รับเครื่องหมายสำหรับการแก้ปัญหาที่อ่านง่ายมีประสิทธิภาพและสวยงาม สิ่งเหล่านี้ไม่ได้เกิดขึ้นพร้อมกันเสมอไป

หนึ่งที่ผมไม่สามารถความเครียดพอ"ใช้ชื่อตัวแปรที่มีความหมาย"

ในตัวอย่างของคุณมันไม่ได้สร้างความแตกต่างมากนัก แต่ถ้าคุณกำลังทำงานในโครงการที่มีบรรทัดการอ่านจำนวนมากกลายเป็นเรื่องสำคัญมาก

อีกสิ่งที่ฉันมักจะเห็นด้วยรหัส C คือคนที่พยายามฉลาด แทนที่จะใช้while (n! = 0)ฉันจะแสดงให้ทุกคนเห็นว่าฉันฉลาดแค่ไหนในการเขียนในขณะที่ (n)เพราะมันหมายถึงสิ่งเดียวกัน มันทำอย่างนั้นในคอมไพเลอร์ที่คุณมี แต่ตามที่คุณแนะนำคุณรุ่นที่เก่ากว่าของครูไม่ได้นำไปใช้เหมือนกัน

ตัวอย่างทั่วไปคือการอ้างอิงดัชนีในอาร์เรย์ในขณะที่เพิ่มขึ้นในเวลาเดียวกัน เบอร์ [i ++] = iPrime;

ตอนนี้โปรแกรมเมอร์คนต่อไปที่ทำงานกับโค้ดต้องรู้ว่าฉันได้รับการเพิ่มก่อนหรือหลังการมอบหมายดังนั้นใครบางคนจึงสามารถอวดได้

พื้นที่ว่างบนดิสก์หนึ่งเมกะไบต์นั้นถูกกว่ากระดาษชำระม้วนหนึ่งออกไปเพื่อความกระจ่างแจ้งแทนที่จะพยายามประหยัดพื้นที่นักเขียนโปรแกรมของคุณจะมีความสุขมากขึ้น


2
ฉันได้โปรแกรมใน C เพียงไม่กี่ครั้งและฉันรู้ว่าการ++iเพิ่มขึ้นก่อนการประเมินและการi++เพิ่มขึ้นหลังจากนั้น while(n)เป็นคุณสมบัติภาษาทั่วไป ตามตรรกะเช่นนี้ฉันเห็นโค้ดif (foo == TRUE)มากมาย ฉันตกลงอีกครั้ง: ชื่อตัวแปรแม้ว่า
alan ocallaghan

3
โดยทั่วไปนั่นไม่ใช่คำแนะนำที่ไม่ดี แต่การหลีกเลี่ยงคุณสมบัติทางภาษาขั้นพื้นฐาน (ซึ่งผู้คนจะต้องเจออย่างหลีกเลี่ยงไม่ได้) สำหรับจุดประสงค์ของการตั้งโปรแกรมการป้องกันก็มีมากเกินไป รหัสสั้น ๆ ที่ชัดเจนมักอ่านง่ายกว่าอยู่ดี เราไม่ได้พูดถึง Perl บ้าหรือทุบตีอย่างใดอย่างหนึ่ง liners ที่นี่เพียงแค่คุณสมบัติภาษาขั้นพื้นฐานจริงๆ
alan ocallaghan

1
ไม่แน่ใจว่า downvotes สำหรับคำตอบนี้เป็นอย่างไร สำหรับฉันทุกอย่างที่ระบุไว้ที่นี่เป็นเรื่องจริงและสำคัญและเป็นคำแนะนำที่ดีสำหรับนักพัฒนาทุกคน โดยเฉพาะสมาร์ทตูดโปรแกรมส่วนหนึ่งแม้ว่าจะwhile(n)ไม่ได้เป็นตัวอย่างที่เลวร้ายที่สุดสำหรับการที่ (ผม "เหมือนที่" if(strcmp(one, two))มากกว่า)
ไก่ Huppmann

1
และนอกจากนี้คุณไม่ควรให้ใครก็ตามที่ไม่ทราบความแตกต่างระหว่างi++และ++iแก้ไขรหัส C ที่ควรใช้ในการผลิต
klutt

2
@PaulMcCarthy เราทั้งคู่มีความสามารถในการอ่านโค้ด แต่เราไม่เห็นด้วยกับสิ่งที่หมายถึง นอกจากนี้มันไม่ได้มีวัตถุประสงค์ สิ่งที่ง่ายต่อการอ่านสำหรับคนคนหนึ่งอาจเป็นเรื่องยากสำหรับคนอื่น บุคลิกภาพและความรู้พื้นฐานมีผลกระทบอย่างยิ่งนี้ ประเด็นหลักของฉันคือคุณไม่สามารถอ่านได้สูงสุดโดยสุ่มสี่สุ่มห้าตามกฎบางอย่าง
klutt

0

ฉันจะไม่โต้แย้งว่าต้นฉบับหรือคำจำกัดความที่ทันสมัยของ '%' นั้นดีกว่าหรือไม่ แต่ใครก็ตามที่เขียนคำสั่ง return สองคำในฟังก์ชันสั้น ๆ ไม่ควรสอนการเขียนโปรแกรม C เลย ผลตอบแทนพิเศษคือคำสั่ง goto และเราไม่ได้ใช้ goto ใน C นอกจากนี้โค้ดที่ไม่มีการตรวจสอบที่ศูนย์จะมีผลลัพธ์เหมือนกันผลตอบแทนพิเศษทำให้อ่านยากขึ้น


4
"ผลตอบแทนพิเศษคือคำสั่ง goto และเราไม่ได้ใช้ goto ใน C. " - นั่นคือการรวมกันของลักษณะทั่วไปที่กว้างมากและยืดออกไปไกลมาก
SS Anne

1
"เรา" ใช้ goto ใน C. แน่นอนไม่มีอะไรผิดปกติ
klutt

1
นอกจากนี้ยังไม่มีอะไรผิดปกติกับฟังก์ชั่นเช่นint findChar(char *str, char c) { if(!str) return -1; int i=0; while(str[i]) { if(str[i] == c) return i; i++; } return -1; }
klutt

1
@PeterKrassoi ฉันไม่ได้สนับสนุนการอ่านรหัสยาก แต่ฉันได้เห็นตัวอย่างมากมายที่รหัสดูเหมือนเป็นระเบียบสมบูรณ์เพียงเพื่อหลีกเลี่ยงการข้ามไปง่าย ๆ หรือการส่งคืนข้อความพิเศษ นี่คือโค้ดบางส่วนที่มีการใช้ goto ที่เหมาะสม ฉันขอให้คุณลบคำชี้แจงของ goto ในขณะเดียวกันก็ทำให้การอ่านและรหัสง่ายขึ้น: pastebin.com/aNaGb66Q
klutt

1
@PeterKrassoi โปรดแสดงเวอร์ชันของคุณด้วย: pastebin.com/qBmR78KA
klutt

0

คำสั่งที่มีปัญหาความสับสน แต่ชี้แจงตัวอย่างเช่นตัวเลขความหมายของผลรวมของตัวเลขของจำนวนที่ยกกำลังสอง นี่คือรุ่นที่ปรับปรุง:

เขียนฟังก์ชันในชุดย่อยทั่วไปของ C และ C ++ ที่รับค่าจำนวนเต็มnในช่วง[-10 7 , 10 7 ]และส่งกลับผลรวมของกำลังสองของตัวเลขที่เป็นตัวแทนในฐาน 10 ตัวอย่าง: ถ้าnเป็น123ฟังก์ชันของคุณ ควรกลับมา14(1 2 + 2 2 + 3 2 = 14)

ฟังก์ชั่นที่คุณเขียนนั้นดียกเว้น 2 รายละเอียด:

  • อาร์กิวเมนต์ควรมีประเภทlongเพื่อรองรับค่าทั้งหมดในช่วงที่ระบุเป็นชนิดlongรับประกันโดย C มาตรฐานจะมีอย่างน้อย 31 บิตค่าเพราะฉะนั้นช่วงที่เพียงพอเพื่อแสดงค่าทั้งหมดใน[-10 7 10 7 ] (โปรดทราบว่าประเภทintนั้นเพียงพอสำหรับประเภทส่งคืนซึ่งมีค่าสูงสุดคือ568)
  • พฤติกรรมของ%ตัวถูกดำเนินการเชิงลบนั้นไม่ง่ายและข้อมูลจำเพาะของมันนั้นแตกต่างกันระหว่าง C99 Standard และรุ่นก่อนหน้า คุณควรบันทึกว่าทำไมแนวทางของคุณถึงถูกต้องแม้จะเป็นปัจจัยลบ

นี่คือรุ่นที่แก้ไข:

int sum_of_digits_squared(long n) {
    int s = 0;

    while (n != 0) {
        /* Since integer division is defined to truncate toward 0 in C99 and C++98 and later,
           the remainder of this division is positive for positive `n`
           and negative for negative `n`, and its absolute value is the last digit
           of the representation of `n` in base 10.
           Squaring this value yields the expected result for both positive and negative `c`.
           dividing `n` by 10 effectively drops the last digit in both cases.
           The loop will not be entered for `n == 0`, producing the correct result `s = 0`.
         */
        int c = n % 10;
        s += c * c;
        n /= 10;
    }
    return s;
}

คำตอบของครูมีข้อบกพร่องหลายประการ:

  • ประเภทintอาจมีช่วงค่าไม่เพียงพอ
  • 0มีความจำเป็นต้องกรณีพิเศษค่าไม่มี
  • n = INT_MINกวนค่าลบที่ไม่จำเป็นและอาจมีพฤติกรรมที่ไม่ได้กำหนดไว้สำหรับ

เนื่องจากข้อ จำกัด พิเศษในคำแถลงปัญหา (C99 และช่วงของค่าสำหรับn) ข้อบกพร่องแรกเท่านั้นที่เป็นปัญหา รหัสพิเศษยังคงสร้างคำตอบที่ถูกต้อง

คุณควรได้รับเครื่องหมายที่ดีในการทดสอบนี้ แต่จำเป็นต้องมีคำอธิบายในแบบทดสอบข้อเขียนเพื่อแสดงความเข้าใจของคุณเกี่ยวกับประเด็นด้านลบnมิฉะนั้นอาจารย์อาจถือว่าคุณไม่รู้และเพิ่งโชคดี ในการสอบปากเปล่าคุณจะได้รับคำถามและคำตอบของคุณจะถูกจับ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.