ศูนย์เป็นค่าคงที่หรือไม่?


15

ฉันได้เจอสำนวนการเขียนโปรแกรมนี้เมื่อเร็ว ๆ นี้:

const float Zero = 0.0;

ซึ่งจะใช้ในการเปรียบเทียบ:

if (x > Zero) {..}

ใครสามารถอธิบายได้ว่าสิ่งนี้มีประสิทธิภาพมากขึ้นหรือสามารถอ่านหรือบำรุงรักษาได้ดีกว่า:

if (x > 0.0) {..}

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


31
นักพัฒนามีการวางแผนในการย้ายรหัสไปยังจักรวาลที่กฎของคณิตศาสตร์แตกต่างกันอย่างไร
vaughandroid

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

@ Baqueta - เป็นจักรวาลสำรองหรือไม่? ฉันคิดว่าพวกเขาอาศัยอยู่ที่นั่นแล้ว! สำหรับตัวเลขเวทย์มนตร์ฉันเห็นด้วย แต่ฉันใช้กฎง่ายๆว่าทุกอย่างยกเว้น 0 & 1 ควรคงที่
NWS

1
หากxมีประเภทแสดงfloatว่าx > 0.0บังคับให้เลื่อนระดับdoubleซึ่งอาจมีประสิทธิภาพน้อยกว่า นั่นไม่ใช่เหตุผลที่ดีสำหรับการใช้ชื่ออย่างต่อเนื่องแม้ว่าจะเพียงเพื่อให้แน่ใจว่าค่าคงที่ของคุณมีประเภทที่ถูกต้อง (เช่น0f, float(0)หรือdecltype(x)(0))
Mike Seymour

1
@JoSo: เป็นธรรมประเภทของ13.37ไม่ได้ก็float doubleดังนั้นถ้าคุณต้องการfloatมันก็เป็นไปได้ว่าครูสอนพิเศษของคุณถูกต้อง ในบริบทบางอย่าง (เช่นการมอบหมายให้ลอย) 13.37โดยปริยายจะถูกแปลงเป็นสิ่งfloatที่คุณต้องการและในบริบทอื่น ๆ (เช่นการลดประเภทเทมเพลต) มันจะไม่เป็นในขณะที่static const floatเริ่มต้นเสมอตามประเภทที่คุณตั้งใจ ดังนั้นปลอดภัยมากขึ้นประเภท ใจคุณดังนั้นจะเป็น13.37f! มีเหตุผลอื่นในการหลีกเลี่ยงมาโครมากกว่า "ประเภทความปลอดภัย" ดังนั้นผู้สอนจึงให้เหตุผลที่ไม่ดีแก่คุณ
Steve Jessop

คำตอบ:


29

สาเหตุที่เป็นไปได้คือการแคชการตั้งชื่อหรือการบังคับประเภท

การแคช (ใช้ไม่ได้)

คุณต้องการหลีกเลี่ยงค่าใช้จ่ายในการสร้างวัตถุระหว่างการเปรียบเทียบ ใน Java ตัวอย่างจะเป็น

BigDecimal zero = new BigDecimal ("0.0");

สิ่งนี้เกี่ยวข้องกับกระบวนการสร้างที่ค่อนข้างหนักและให้บริการที่ดีกว่าโดยใช้วิธีสแตติกที่ให้มา:

BigDecimal zero = BigDecimal.ZERO;

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

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

การตั้งชื่อ (ไม่น่า)

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

บังคับประเภท (น่าจะ)

นักพัฒนาดั้งเดิมพยายามที่จะบังคับประเภทดั้งเดิมโดยเฉพาะเพื่อให้แน่ใจว่าการเปรียบเทียบถูกส่งไปยังประเภทที่ถูกต้องและอาจเป็นไปได้ในระดับที่เฉพาะเจาะจง (จำนวนตำแหน่งทศนิยม) นี่เป็นเรื่องปกติ แต่ชื่อแบบง่าย "ศูนย์" อาจมีรายละเอียดไม่เพียงพอสำหรับกรณีการใช้งานนี้โดยที่ ZERO_1DP เป็นนิพจน์ที่เหมาะสมยิ่งขึ้นของเจตนา


2
+1 สำหรับประเภทการบังคับ ฉันจะเพิ่มสิ่งนั้นในภาษาอย่าง C ++ ที่อนุญาตให้ผู้ปฏิบัติงานทำการโหลดมากเกินไปการกำหนดค่าคงที่และการใช้typedefจะทำให้ชนิดของตัวแปรอยู่ในที่เดียวและสามารถเปลี่ยนได้โดยไม่ต้องเปลี่ยนรหัส
Blrfl

3
ประเภทการบังคับไม่น่าจะเป็นสิ่งที่พวกเขาพยายาม แต่นี่เป็นคำอธิบายที่ดีที่สุดว่าทำไมมันสามารถทำได้!
NWS

7
0.0fสำหรับการบังคับประเภทผมอาจจะมากกว่าเพียงแค่การใช้งาน
Svish

การบังคับประเภทบางครั้งอาจมีประโยชน์ใน vb.net โดยที่การใช้ตัวดำเนินการระดับบิตในไบต์จะให้ผลลัพธ์เป็นไบต์ การพูดbyteVar1 = byteVar2 Or CB128ดูเหมือนว่าดีกว่าbyteVar1 = byteVar2 Or CByte(128)เล็กน้อย แน่นอนว่าการมีส่วนต่อท้ายตัวเลขที่เหมาะสมสำหรับไบต์จะดีกว่า เนื่องจาก C # ส่งเสริมตัวถูกดำเนินการของ bitwise ให้กับผู้ประกอบการถึงintแม้ว่าผลลัพธ์จะได้รับการรับรองว่าเหมาะสมกับ a byteปัญหาจึงไม่เกี่ยวข้องกัน
supercat

ฉันไม่แน่ใจว่ามีชื่อคงที่เป็นศูนย์สำหรับ '0' แต่บางครั้งมันช่วยในการอ่านรหัส; ตัวอย่างเช่นการมีค่าคงที่นี้สำหรับศูนย์ - "ROOT_TYPE_ID = 0" จะช่วยในการเขียนคำสั่งเช่นถ้า (id! = ROOT_TYPE_ID) {.. }
เทค Junkie

6

มันเป็นเพราะ "Tooling Nagging"

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

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

และเมื่อสิ่งนั้นเกิดขึ้นบางครั้งคุณต้องเผชิญกับทางเลือก:

  • ทำเครื่องหมายว่าเป็นผลบวกปลอมหากเครื่องมืออนุญาต (โดยปกติจะมีรูปแบบความคิดเห็นเป็นพิเศษซึ่งเป็นที่น่ารำคาญสำหรับผู้ที่ไม่ได้ใช้เครื่องมือ)
  • หรือแยกค่าเหล่านี้ไปยังค่าคงที่ไม่ว่าจะสำคัญหรือไม่ก็ตาม

เกี่ยวกับประสิทธิภาพ

มันขึ้นอยู่กับภาษาที่ฉันเดา แต่นี้เป็นเรื่องธรรมดาในชวาและมีผลกระทบต่อประสิทธิภาพไม่เป็นค่า inlined static finalที่รวบรวมเวลาถ้าพวกเขามีค่าคงที่จริง มันจะไม่มีผลกระทบใน C หรือ C ++ หากพวกมันถูกประกาศเป็นค่าคงที่หรือแม้แต่มาโครตัวประมวลผลล่วงหน้า


5

ซึ่งอาจทำให้ความรู้สึกเพราะมันชัดเจนกำหนดให้เป็นประเภทZerofloat

อย่างน้อยก็ใน C และ C ++ ค่า0.0เป็นประเภทdoubleขณะที่เทียบเท่าคือfloat 0.0fดังนั้นการสมมติว่าxคุณเปรียบเทียบกับก็เป็นfloatคำพูดเสมอ

x > 0.0

ในขณะที่ส่งเสริมxให้doubleตรงกับประเภท0.0ที่อาจนำไปสู่ปัญหา (ด้วยการทดสอบความเท่าเทียมกันโดยเฉพาะ) แน่นอนว่าการเปรียบเทียบโดยไม่มีการแปลงย่อมเป็นเช่นนั้น

x > 0.0f

ซึ่งทำเช่นเดียวกัน

float Zero = 0.0; // double 0.0 converted to float  
x > Zero

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


1

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

ฉันไม่เห็นเหตุผลอื่นใดว่าทำไมจึงZeroมีการประกาศค่าคงที่ที่นี่ มันเป็นแค่สไตล์การเขียนโค้ดและมันจะดีกว่าถ้าคุณใช้สไตล์นี้ในทุกที่ในโปรแกรมนั้น ๆ


1

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

สำหรับคนที่อ่านได้ง่ายกว่าx > 0... โปรดจำไว้ว่ามีคนที่จริงใจอย่างแท้จริงคิดว่า COBOL เป็นความคิดที่ดีและมีความสุขที่ได้ทำงานด้วย - แล้วมีคนที่คิดเหมือนกันเกี่ยวกับ C. (ข่าวลือมี มันมีโปรแกรมเมอร์บางคนที่มีความคิดเห็นแบบเดียวกันเกี่ยวกับ C ++!) ในคำอื่น ๆ คุณจะไม่ได้รับข้อตกลงทั่วไปในจุดนี้และอาจไม่คุ้มค่าที่จะต่อสู้


0

[คือ] สิ่งนี้มีประสิทธิภาพมากขึ้นหรือสามารถอ่านได้หรือสามารถบำรุงรักษาได้มากกว่า:

if (x > 0.0) {..}

หากคุณกำลังเขียนโค้ดทั่วไป (เช่นไม่เฉพาะเจาะจง) อาจเป็นไปได้มาก zero()ฟังก์ชั่นสามารถนำไปใช้ประเภทพีชคณิตใด ๆ หรือประเภทใด ๆ ที่เป็นยังกลุ่ม WRT มันอาจจะเป็นจำนวนเต็มมันอาจเป็นค่าจุดลอยตัวมันอาจจะเป็นฟังก์ชั่นถ้าตัวแปรของคุณคือพูดตัวเองฟังก์ชั่นภายในพื้นที่เชิงเส้นบางส่วน (เช่น x เป็นฟังก์ชันเชิงเส้นของรูปแบบ z -> a_x * z + b_x) จากนั้นzero()จัดเตรียมฟังก์ชันด้วย a และ b ทั้งคู่เป็นzero()ประเภทพื้นฐาน

ดังนั้นคุณจะคาดหวังว่ารหัสดังกล่าวเป็นไปได้ว่า C ++ อาจเป็นไปได้ (แม้ว่าzero()AFAIK จะไม่ธรรมดามาก) หรือใน Julia และภาษาอื่น ๆ

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