Làm cách nào để xử lý chia cho số 0 trong một ngôn ngữ không hỗ trợ các ngoại lệ?


62

Tôi đang trong giai đoạn phát triển một ngôn ngữ lập trình mới để giải quyết một số yêu cầu kinh doanh và ngôn ngữ này được nhắm đến người dùng mới làm quen. Vì vậy, không có hỗ trợ cho việc xử lý ngoại lệ trong ngôn ngữ và tôi sẽ không mong đợi họ sử dụng nó ngay cả khi tôi đã thêm nó.

Tôi đã đạt đến điểm mà tôi phải triển khai toán tử chia và tôi tự hỏi làm thế nào để xử lý tốt nhất một phép chia bằng 0?

Tôi dường như chỉ có ba cách có thể để xử lý trường hợp này.

  1. Bỏ qua lỗi và tạo ra 0kết quả. Ghi nhật ký cảnh báo nếu có thể.
  2. Thêm NaNmột giá trị có thể cho các số, nhưng điều đó đặt ra câu hỏi về cách xử lý NaNcác giá trị trong các lĩnh vực khác của ngôn ngữ.
  3. Chấm dứt việc thực hiện chương trình và báo cáo cho người dùng một lỗi nghiêm trọng đã xảy ra.

Lựa chọn số 1 dường như là giải pháp hợp lý duy nhất. Tùy chọn # 3 không thực tế vì ngôn ngữ này sẽ được sử dụng để chạy logic dưới dạng cron hàng đêm.

Các lựa chọn thay thế của tôi để xử lý một phép chia cho sai số 0 là gì và những rủi ro khi đi với tùy chọn # 1.


12
nếu bạn đã thêm hỗ trợ ngoại lệ và người dùng đã không bắt được thì bạn sẽ có tùy chọn # 3
ratchet freak

82
Tôi tò mò, loại yêu cầu ngu ngốc nào sẽ yêu cầu bạn tạo ra một ngôn ngữ lập trình hoàn toàn mới? Theo kinh nghiệm của tôi, mọi ngôn ngữ từng được tạo ra (trong thiết kế hoặc thực thi, thường là cả hai) và phải mất rất nhiều nỗ lực để có được số tiền đó . Có một vài trường hợp ngoại lệ cho lần đầu tiên, nhưng không phải lần thứ hai và vì chúng dễ dàng <0,01% các trường hợp, có lẽ chúng là lỗi đo lường ;-)

16
@delnan hầu hết các ngôn ngữ mới được tạo ra để cho phép tách biệt các quy tắc kinh doanh khỏi cách chúng được thực thi. Người dùng không cần biết cách reject "Foo"triển khai, mà đơn giản là nó từ chối một tài liệu nếu nó chứa từ khóa Foo. Tôi cố gắng làm cho ngôn ngữ trở nên dễ đọc bằng các thuật ngữ mà người dùng quen thuộc. Cung cấp cho người dùng ngôn ngữ lập trình riêng của họ cho phép họ thêm các quy tắc kinh doanh mà không phụ thuộc vào nhân viên kỹ thuật.
Phản ứng

19
@Mathew Foscarini. Không bao giờ, bỏ qua lỗi và âm thầm trả về 0. Khi thực hiện phép chia, 0 có thể là một giá trị pháp lý hoàn hảo (vì một lý do nào đó, có một điều như vậy trong Power Basic, và nó thực sự là một nỗi đau). Nếu bạn chia số dấu phẩy động, Nan hoặc Inf sẽ rất tuyệt (hãy xem qua IEEE 754 để hiểu lý do tại sao). Nếu bạn chia số nguyên, bạn có thể dừng chương trình, chia cho 0 không bao giờ được phép (tốt, trừ khi bạn muốn triển khai một hệ thống ngoại lệ thực sự).

16
Tôi thích thú và bị mê hoặc bởi một phức hợp lĩnh vực kinh doanh đủ để biện minh cho một ngôn ngữ lập trình hoàn chỉnh, độc quyền nhưng đủ lỏng lẻo để chịu đựng những kết quả không chính xác.
Đánh dấu E. Haase

Câu trả lời:


98

Tôi thực sự khuyên bạn nên chống lại # 1, bởi vì chỉ cần bỏ qua lỗi là một mô hình chống nguy hiểm. Nó có thể dẫn đến các lỗi khó phân tích. Đặt kết quả của phép chia từ 0 thành 0 sẽ không có ý nghĩa gì và việc tiếp tục thực hiện chương trình với giá trị vô nghĩa sẽ gây rắc rối. Đặc biệt là khi chương trình đang chạy không giám sát. Khi trình thông dịch chương trình thông báo rằng có lỗi trong chương trình (và phần chia gần như luôn luôn là lỗi thiết kế), việc hủy bỏ nó và giữ mọi thứ như thường được ưu tiên hơn là lấp đầy cơ sở dữ liệu của bạn.

Ngoài ra, bạn sẽ không thể thành công với việc hoàn toàn làm theo mô hình này thông qua. Sớm hay muộn bạn sẽ gặp phải các tình huống lỗi mà không thể bỏ qua (như hết bộ nhớ hoặc tràn ngăn xếp) và bạn sẽ phải thực hiện một cách để chấm dứt chương trình.

Tùy chọn # 2 (sử dụng NaN) sẽ là một chút công việc, nhưng không nhiều như bạn nghĩ. Cách xử lý NaN trong các tính toán khác nhau được ghi lại rõ ràng trong tiêu chuẩn IEEE 754, do đó bạn có thể chỉ cần làm những gì ngôn ngữ mà trình thông dịch của bạn viết.

Nhân tiện: Tạo một ngôn ngữ lập trình có thể sử dụng được bởi những người không phải là lập trình viên là điều chúng tôi đã cố gắng thực hiện kể từ năm 1964 (Dartmouth BASIC). Cho đến nay, chúng tôi đã không thành công. Nhưng dù sao cũng may mắn.


14
+1 cảm ơn. Bạn đã thuyết phục tôi ném lỗi và bây giờ tôi đọc câu trả lời của bạn, tôi không hiểu tại sao tôi lại do dự. PHPđã ảnh hưởng xấu đến tôi.
Phản ứng

24
Vâng, nó có. Khi tôi đọc câu hỏi của bạn, tôi nghĩ ngay rằng đó là một điều rất kỳ quặc khi tạo ra đầu ra không chính xác và tiếp tục vận chuyển khi đối mặt với lỗi. Có nhiều lý do tại sao PHP là ngoại lệ trong việc này.
Joel

4
+1 cho nhận xét CƠ BẢN. Tôi không khuyên bạn nên sử dụng NaNngôn ngữ của người mới bắt đầu, nhưng nói chung, câu trả lời tuyệt vời.
Ross Patterson

8
@Joel Nếu anh ta sống đủ lâu, Dijkstra có thể đã nói rằng "Việc sử dụng [PHP] làm tê liệt tâm trí; do đó, việc dạy học của nó nên được coi là một hành vi phạm tội."
Ross Patterson

12
@Ross. "Sự kiêu ngạo trong khoa học máy tính được đo bằng nano-Dijkstras" - Alan Kay

33

1 - Bỏ qua lỗi và tạo ra 0kết quả. Ghi nhật ký cảnh báo nếu có thể.

Đó không phải là một ý tưởng tốt. Ở tất cả. Mọi người sẽ bắt đầu tùy thuộc vào nó và nếu bạn sửa nó, bạn sẽ phá vỡ rất nhiều mã.

2 - Thêm NaNdưới dạng giá trị có thể cho các số, nhưng điều đó đặt ra câu hỏi về cách xử lý NaNcác giá trị trong các lĩnh vực khác của ngôn ngữ.

Bạn nên xử lý NaN theo cách thời gian chạy của các ngôn ngữ khác thực hiện: Bất kỳ phép tính nào khác cũng mang lại NaN và mọi so sánh (thậm chí NaN == NaN) đều cho kết quả sai.

Tôi nghĩ rằng điều này là chấp nhận được, nhưng không nhất thiết phải thân thiện với người mới.

3 - Chấm dứt việc thực hiện chương trình và báo cáo cho người dùng xảy ra lỗi nghiêm trọng.

Đây là giải pháp tốt nhất tôi nghĩ. Với thông tin đó trong tay, người dùng sẽ có thể xử lý 0. Bạn nên cung cấp môi trường thử nghiệm, đặc biệt nếu nó dự định chạy một lần một đêm.

Ngoài ra còn có một lựa chọn thứ tư. Làm cho phân chia một hoạt động ternary. Bất kỳ hai trong số này sẽ làm việc:

  • div (tử số, số đếm, thay thế_result)
  • div (tử số, số đếm, thay thế_denumerator)

Nhưng nếu bạn thực hiện NaN == NaNđược false, sau đó bạn sẽ có thêm một isNaN()chức năng để người dùng có thể phát hiện NaNs.
AJMansfield

2
@AJMansfield: Hoặc là, hoặc mọi người tự thực hiện nó : isNan(x) => x != x. Tuy nhiên, khi bạn NaNxuất hiện trong mã lập trình của mình, bạn không nên bắt đầu thêm isNaNséc, mà nên theo dõi nguyên nhân và thực hiện các kiểm tra cần thiết ở đó. Do đó, điều quan trọng là NaNđể tuyên truyền đầy đủ.
back2dos

5
NaNs chủ yếu là phản trực giác. Trong ngôn ngữ của người mới bắt đầu, họ đã chết khi đến nơi.
Ross Patterson

2
@RossPatterson Nhưng một người mới bắt đầu có thể dễ dàng nói 1/0- bạn phải làm gì đó với nó. Không có kết quả hữu ích nào khác ngoài Infhoặc NaN- một cái gì đó sẽ lan truyền lỗi hơn nữa vào chương trình. Nếu không, giải pháp duy nhất là dừng lại với một lỗi tại thời điểm này.
Đánh dấu Hurd

1
Tùy chọn 4 có thể được cải thiện bằng cách cho phép gọi hàm, do đó có thể thực hiện bất kỳ hành động nào là cần thiết để khôi phục từ ước số 0 bất ngờ.
CyberFonic

21

Chấm dứt ứng dụng đang chạy với định kiến ​​cực đoan. (Trong khi cung cấp thông tin gỡ lỗi đầy đủ)

Sau đó, hướng dẫn người dùng của bạn xác định và xử lý các điều kiện trong đó số chia có thể bằng 0 (giá trị nhập của người dùng, v.v.)


13

Trong Haskell (và tương tự trong Scala), thay vì ném ngoại lệ (hoặc trả về tham chiếu null), các loại trình bao bọc MaybeEithercó thể được sử dụng. Với Maybengười dùng có cơ hội kiểm tra xem giá trị anh ta nhận được có "trống" hay không, hoặc anh ta có thể cung cấp một giá trị mặc định khi "hủy ghép nối". Eitherlà tương tự, nhưng có thể được sử dụng trả về một đối tượng (ví dụ: chuỗi lỗi) mô tả sự cố nếu có.


1
Đúng, nhưng lưu ý rằng Haskell không sử dụng số này để chia cho số không. Thay vào đó, mọi loại Haskell đều có "đáy" là một giá trị có thể. Điều này không giống như con trỏ null theo nghĩa là "giá trị" của biểu thức không kết thúc. Dĩ nhiên, bạn không thể kiểm tra độ trễ như một giá trị, nhưng trong ngữ nghĩa hoạt động, các trường hợp không kết thúc là một phần của ý nghĩa của biểu thức. Trong Haskell, giá trị "dưới cùng" đó cũng xử lý các kết quả trường hợp lỗi bổ sung như error "some message"hàm được đánh giá.
Steve314

Cá nhân, nếu hiệu ứng hủy bỏ toàn bộ chương trình được coi là hợp lệ, tôi không biết tại sao mã thuần không thể có tác dụng ném ngoại lệ, nhưng đó chỉ là tôi - Haskellkhông cho phép các biểu thức thuần túy ném ngoại lệ.
Steve314

Tôi nghĩ rằng đây là một ý tưởng tốt vì ngoài việc đưa ra một ngoại lệ, tất cả các tùy chọn được đề xuất không liên lạc với người dùng rằng họ đã phạm sai lầm. Ý tưởng cơ bản là người dùng mắc lỗi với giá trị họ đã cung cấp cho chương trình, vì vậy chương trình nên thông báo cho người dùng rằng họ đã nhập sai (khi đó người dùng có thể nghĩ ra cách khắc phục). Không nói cho người dùng về sai lầm của họ, bất kỳ giải pháp nào cũng cảm thấy rất kỳ quặc.
Được thông báo vào

Tôi nghĩ rằng đây là cách để đi ... Ngôn ngữ lập trình Rust sử dụng nó rộng rãi trong thư viện tiêu chuẩn của nó.
aochagavia

12

Các câu trả lời khác đã được coi là giá trị tương đối của ý tưởng của bạn. Tôi đề xuất một cách khác: sử dụng phân tích dòng cơ bản để xác định xem một biến thể bằng không. Sau đó, bạn chỉ có thể không cho phép phân chia theo các biến có khả năng bằng không.

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.

Ngoài ra, có một chức năng khẳng định thông minh thiết lập các bất biến:

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

Điều này cũng tốt như ném lỗi thời gian chạy. Bạn hoàn toàn không xác định được các hoạt động không xác định hoàn toàn, nhưng có một lợi thế là đường dẫn mã thậm chí không bị ảnh hưởng cho lỗi không thể bị lộ. Nó có thể được thực hiện giống như đánh máy thông thường, bằng cách đánh giá tất cả các nhánh của chương trình với các môi trường gõ lồng nhau để theo dõi và xác minh các bất biến:

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

Hơn nữa, nó mở rộng tự nhiên đến phạm vi và nullkiểm tra, nếu ngôn ngữ của bạn có các tính năng như vậy.


4
Ý tưởng gọn gàng, nhưng loại giải quyết ràng buộc này là NP-Complete. Hãy tưởng tượng một cái gì đó như thế nào def foo(a,b): return a / ord(sha1(b)[0]). Máy phân tích tĩnh không thể đảo ngược SHA-1. Clang có loại phân tích tĩnh này và rất tuyệt vời để tìm ra các lỗi nông nhưng có rất nhiều trường hợp không thể xử lý được.
Đánh dấu E. Haase

9
Đây không phải là NP-đầy đủ, điều này là không thể - nói bổ đề tạm dừng. Tuy nhiên, bộ phân tích tĩnh không cần phải giải quyết vấn đề này, nó chỉ có thể đưa ra một tuyên bố như thế này và yêu cầu bạn thêm một xác nhận hoặc trang trí rõ ràng.
MK01

1
@ MK01: Nói cách khác, phân tích là một người bảo thủ.
Jon Purdy

11

Số 1 (chèn số 0 không thể truy cập) luôn luôn xấu. Sự lựa chọn giữa # 2 (tuyên truyền NaN) và # 3 (tiêu diệt quá trình) phụ thuộc vào bối cảnh và lý tưởng nên là một thiết lập toàn cầu, như trong Numpy.

Nếu bạn đang thực hiện một phép tính lớn, tích hợp, tuyên truyền NaN là một ý tưởng tồi vì cuối cùng nó sẽ lan rộng và lây nhiễm toàn bộ tính toán của bạn --- khi bạn nhìn vào kết quả vào buổi sáng và thấy rằng tất cả đều là NaN, bạn ' d phải ném ra kết quả và bắt đầu lại dù thế nào đi nữa. Sẽ tốt hơn nếu chương trình kết thúc, bạn nhận được một cuộc gọi vào giữa đêm và sửa nó --- ít nhất là về số giờ lãng phí, ít nhất là.

Nếu bạn đang thực hiện nhiều phép tính nhỏ, chủ yếu là độc lập (như tính toán giảm bản đồ hoặc song song lúng túng) và bạn có thể chấp nhận một số phần trăm trong số chúng không thể sử dụng được do NaN, thì đó có lẽ là lựa chọn tốt nhất. Việc chấm dứt chương trình và không thực hiện 99% sẽ tốt và hữu ích với tài khoản 1% không đúng định dạng và chia cho số 0 có thể là một sai lầm.

Một tùy chọn khác, liên quan đến NaN: cùng thông số kỹ thuật dấu phẩy động của IEEE xác định Inf và -Inf và chúng được truyền bá khác với NaN. Chẳng hạn, tôi khá chắc chắn rằng Inf> bất kỳ số nào và -Inf <bất kỳ số nào, đó sẽ là những gì bạn muốn nếu phép chia của bạn bằng 0 xảy ra vì số 0 chỉ là một số nhỏ. Nếu đầu vào của bạn được làm tròn và bị lỗi về lỗi (như các phép đo vật lý được thực hiện bằng tay), sự khác biệt của hai số lượng lớn có thể dẫn đến không. Nếu không có số chia cho 0, bạn sẽ có được một số lượng lớn và có thể bạn không quan tâm nó lớn đến mức nào. Trong trường hợp đó, In và -Inf là kết quả hợp lệ hoàn toàn.

Nó cũng có thể chính xác, --- chỉ cần nói rằng bạn đang làm việc trong các thực tế mở rộng.


Nhưng chúng tôi không thể biết liệu mẫu số được dự định là dương hay âm, vì vậy sự phân chia có thể mang lại + inf khi -inf được mong muốn, hoặc ngược lại.
Daniel Lubarov

Đúng, lỗi đo lường của bạn quá nhỏ để phân biệt giữa + inf và -inf. Điều này gần giống nhất với hình cầu Riemann, trong đó toàn bộ mặt phẳng phức được ánh xạ tới một quả bóng với chính xác một điểm vô hạn (điểm đối diện với gốc tọa độ). Các số dương rất lớn, các số âm rất lớn và thậm chí các số phức và tưởng tượng rất lớn đều gần với một điểm vô hạn đó. Với một chút lỗi đo lường, bạn không thể phân biệt chúng.
Jim Pivarski

Nếu bạn đang làm việc trong loại hệ thống đó, bạn phải xác định + inf và -inf là tương đương, giống như bạn phải xác định +0 và -0 là tương đương, mặc dù chúng có các biểu diễn nhị phân khác nhau.
Jim Pivarski

8

3. Chấm dứt việc thực hiện chương trình và báo cáo cho người dùng một lỗi nghiêm trọng đã xảy ra.

[Tùy chọn này] không thực tế

Tất nhiên đó là thực tế: Trách nhiệm của các lập trình viên là viết một chương trình thực sự có ý nghĩa. Chia cho 0 không có nghĩa gì cả. Do đó, nếu lập trình viên đang thực hiện phép chia, thì trách nhiệm của họ là phải xác minh trước rằng số chia không bằng 0. Nếu lập trình viên không thực hiện kiểm tra xác thực đó, thì họ nên nhận ra lỗi đó ngay khi có thể, và kết quả tính toán không chuẩn hóa (NaN) hoặc không chính xác (0) chỉ đơn giản là không giúp ích về mặt đó.

Tùy chọn 3 tình cờ là lựa chọn mà tôi muốn giới thiệu cho bạn, btw, vì là cách đơn giản nhất, trung thực và đúng đắn nhất về mặt toán học.


4

Nó có vẻ như là một ý tưởng tồi đối với tôi để chạy các nhiệm vụ quan trọng (ví dụ: "cron nightly") trong một môi trường mà các lỗi bị bỏ qua. Đó là một ý tưởng khủng khiếp để làm cho tính năng này. Điều này quy định các tùy chọn 1 và 2.

Phương án 3 là giải pháp duy nhất được chấp nhận. Các ngoại lệ không phải là một phần của ngôn ngữ, nhưng chúng là một phần của thực tế. Thông báo chấm dứt của bạn phải càng cụ thể và nhiều thông tin càng tốt về lỗi.


3

IEEE 754 thực sự có một giải pháp được xác định rõ ràng cho vấn đề của bạn. Xử lý ngoại lệ mà không sử dụng exceptions http://en.wikipedia.org/wiki/IEEE_floating_point#Exception_handling

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

cách này tất cả các hoạt động của bạn làm cho ý nghĩa toán học.

\ lim_ {x \ đến 0} 1 / x = Inf

Theo ý kiến ​​của tôi, theo IEEE 754 có ý nghĩa nhất vì nó đảm bảo rằng các tính toán của bạn chính xác như trên máy tính và bạn cũng phù hợp với hành vi của các ngôn ngữ lập trình khác.

Vấn đề duy nhất nảy sinh là Inf và NaN sẽ làm ô nhiễm kết quả của bạn và người dùng của bạn sẽ không biết chính xác vấn đề đến từ đâu. Hãy xem một ngôn ngữ như Julia làm điều này khá tốt.

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

Lỗi phân chia được lan truyền chính xác thông qua các hoạt động toán học nhưng cuối cùng, người dùng không nhất thiết phải biết lỗi đó bắt nguồn từ hoạt động nào.

edit:Tôi đã không thấy phần thứ hai của câu trả lời của Jim Pivarski, về cơ bản là những gì tôi đang nói ở trên. Lỗi của tôi.


2

SQL, dễ dàng là ngôn ngữ được sử dụng rộng rãi nhất bởi những người không lập trình, làm # 3, cho bất cứ điều gì đáng giá. Theo kinh nghiệm của tôi khi quan sát và hỗ trợ những người không lập trình viết SQL, hành vi này thường được hiểu rõ và dễ dàng được bù đắp (với một tuyên bố trường hợp hoặc tương tự). Nó giúp thông báo lỗi bạn nhận được có xu hướng khá trực tiếp, ví dụ như trong Postgres 9, bạn nhận được "LRI: chia cho số không".


2

Tôi nghĩ vấn đề là "nhắm vào người dùng mới. -> Vì vậy, không có hỗ trợ cho ..."

Tại sao bạn nghĩ rằng xử lý ngoại lệ là vấn đề đối với người dùng mới làm quen?

Thật là tệ? Có một tính năng "khó" hoặc không biết tại sao điều gì đó xảy ra? Điều gì có thể gây nhầm lẫn nhiều hơn? Sự cố với kết xuất lõi hoặc "Lỗi nghiêm trọng: Chia cho số không"?

Thay vào đó, tôi nghĩ là FAR tốt hơn để nhắm đến các lỗi tin nhắn TUYỆT VỜI. Vì vậy, thay vào đó: "Tính toán sai, Chia 0/0" (nghĩa là: Luôn hiển thị DATA gây ra sự cố, không chỉ là loại vấn đề). Hãy xem cách PostgreSql thực hiện các lỗi thông báo, đó là IMHO tuyệt vời.

Tuy nhiên, bạn có thể xem các cách khác để làm việc với các ngoại lệ như:

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

Tôi cũng có ước mơ xây dựng một ngôn ngữ và trong trường hợp này tôi nghĩ rằng việc trộn Có thể / Tùy chọn với Ngoại lệ thông thường có thể là tốt nhất:

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

Theo quan điểm của tôi, ngôn ngữ của bạn sẽ cung cấp một cơ chế chung để phát hiện và xử lý lỗi. Lỗi lập trình nên được phát hiện tại thời điểm biên dịch (hoặc càng sớm càng tốt) và thông thường sẽ dẫn đến việc chấm dứt chương trình. Các lỗi xuất phát từ dữ liệu bất ngờ hoặc sai sót hoặc do các điều kiện bên ngoài không mong muốn nên được phát hiện và cung cấp cho hành động thích hợp, nhưng cho phép chương trình tiếp tục bất cứ khi nào có thể.

Các hành động hợp lý bao gồm (a) chấm dứt (b) nhắc người dùng về một hành động (c) ghi lại lỗi (d) thay thế một giá trị đã sửa (e) đặt một chỉ báo được kiểm tra trong mã (f) gọi một quy trình xử lý lỗi. Cái nào trong số này bạn có sẵn và bằng phương tiện nào là lựa chọn bạn phải đưa ra.

Theo kinh nghiệm của tôi, các lỗi dữ liệu phổ biến như chuyển đổi bị lỗi, chia cho 0, tràn và giá trị ngoài phạm vi là lành tính và theo mặc định nên được xử lý bằng cách thay thế một giá trị khác và đặt cờ lỗi. (Không phải lập trình viên) sử dụng ngôn ngữ này sẽ thấy dữ liệu bị lỗi và nhanh chóng hiểu được sự cần thiết phải kiểm tra lỗi và xử lý chúng.

[Ví dụ, xem xét một bảng tính Excel. Excel không chấm dứt bảng tính của bạn vì một số bị tràn hoặc bất cứ điều gì. Các tế bào nhận được một giá trị lạ và bạn đi tìm hiểu tại sao và sửa nó.]

Vì vậy, để trả lời câu hỏi của bạn: bạn chắc chắn không nên chấm dứt. Bạn có thể thay thế NaN nhưng bạn không nên làm cho nó hiển thị, chỉ cần đảm bảo phép tính hoàn thành và tạo ra một giá trị cao kỳ lạ. Và đặt cờ lỗi để người dùng cần nó có thể xác định rằng đã xảy ra lỗi.

Tiết lộ: Tôi đã tạo ra một triển khai ngôn ngữ như vậy (Powerflex) và giải quyết chính xác vấn đề này (và nhiều vấn đề khác) trong những năm 1980. Có rất ít hoặc không có tiến bộ về ngôn ngữ cho những người không lập trình trong 20 năm qua hoặc lâu hơn, và bạn sẽ thu hút được vô số lời chỉ trích vì đã cố gắng, nhưng tôi thực sự hy vọng bạn thành công.


1

Tôi thích toán tử ternary nơi bạn cung cấp một giá trị thay thế trong trường hợp bộ khử là 0.

Một ý tưởng nữa tôi không thấy là tạo ra một giá trị "không hợp lệ" chung. Một biến chung "biến này không có giá trị vì chương trình đã làm điều gì đó xấu", mang theo dấu vết ngăn xếp đầy đủ với chính nó. Sau đó, nếu bạn từng sử dụng giá trị đó ở bất cứ đâu, kết quả lại không hợp lệ, với thao tác mới được thử ở trên (nghĩa là nếu giá trị không hợp lệ từng xuất hiện trong một biểu thức, toàn bộ biểu thức sẽ không hợp lệ và không có lệnh gọi hàm nào được thực hiện; là toán tử boolean - đúng hoặc không hợp lệ là đúng và sai và không hợp lệ là sai - cũng có thể có các ngoại lệ khác cho điều đó). Khi giá trị đó không còn được tham chiếu ở bất cứ đâu, bạn đăng nhập một mô tả dài đẹp về toàn bộ chuỗi nơi mọi thứ đã sai và tiếp tục kinh doanh như bình thường. Có thể gửi email dấu vết để dẫn dự án hoặc một cái gì đó.

Một cái gì đó giống như có lẽ đơn nguyên. Nó cũng sẽ hoạt động với bất cứ điều gì khác có thể thất bại, và bạn có thể cho phép mọi người xây dựng thương binh của riêng họ. Và chương trình sẽ tiếp tục chạy miễn là lỗi không quá sâu, đó là điều thực sự muốn ở đây, tôi nghĩ vậy.


1

Có hai lý do cơ bản để chia cho số không.

  1. Trong một mô hình chính xác (như số nguyên), bạn được chia cho DBZ bằng 0 vì đầu vào sai. Đây là loại DBZ mà hầu hết chúng ta nghĩ đến.
  2. Trong mô hình không chính xác (như pt nổi), bạn có thể nhận được DBZ do lỗi làm tròn mặc dù đầu vào là hợp lệ. Đây là những gì chúng ta thường không nghĩ đến.

Đối với 1. bạn phải thông báo lại cho người dùng rằng họ đã phạm sai lầm vì họ là người chịu trách nhiệm và họ là người biết cách khắc phục tình huống tốt nhất.

Đối với 2. Đây không phải là lỗi của người dùng, bạn có thể chỉ tay vào thuật toán, triển khai phần cứng, v.v. nhưng đây không phải là lỗi của người dùng nên bạn không nên chấm dứt chương trình hoặc thậm chí ném ngoại lệ (nếu không được phép trong trường hợp này). Vì vậy, một giải pháp hợp lý là tiếp tục các hoạt động một cách hợp lý.

Tôi có thể thấy người hỏi câu hỏi này được hỏi trong trường hợp 1. Vì vậy, bạn cần liên lạc lại với người dùng. Sử dụng bất kỳ tiêu chuẩn dấu phẩy động nào, Inf, -Inf, Nan, IEEE không phù hợp trong tình huống này. Chiến lược sai về cơ bản.


0

Không cho phép nó trong ngôn ngữ. Điều đó có nghĩa là, không cho phép chia cho một số cho đến khi nó chắc chắn không bằng 0, thường là bằng cách kiểm tra nó trước. I E.

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

Để làm điều này, bạn cần một loại số mới, một số tự nhiên, trái ngược với một số nguyên. Điều đó có thể ... khó khăn ... để giải quyết.
Phục vụ

@Servy: Không, bạn sẽ không. Tại sao bạn? Bạn cần logic trong trình biên dịch để xác định các giá trị có thể, nhưng dù sao bạn cũng muốn điều đó (vì lý do tối ưu hóa).
MSalters

Nếu bạn không có loại khác, một loại bằng 0 và một loại có giá trị khác không, thì bạn sẽ không thể giải quyết vấn đề trong trường hợp chung. Bạn có thể có kết quả dương tính giả và buộc người dùng kiểm tra số 0 thường xuyên hơn mức thực tế hoặc bạn sẽ tạo ra tình huống mà họ vẫn có thể chia cho số không.
Phục vụ

@Servy: Bạn đang nhầm: trình biên dịch có thể theo dõi trạng thái đó mà không cần loại như vậy, và ví dụ GCC đã làm như vậy. Ví dụ, loại C intcho phép giá trị 0, nhưng GCC vẫn có thể xác định vị trí trong mã cụ thể không thể bằng 0.
MSalters

2
Nhưng chỉ trong một số trường hợp nhất định; nó không thể làm như vậy, với độ chính xác 100%, trong mọi trường hợp. Bạn sẽ có dương tính giả hoặc âm tính giả. Điều này là có thể đúng. Ví dụ: tôi có thể tạo một đoạn mã có thể hoặc thậm chí không hoàn thành . Nếu trình biên dịch thậm chí không thể biết nếu nó kết thúc, làm sao nó có thể biết nếu int kết quả là khác không? Nó có thể bắt các trường hợp rõ ràng đơn giản, nhưng không phải tất cả các trường hợp.
Phục vụ

0

Khi bạn viết một ngôn ngữ lập trình, bạn nên tận dụng thực tế và làm cho nó bắt buộc phải bao gồm một hành động cho phát minh bằng trạng thái không. a <= n / c: 0 div-by-zero-action

Tôi biết những gì tôi vừa đề xuất về cơ bản là thêm một 'goto' vào PL của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.