2011-01-05 30 views
34

Hãy xem xét đoạn mã sau và nhận xét:Sự mâu thuẫn trong chia-by-zero hành vi giữa các loại giá trị khác nhau

Console.WriteLine(1/0); // will not compile, error: Division by constant zero 

int i = 0; 
Console.WriteLine(1/i); // compiles, runs, throws: DivideByZeroException 

double d = 0; 
Console.WriteLine(1/d); // compiles, runs, results in: Infinity 

tôi có thể hiểu trình biên dịch tích cực kiểm tra cho phép chia cho không liên tục và DivideByZeroException khi chạy nhưng:

Tại sao phải sử dụng giá trị tăng gấp đôi theo từng số không, thay vì ném ngoại lệ? Đây có phải là do thiết kế hay là một lỗi?

Chỉ cần cho đá, tôi đã làm điều này trong VB.NET là tốt, với "phù hợp hơn" Kết quả:

dim d as double = 0.0 
Console.WriteLine(1/d) ' compiles, runs, results in: Infinity 

dim i as Integer = 0 
Console.WriteLine(1/i) ' compiles, runs, results in: Infinity 

Console.WriteLine(1/0) ' compiles, runs, results in: Infinity 

EDIT:

Dựa trên phản hồi kekekela của tôi chạy sau đó dẫn trong vô cùng:

Console.WriteLine(1/
    .0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); 

Thử nghiệm này dường như chứng thực ý tưởng và một đôi chữ nghĩa là 0.0 thực sự là một phần rất, rất nhỏ mà sẽ dẫn đến vô cùng ...

+3

Đây là bài viết của tôi về chủ đề: http://blogs.msdn.com/b/ericlippert/archive/2009/10/15/as-timeless-as-infinity.aspx –

+0

@EricLippert cool! – Jalal

Trả lời

32

Tóm lại: loại double xác định giá trị cho vô cùng trong khi loại int thì không. Vì vậy, trong trường hợp double, kết quả của phép tính là giá trị mà bạn thực sự có thể thể hiện trong loại đã cho vì nó được xác định. Trong trường hợp int, không có giá trị cho vô cùng và do đó không có cách nào để trả lại kết quả chính xác. Do đó ngoại lệ.

VB.NET thực hiện mọi việc hơi khác một chút; phân chia số nguyên tự động dẫn đến giá trị dấu phẩy động bằng toán tử /. Điều này là để cho phép các nhà phát triển viết, ví dụ: biểu thức 1/2 và đánh giá nó là 0.5, một số sẽ xem xét trực quan. Nếu bạn muốn nhìn thấy hành vi phù hợp với C#, hãy thử này:

Console.WriteLine(1 \ 0) 

Lưu ý việc sử dụng các nguyên phận điều hành (\, không /) ở trên. Tôi tin rằng bạn sẽ nhận được một ngoại lệ (hoặc một lỗi biên dịch - không chắc chắn).

Tương tự như vậy, hãy thử này:

Dim x As Object = 1/0 
Console.WriteLine(x.GetType()) 

Đoạn mã trên sẽ ra System.Double.

Đối với quan điểm về sự thiếu chính xác, đây là một cách khác để xem xét nó. Nó không phải là loại double không có giá trị cho chính xác bằng không (nó có); thay vào đó, loại double không có nghĩa là cung cấp kết quả chính xác về mặt toán học ngay từ đầu. Sau khi tất cả, giá trị của biểu thức toán học1/0 không được xác định (tôi đã kiểm tra lần cuối). Nhưng 1/x tiếp cận vô cùng khi x tiến tới 0. Vì vậy, từ quan điểm này, nếu chúng tôi không thể đại diện cho hầu hết các phân số n/mchính xác, điều đó có ý nghĩa để coi trường hợp x/0 là gần đúng và cung cấp giá trị cho nó cách tiếp cận - ít nhất.

+1

Mặc dù rất hữu ích, câu trả lời này cũng hơi sai. Câu trả lời là sai bởi vì chia cho số không phải là vô cùng - nó không được xác định về mặt toán học. Câu trả lời thực sự là đôi không phải là số thực (R) như được chỉ ra trong phần sau của câu trả lời này. Chúng là các số dấu phẩy động, và OP đang cố áp dụng lý luận số thực vào thứ gì đó không phải là số thực. Chúng xuất hiện tương tự rất nhiều thời gian vì chúng được thiết kế giống nhau, nhưng về cơ bản chúng khác nhau. Nhân đôi xác định một cái gì đó gọi là 'NaN'," Không phải là số ", (tiếp theo ...) – AnorZaken

+1

(tiếp ...) sẽ là một kết quả" toán học "chính xác hơn nếu thực tế là số thực. Tuy nhiên, nó không trả lại NaN. Lý do là gần như được mô tả trong câu trả lời: bởi vì trong logic dấu chấm động, bạn chủ yếu giả định rằng "0.0" là một số rất nhỏ. Tuy nhiên, bạn không thể nói rằng nó là một số nhỏ, cho biết rằng _is_ và đại diện chính xác của số không là gây hiểu nhầm, bởi vì không có ánh xạ từ 1 đến 1 từ float đến R. Thay vào đó mỗi bản đồ giá trị float đến một phạm vi thực -giá trị, và phao "0.0" bao gồm cả _actual_ zero _and_ một loạt các giá trị nhỏ khác. – AnorZaken

+0

Một ví dụ nữa về dấu phẩy động cơ bản khác với số thực là các số phao xác định "-0.0" (số âm), và có OP chia cho kết quả sẽ là 'Phủ định cực '. Tuy nhiên, những gì bạn nghĩ rằng '0.0 == -0.0' đánh giá? – AnorZaken

7

Một đôi là một số dấu chấm động và không phải là giá trị chính xác, vì vậy những gì bạn đang thực sự chia theo quan điểm của trình biên dịch là một cái gì đó gần bằng không, nhưng không chính xác số không.

+1

Trên thực tế, đôi có một đại diện của một giá trị đó là chính xác bằng không. Lý do thực sự là do định nghĩa một phân chia bằng không của một kết quả gấp đôi trong giá trị của Inf - đó là do thiết kế không phải do tai nạn. – slebetman

+2

@slebetman - Nếu tăng gấp đôi có "đại diện của một giá trị chính xác bằng không", thì tại sao đôi lại phân biệt giữa "+0" và "-0" và tại sao 1/+ 0 và 1/-0 cho kết quả khác nhau? Ý tưởng về "số không có chữ ký" chỉ có ý nghĩa nếu các giá trị đó được xem là giá trị dương hoặc âm quá nhỏ để được biểu diễn bình thường. Lưu ý rằng không có số không dấu trong các loại điểm nổi IEEE 754. –

+1

@Jeffrey: +0 và -0 vẫn là 0. Ngay cả khi bạn cho rằng các số nhỏ hơn epsilon được biểu thị bằng 0, bạn vẫn đang nói rằng các số quá nhỏ có hiệu quả 0. Có, trong toán học -0 thì không ý nghĩa nhưng khi 754 được thiết kế, các nhà vật lí chạy mô phỏng cho rằng họ muốn biết hướng nào là kết quả từ nếu giới hạn của phép tính dẫn đến 0. – slebetman

1

Điều này có thể liên quan đến thực tế là các điểm dấu phẩy động và điểm nổi chính xác của IEEE có giá trị "vô cùng" được chỉ định. .NET chỉ hiển thị cái gì đó đã tồn tại ở cấp phần cứng.

Xem câu trả lời của kekekela về lý do tại sao điều này có ý nghĩa, hợp lý.

1

Điều này là do thiết kế vì loại double tuân thủ IEEE 754, tiêu chuẩn cho số học dấu phẩy động. Xem tài liệu cho Double.NegativeInfinityDouble.PositiveInfinity.

Giá trị của hằng số này là kết quả của việc chia số dương {hoặc âm} thành 0.

2

Vì điểm nổi "số" không có gì thuộc loại này. Floating hoạt động điểm:

  • không kết
  • không phân phối
  • có thể không có một nghịch đảo

(thấy http://www.cs.uiuc.edu/class/fa07/cs498mjg/notes/floating-point.pdf cho một số ví dụ)

Điểm nổi là một xây dựng để giải quyết một vấn đề cụ thể, và được sử dụng trên tất cả khi nó không nên. Tôi nghĩ chúng khá khủng khiếp, nhưng đó là chủ quan.

Các vấn đề liên quan