วิธีจัดการหารด้วยศูนย์ในภาษาที่ไม่สนับสนุนข้อยกเว้น?


62

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

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

ฉันมีสามวิธีที่เป็นไปได้ในการจัดการกรณีนี้

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

ตัวเลือก # 1 ดูเหมือนจะเป็นทางออกที่สมเหตุสมผลเท่านั้น ตัวเลือก # 3 ไม่สามารถใช้งานได้เนื่องจากภาษานี้จะใช้ในการเรียกใช้ตรรกะเป็น cron ทุกคืน

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


12
หากคุณเพิ่มการสนับสนุนข้อยกเว้นและผู้ใช้ไม่ได้จับมันแล้วคุณจะมีตัวเลือก # 3
ประหลาดวงล้อ

82
ฉันอยากรู้อยากเห็นความต้องการโง่ ๆ แบบไหนที่ทำให้คุณต้องสร้างภาษาการเขียนโปรแกรมใหม่ทั้งหมด? จากประสบการณ์ของฉันทุกภาษาที่สร้างขึ้นมาดูด (ในการออกแบบหรือในการดำเนินการบ่อยครั้งในทั้งสอง) และมันใช้ความพยายามอย่างไม่มีเหตุผลที่จะได้รับมากว่า มีข้อยกเว้นบางประการสำหรับข้อแรก แต่ไม่ใช่ข้อที่สองและเนื่องจากเป็นเรื่องง่าย <0.01% ของคดีพวกเขาอาจวัดข้อผิดพลาดได้ ;-)

16
@delnan ภาษาใหม่ส่วนใหญ่ถูกสร้างขึ้นเพื่ออนุญาตให้แยกกฎธุรกิจออกจากวิธีการนำไปใช้ ผู้ใช้ไม่จำเป็นต้องรู้วิธีreject "Foo"การใช้งาน Fooแต่เพียงว่ามันปฏิเสธเอกสารถ้ามีคำหลัก ฉันพยายามทำให้ภาษาเป็นเรื่องง่ายที่จะอ่านโดยใช้คำศัพท์ที่ผู้ใช้คุ้นเคย การให้ผู้ใช้ภาษาโปรแกรมของพวกเขาเองนั้นให้อำนาจพวกเขาในการเพิ่มกฎเกณฑ์ทางธุรกิจโดยไม่ต้องพึ่งพาเจ้าหน้าที่ด้านเทคนิค
ซ้ำ

19
@Mathew Foscarini ไม่เคยเพิกเฉยต่อข้อผิดพลาดและส่งคืน 0 อย่างเงียบ ๆ เมื่อทำการแบ่ง 0 อาจเป็นค่าทางกฎหมายที่สมบูรณ์แบบ (ด้วยเหตุผลบางอย่างมีสิ่งนั้นอยู่ใน Power Basic และมันเจ็บปวดจริงๆ) หากคุณ divie เลขทศนิยม, น่านหรือ Inf จะดี (ดูที่IEEE 754เพื่อทำความเข้าใจว่าทำไม) หากคุณหารจำนวนเต็มคุณอาจหยุดโปรแกรมไม่ควรอนุญาตให้หารด้วย 0 (ดียกเว้นว่าคุณต้องการใช้ระบบยกเว้นจริง)

16
ฉันสนุกและหลงใหลในโดเมนธุรกิจที่ซับซ้อนพอที่จะพิสูจน์ความเป็นเจ้าของภาษาการเขียนโปรแกรมทัวริงที่สมบูรณ์ แต่หละหลวมพอที่จะทนต่อผลลัพธ์ที่ไม่ถูกต้องอย่างมาก
Mark E. Haase

คำตอบ:


98

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

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

ตัวเลือก # 2 (ใช้ NaN) อาจจะใช้งานได้บ้าง แต่ก็ไม่มากเท่าที่คุณคิด วิธีจัดการกับ NaN ในการคำนวณที่แตกต่างกันนั้นได้รับการบันทึกไว้เป็นอย่างดีในมาตรฐาน IEEE 754 ดังนั้นคุณจึงสามารถทำในสิ่งที่ภาษาที่นักแปลของคุณเขียนนั้นทำได้

โดยวิธีการ:การสร้างภาษาการเขียนโปรแกรมใช้งานได้โดยไม่ใช่โปรแกรมเมอร์เป็นสิ่งที่เราได้พยายามทำตั้งแต่ปี 1964 (Dartmouth BASIC) จนถึงตอนนี้เราไม่ประสบความสำเร็จ แต่ขอให้โชคดี


14
+1 คุณทำให้ฉันโน้มน้าวให้เกิดข้อผิดพลาดและตอนนี้ฉันอ่านคำตอบของคุณแล้วฉันไม่เข้าใจว่าทำไมฉันถึงลังเล PHPได้รับอิทธิพลที่ไม่ดีกับฉัน
ซ้ำ

24
ใช่มันมี เมื่อฉันอ่านคำถามของคุณฉันก็คิดทันทีว่ามันเป็นสิ่งที่ PHP-esque มากในการสร้างผลลัพธ์ที่ไม่ถูกต้อง มีเหตุผลที่ดีว่าทำไม PHP จึงเป็นข้อยกเว้นในการทำเช่นนี้
Joel

4
+1 สำหรับความคิดเห็นพื้นฐาน ฉันไม่แนะนำให้ใช้NaNในภาษาของผู้เริ่มต้น แต่โดยทั่วไปแล้วคำตอบที่ดี
Ross Patterson

8
@Joel ถ้าเขามีชีวิตอยู่นานพอ Dijkstra คงพูดว่า "การใช้ [PHP] ทำลายจิตใจ; การสอนของมันจึงควรถูกมองว่าเป็นความผิดทางอาญา"
Ross Patterson

12
@Ross "ความเย่อหยิ่งในวิทยาศาสตร์คอมพิวเตอร์ถูกวัดในนาโน

33

1 - ละเว้นข้อผิดพลาดและสร้าง0ผลลัพธ์ การบันทึกคำเตือนหากเป็นไปได้

นั่นไม่ใช่ความคิดที่ดี เลย ผู้คนจะเริ่มขึ้นอยู่กับมันและหากคุณเคยแก้ไขคุณจะทำลายรหัสจำนวนมาก

2 - เพิ่มNaNเป็นค่าที่เป็นไปได้สำหรับตัวเลข แต่นั่นทำให้เกิดคำถามเกี่ยวกับวิธีจัดการกับNaNค่าในส่วนอื่น ๆ ของภาษา

คุณควรจัดการกับ NaN แบบที่ runtimes ของภาษาอื่นทำ: การคำนวณเพิ่มเติมใด ๆ ก็ให้ผล NaN และทุกการเปรียบเทียบ (แม้แต่ NaN == NaN) ให้ผลเป็นเท็จ

ฉันคิดว่านี่เป็นที่ยอมรับ แต่ไม่จำเป็นต้องเป็นมิตรกับผู้มาใหม่

3 - ยุติการทำงานของโปรแกรมและรายงานไปยังผู้ใช้ว่าเกิดข้อผิดพลาดร้ายแรง

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

นอกจากนี้ยังมีตัวเลือกที่สี่ ทำให้การแบ่งดำเนินการแบบไตรภาค หนึ่งในสองเหล่านี้จะทำงาน:

  • div (ตัวเศษ, ตัวเศษ, Altern_result)
  • div (ตัวเศษ, ตัวส่วน, Altern_denumerator)

แต่ถ้าคุณNaN == NaNเป็นfalseแล้วคุณจะต้องเพิ่มisNaN()ฟังก์ชั่นเพื่อให้ผู้ใช้สามารถตรวจสอบNaNs
AJMansfield

2
@AJMansfield: isNan(x) => x != xทั้งที่หรือคนที่ใช้มันด้วยตัวเอง: ถึงกระนั้นเมื่อคุณNaNเข้ามาในรหัสการเขียนโปรแกรมของคุณคุณไม่ควรเริ่มเพิ่มการisNaNตรวจสอบ แต่ควรติดตามสาเหตุและทำการตรวจสอบที่จำเป็น ดังนั้นจึงเป็นสิ่งสำคัญสำหรับNaNการเผยแพร่อย่างเต็มที่
back2dos

5
NaNs นั้นส่วนใหญ่ตอบโต้ได้ง่าย ในภาษาของผู้เริ่มต้นพวกเขาจะตายเมื่อเดินทางมาถึง
Ross Patterson

2
@ RossPatterson แต่ผู้เริ่มต้นสามารถพูดได้อย่างง่ายดาย1/0- คุณต้องทำอะไรกับมัน ไม่มีผลลัพธ์ที่มีประโยชน์ที่อาจเป็นไปได้นอกจากInfหรือNaN- สิ่งที่จะเผยแพร่ข้อผิดพลาดเพิ่มเติมในโปรแกรม มิฉะนั้นทางออกเดียวคือหยุดด้วยข้อผิดพลาด ณ จุดนี้
Mark Hurd

1
ตัวเลือกที่ 4 สามารถปรับปรุงได้โดยการอนุญาตให้เรียกใช้ฟังก์ชันซึ่งจะสามารถดำเนินการใด ๆ ก็ตามที่จำเป็นในการกู้คืนจากตัวหาร 0 ที่ไม่คาดคิด
CyberFonic

21

ยุติแอปพลิเคชันที่ทำงานอยู่ด้วยอคติที่รุนแรงที่สุด (ในขณะที่ให้ข้อมูลการดีบักที่เพียงพอ)

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


13

ใน Haskell (และคล้ายกันใน Scala) แทนที่จะโยนข้อยกเว้น (หรือคืนค่าการอ้างอิง Null) ชนิดของ wrapper MaybeและEitherสามารถใช้งานได้ ด้วยMaybeผู้ใช้มีโอกาสที่จะทดสอบว่าค่าที่เขาได้รับคือ "ว่างเปล่า" หรือเขาอาจให้ค่าเริ่มต้นเมื่อ "คลาย" Eitherคล้ายกัน แต่สามารถใช้ส่งคืนวัตถุ (เช่นสตริงข้อผิดพลาด) อธิบายปัญหาหากมี


1
จริง แต่โปรดทราบว่า Haskell ไม่ได้ใช้สิ่งนี้เพื่อหารด้วยศูนย์ ทุกประเภท Haskell โดยปริยายมี "bottom" เป็นค่าที่เป็นไปได้แทน นี่ไม่เหมือนตัวชี้ null ในแง่ที่ว่า "ค่า" ของนิพจน์ที่ล้มเหลวในการยกเลิก คุณไม่สามารถทดสอบการกำจัดโดยไม่คำนึงถึงคุณค่า แต่ในความหมายในการปฏิบัติการกรณีที่ล้มเหลวในการยกเลิกเป็นส่วนหนึ่งของความหมายของนิพจน์ ใน Haskell ค่า "bottom" นั้นจะจัดการกับผลลัพธ์ของตัวผิดพลาดเพิ่มเติมเช่นerror "some message"ฟังก์ชันที่ถูกประเมิน
Steve314

โดยส่วนตัวถ้าผลของการยกเลิกโปรแกรมทั้งหมดนั้นถือว่าถูกต้องฉันไม่รู้ว่าทำไมรหัสบริสุทธิ์ไม่สามารถส่งผลให้เกิดHaskellข้อยกเว้นได้
Steve314

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

ฉันคิดว่านี่เป็นวิธีที่จะไป ... ภาษาโปรแกรม Rust ใช้มันอย่างกว้างขวางในไลบรารีมาตรฐานของมัน
aochagavia

12

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

x = ...
y = ...

if y ≠ 0:
  return x / y    // In this block, y is known to be nonzero.
else:
  return x / y    // This, however, is a compile-time error.

นอกจากนี้ยังมีฟังก์ชั่นยืนยันอัจฉริยะที่สร้างค่าคงที่:

x = ...
require x ≠ 0, "Unexpected zero in calculation"
// For the remainder of this scope, x is known to be nonzero.

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

x = ...           // env1 = { x :: int }
y = ...           // env2 = env1 + { y :: int }
if y ≠ 0:         // env3 = env2 + { y ≠ 0 }
  return x / y    // (/) :: (int, int ≠ 0) → int
else:             // env4 = env2 + { y = 0 }
  ...
...               // env5 = env2

นอกจากนี้ยังขยายขอบเขตและnullตรวจสอบอย่างเป็นธรรมชาติหากภาษาของคุณมีคุณสมบัติดังกล่าว


4
แนวคิดที่ดี แต่การแก้ไขข้อ จำกัด ประเภทนี้สมบูรณ์แบบ NP def foo(a,b): return a / ord(sha1(b)[0])ลองจินตนาการถึงสิ่งที่ต้องการ ตัววิเคราะห์แบบสแตติกไม่สามารถสลับกลับ SHA-1 เสียงดังกราวมีการวิเคราะห์แบบคงที่ประเภทนี้และมันยอดเยี่ยมสำหรับการค้นหาข้อบกพร่องตื้น ๆ แต่มีหลายกรณีที่ไม่สามารถจัดการได้
Mark E. Haase

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

1
@ MK01: กล่าวอีกนัยหนึ่งการวิเคราะห์คือ“ อนุรักษ์นิยม”
Jon Purdy

11

หมายเลข 1 (แทรกศูนย์ undebuggable) ไม่ถูกต้องเสมอ ตัวเลือกระหว่าง # 2 (แพร่กระจาย NaN) และ # 3 (ฆ่ากระบวนการ) ขึ้นอยู่กับบริบทและควรเป็นการตั้งค่าระดับโลกตามที่เป็นอยู่ใน Numpy

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

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

ตัวเลือกอื่นที่เกี่ยวข้องกับ NaN: ข้อมูลจำเพาะจุดลอยตัว IEEE เดียวกันกำหนด Inf และ -Inf และสิ่งเหล่านี้จะถูกเผยแพร่ต่างไปจาก NaN ตัวอย่างเช่นฉันค่อนข้างแน่ใจว่า Inf> หมายเลขใด ๆ และ - ใส่ <หมายเลขใด ๆ ซึ่งจะเป็นสิ่งที่คุณต้องการหากการหารด้วยศูนย์เกิดขึ้นเพราะศูนย์ควรจะเป็นจำนวนน้อย หากอินพุตของคุณถูกปัดเศษและเกิดข้อผิดพลาดจากการวัด (เช่นการวัดทางกายภาพด้วยมือ) ความแตกต่างของสองปริมาณมากอาจทำให้มีค่าเป็นศูนย์ หากไม่มีการหารศูนย์คุณจะได้รับจำนวนมากและบางทีคุณอาจไม่สนใจว่ามันจะใหญ่แค่ไหน ในกรณีนั้นทั้งในและ --Inf เป็นผลลัพธ์ที่ถูกต้อง perrfectly

สามารถแก้ไขได้อย่างเป็นทางการเช่นกัน - เพียงแค่บอกว่าคุณกำลังทำงานใน reals แบบขยาย


แต่เราไม่สามารถบอกได้ว่าตัวส่วนมีเจตนาที่จะเป็นบวกหรือลบดังนั้นแผนกอาจให้ผลบวก + inf เมื่อต้องการ -inf หรือไม่ก็ขอวีซ่าในทางกลับกัน
Daniel Lubarov

จริงข้อผิดพลาดการวัดของคุณเล็กเกินไปที่จะแยกแยะระหว่าง + inf และ -inf ซึ่งคล้ายกับทรงกลมรีมันน์มากที่สุดซึ่งระนาบเชิงซ้อนทั้งหมดจะถูกจับคู่กับลูกบอลที่มีจุดอนันต์หนึ่งจุด (จุดที่อยู่ตรงข้ามกับจุดกำเนิด diametrically) ตัวเลขบวกที่มีขนาดใหญ่มากจำนวนลบที่มีขนาดใหญ่มากและตัวเลขจำนวนเชิงซ้อนและจำนวนเชิงซ้อนที่สูงมากล้วนอยู่ใกล้กับจุดอนันต์จุดเดียว ด้วยข้อผิดพลาดการวัดเล็กน้อยคุณจะไม่สามารถแยกแยะได้
จิม Pivarski

หากคุณกำลังทำงานในระบบประเภทนั้นคุณจะต้องระบุ + inf และ -inf ว่าเท่ากันเช่นเดียวกับที่คุณต้องระบุ +0 และ -0 ว่าเทียบเท่ากันแม้ว่าพวกเขาจะมีตัวแทนไบนารีต่างกัน
จิม Pivarski

8

3. ยุติการทำงานของโปรแกรมและรายงานข้อผิดพลาดร้ายแรงที่เกิดขึ้นกับผู้ใช้

[ตัวเลือกนี้] ใช้งานไม่ได้ ...

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

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


4

ดูเหมือนว่าเป็นความคิดที่ไม่ดีที่ฉันจะทำงานที่สำคัญ (เช่น "cron ยามค่ำคืน") ในสภาพแวดล้อมที่มีข้อผิดพลาดจะถูกละเว้น มันเป็นความคิดที่แย่มากที่จะสร้างฟีเจอร์นี้ กฎนี้ออกตัวเลือก 1 และ 2

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


3

IEEE 754 มีวิธีแก้ไขปัญหาของคุณอย่างชัดเจน การจัดการข้อยกเว้นโดยไม่ใช้exceptions http://en.wikipedia.org/wiki/IEEE_floating_point#Exception_handling

1/0  = Inf
-1/0 = -Inf
0/0  = NaN

ด้วยวิธีนี้การดำเนินการทั้งหมดของคุณทำให้รู้สึกทางคณิตศาสตร์

\ lim_ {x \ to 0} 1 / x = Inf

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

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

julia> 1/0
Inf

julia> -1/0
-Inf

julia> 0/0
NaN

julia> a = [1,1,1] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 Inf

julia> sum(a)
Inf

julia> a = [1,1,0] ./ [2,1,0]
3-element Array{Float64,1}:
   0.5
   1.0
 NaN

julia> sum(a)
NaN

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

edit:ฉันไม่เห็นส่วนที่สองของคำตอบโดย Jim Pivarski ซึ่งเป็นสิ่งที่ฉันพูดด้านบน ความผิดฉันเอง.


2

SQL เป็นภาษาที่ใช้กันอย่างแพร่หลายโดยผู้ที่ไม่ใช่โปรแกรมเมอร์ทำอันดับ 3 ในทุกสิ่งที่คุ้มค่า จากประสบการณ์ของฉันในการสังเกตและช่วยเหลือผู้ที่ไม่ใช่โปรแกรมเมอร์ที่เขียน SQL พฤติกรรมนี้มักจะเข้าใจได้ดีและชดเชยได้ง่าย ช่วยให้ข้อความแสดงข้อผิดพลาดที่คุณได้รับค่อนข้างตรงเช่นใน Postgres 9 คุณได้รับ "ข้อผิดพลาด: การหารด้วยศูนย์"


2

ฉันคิดว่าปัญหาคือ "กำหนดเป้าหมายไปที่ผู้ใช้มือใหม่ -> ดังนั้นจึงไม่มีการสนับสนุนสำหรับ ... "

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

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

แต่ฉันคิดว่าดีกว่าที่จะมุ่งไปที่ข้อผิดพลาดที่ดี ดังนั้นให้ทำดังนี้: "การคำนวณที่ไม่ถูกต้องแบ่ง 0/0" (เช่น: แสดงข้อมูลที่ทำให้เกิดปัญหาเสมอไม่ใช่แค่ชนิดของปัญหา) ดูวิธีที่ PostgreSql ทำข้อผิดพลาดของข้อความนั่นเป็น IMHO ที่ยอดเยี่ยม

อย่างไรก็ตามคุณสามารถดูวิธีอื่น ๆ ในการทำงานกับข้อยกเว้นเช่น:

http://dlang.org/exception-safe.html

ฉันยังมีความฝันในการสร้างภาษาและในกรณีนี้ฉันคิดว่าการผสมบางที / ตัวเลือกกับข้อยกเว้นปกติอาจทำได้ดีที่สุด:

def openFile(fileName): File | Exception
    if not(File.Exist(fileName)):
        raise FileNotExist(fileName)
    else:
        return File.Open()

#This cause a exception:

theFile = openFile('not exist')

# But this, not:

theFile | err = openFile('not exist')

1

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

การกระทำที่เป็นไปได้ ได้แก่ (a) ยุติ (b) แจ้งให้ผู้ใช้สำหรับการกระทำ (c) บันทึกข้อผิดพลาด (d) แทนที่ค่าที่ถูกต้อง (e) ตั้งค่าตัวบ่งชี้ที่จะทดสอบในรหัส (f) เรียกใช้รูทีนการจัดการข้อผิดพลาด คุณเลือกใช้ตัวเลือกใดในตัวเลือกเหล่านี้

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

[ตัวอย่างเช่นพิจารณาสเปรดชีต Excel Excel จะไม่ยุติสเปรดชีตของคุณเนื่องจากมีตัวเลขล้นหรืออะไรก็ตาม เซลล์ได้รับค่าแปลกและคุณไปหาสาเหตุและแก้ไข]

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

การเปิดเผยข้อมูล: ฉันสร้างเพียงการใช้ภาษา (Powerflex) และแก้ไขปัญหานี้ (และอื่น ๆ อีกมากมาย) ในทศวรรษ 1980 ในช่วง 20 ปีที่ผ่านมามีความก้าวหน้าทางภาษาเพียงเล็กน้อยหรือไม่มีเลยสำหรับนักเขียนโปรแกรมและคุณจะได้รับการวิพากษ์วิจารณ์จากความพยายามมากมาย แต่ฉันหวังว่าคุณจะประสบความสำเร็จ


1

ฉันชอบผู้ประกอบการที่คุณให้ค่าทางเลือกในกรณีที่ตัวส่วนเป็น 0

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

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


1

มีสองเหตุผลพื้นฐานสำหรับการหารด้วยศูนย์

  1. ในรูปแบบที่แม่นยำ (เช่นจำนวนเต็ม) คุณจะได้หารด้วยศูนย์ DBZ เพราะอินพุตไม่ถูกต้อง นี่คือ DBZ ชนิดที่พวกเราส่วนใหญ่นึกถึง
  2. ในรูปแบบที่ไม่แม่นยำ (เช่น floating pt) คุณอาจได้รับ DBZ เนื่องจากการปัดเศษ - ข้อผิดพลาดแม้ว่าอินพุตจะถูกต้อง นี่คือสิ่งที่ปกติเราไม่คิด

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

สำหรับ 2 นี่ไม่ใช่ความผิดของผู้ใช้คุณสามารถใช้นิ้วชี้ไปที่อัลกอริทึมการใช้งานฮาร์ดแวร์ ฯลฯ แต่นี่ไม่ใช่ความผิดของผู้ใช้ดังนั้นคุณไม่ควรยกเลิกโปรแกรมหรือแม้แต่โยนข้อยกเว้น (ถ้าไม่ได้รับอนุญาต ดังนั้นทางออกที่สมเหตุสมผลคือการดำเนินการต่อในลักษณะที่สมเหตุสมผล

ฉันเห็นคนที่ถามคำถามนี้ถามถึงกรณีที่ 1 ดังนั้นคุณต้องติดต่อกลับไปยังผู้ใช้ การใช้มาตรฐานจุดลอยตัวใดก็ตาม Inf, -Inf, Nan, IEEE ไม่เหมาะกับสถานการณ์นี้ กลยุทธ์ที่ผิดขั้นพื้นฐาน


0

ไม่อนุญาตเป็นภาษา กล่าวคือไม่อนุญาตให้หารด้วยตัวเลขจนกว่าจะพิสูจน์ได้ว่าไม่เป็นศูนย์โดยปกติจะทำการทดสอบก่อน กล่าวคือ

int div = random(0,100);
int b = 10000 / div; // Error E0000: div might be zero

ในการทำเช่นนี้คุณต้องมีประเภทตัวเลขใหม่จำนวนธรรมชาติซึ่งตรงข้ามกับจำนวนเต็ม นั่นอาจเป็น ... ยาก ... ที่จะจัดการกับ
Servy

@Servy: ไม่คุณไม่ต้องการ ทำไมคุณ คุณต้องการตรรกะในคอมไพเลอร์เพื่อกำหนดค่าที่เป็นไปได้ แต่คุณต้องการสิ่งนั้นอยู่ดี (เพื่อเหตุผลที่ดีที่สุด)
MSalters

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

@Servy: คุณเข้าใจผิด: คอมไพเลอร์สามารถติดตามสถานะนั้นโดยไม่จำเป็นต้องมีประเภทดังกล่าวและเช่น GCC ได้ทำเช่นนั้น เช่นประเภท C intอนุญาตให้มีค่าเป็นศูนย์ แต่ GCC ยังคงสามารถระบุได้ว่าใน ints รหัสเฉพาะไม่สามารถเป็นศูนย์
MSalters

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

0

ในขณะที่คุณเขียนภาษาโปรแกรมคุณควรใช้ประโยชน์จากข้อเท็จจริงและทำให้จำเป็นต้องมีการดำเนินการสำหรับการประดิษฐ์โดยไม่มีศูนย์ a <= n / c: 0 div-by-zero-action

ฉันรู้ว่าสิ่งที่ฉันเพิ่งแนะนำคือการเพิ่ม 'goto' ลงใน PL ของคุณ

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