Lý do cho tất cả các so sánh trả về sai cho các giá trị NaN của IEEE754 là gì?


267

Tại sao so sánh các giá trị NaN lại hành xử khác với tất cả các giá trị khác? Nghĩa là, tất cả các so sánh với các toán tử ==, <=,> =, <,> trong đó một hoặc cả hai giá trị là NaN trả về sai, trái với hành vi của tất cả các giá trị khác.

Tôi cho rằng điều này đơn giản hóa các tính toán số theo một cách nào đó, nhưng tôi không thể tìm thấy một lý do được nêu rõ ràng, ngay cả trong Ghi chú bài giảng về Tình trạng của IEEE 754 của Kahan, thảo luận chi tiết về các quyết định thiết kế khác.

Hành vi lệch lạc này đang gây rắc rối khi thực hiện xử lý dữ liệu đơn giản. Ví dụ: khi sắp xếp danh sách các bản ghi ghi một số trường có giá trị thực trong chương trình C, tôi cần viết thêm mã để xử lý NaN là phần tử tối đa, nếu không thuật toán sắp xếp có thể bị nhầm lẫn.

Chỉnh sửa: Các câu trả lời cho đến nay đều cho rằng việc so sánh NaN là vô nghĩa.

Tôi đồng ý, nhưng điều đó không có nghĩa là câu trả lời đúng là sai, thay vào đó sẽ là Không phải là Boolean (NaB), điều may mắn là không tồn tại.

Vì vậy, sự lựa chọn trả về đúng hay sai để so sánh là theo quan điểm của tôi tùy ý và để xử lý dữ liệu chung sẽ thuận lợi nếu nó tuân theo các quy luật thông thường (tính phản xạ của ==, trichotomy của <, ==,>), kẻo cấu trúc dữ liệu mà dựa vào những luật này trở nên bối rối.

Vì vậy, tôi đang yêu cầu một số lợi thế cụ thể của việc vi phạm các luật này, không chỉ là lý luận triết học.

Chỉnh sửa 2: Tôi nghĩ bây giờ tôi đã hiểu tại sao làm cho NaN tối đa sẽ là một ý tưởng tồi, nó sẽ làm rối tung tính toán của các giới hạn trên.

NaN! = NaN có thể được mong muốn để tránh phát hiện sự hội tụ trong một vòng lặp, chẳng hạn như

while (x != oldX) {
    oldX = x;
    x = better_approximation(x);
}

tuy nhiên tốt hơn nên được viết bằng cách so sánh sự khác biệt tuyệt đối với một giới hạn nhỏ. Vì vậy, IMHO đây là một lập luận tương đối yếu để phá vỡ tính phản xạ tại NaN.


2
Khi một NaN đã vào tính toán, nó thường sẽ không bao giờ rời đi, do đó, bài kiểm tra hội tụ của bạn sẽ trở thành một vòng lặp vô hạn. Thông thường tốt hơn là báo cáo sự thất bại trong việc hội tụ thói quen gọi điện, có thể bằng cách trả lại NaN. Do đó, cấu trúc vòng lặp thường sẽ trở thành một cái gì đó giống như while (fabs(x - oldX) > threshold), thoát khỏi vòng lặp nếu sự hội tụ xảy ra hoặc NaN đi vào tính toán. Phát hiện NaN và biện pháp khắc phục thích hợp sau đó sẽ xảy ra bên ngoài vòng lặp.
Stephen Canon

1
Nếu NaN là phần tử tối thiểu của thứ tự vòng lặp while vẫn hoạt động.
starblue

2
Thức ăn cho suy nghĩ: grouper.ieee.org/groups/1788/email/pdfmPSi1DgZZf.pdf trang 10
starblue

Câu trả lời:


535

Tôi là thành viên của ủy ban IEEE-754, tôi sẽ cố gắng giúp làm rõ mọi thứ một chút.

Trước hết, số dấu phẩy động không phải là số thực và số học dấu phẩy động không thỏa mãn các tiên đề của số học thực. Trichotomy không phải là tài sản duy nhất của số học thực sự không giữ cho phao, thậm chí không quan trọng nhất. Ví dụ:

  • Ngoài ra không phải là kết hợp.
  • Luật phân phối không giữ.
  • Có số dấu phẩy động mà không đảo.

Tôi có thể tiếp tục. Không thể chỉ định loại số học có kích thước cố định thỏa mãn tất cả các thuộc tính của số học thực mà chúng ta biết và yêu thích. Ủy ban 754 phải quyết định bẻ cong hoặc phá vỡ một số trong số họ. Điều này được hướng dẫn bởi một số nguyên tắc khá đơn giản:

  1. Khi chúng ta có thể, chúng ta khớp với hành vi của số học thực sự.
  2. Khi không thể, chúng tôi cố gắng thực hiện các hành vi vi phạm như có thể dự đoán và dễ chẩn đoán nhất có thể.

Về nhận xét của bạn "điều đó không có nghĩa là câu trả lời đúng là sai", điều này là sai. Vị ngữ (y < x)hỏi có ynhỏ hơn không x. Nếu ylà NaN, thì nó không nhỏ hơn bất kỳ giá trị dấu phẩy động nàox , vì vậy câu trả lời nhất thiết là sai.

Tôi đã đề cập rằng trichotomy không giữ các giá trị dấu phẩy động. Tuy nhiên, có một tài sản tương tự mà giữ. Khoản 5.11, khoản 2 của tiêu chuẩn 754-2008:

Bốn quan hệ loại trừ lẫn nhau là có thể: ít hơn, bằng nhau, lớn hơn và không có thứ tự. Trường hợp cuối cùng phát sinh khi có ít nhất một toán hạng là NaN. Mỗi NaN sẽ so sánh không có thứ tự với tất cả mọi thứ, bao gồm cả chính nó.

Theo như viết mã bổ sung để xử lý NaN, thường có thể (mặc dù không phải lúc nào cũng dễ dàng) để cấu trúc mã của bạn theo cách mà NaN rơi đúng cách, nhưng điều này không phải lúc nào cũng đúng. Khi không, một số mã bổ sung có thể là cần thiết, nhưng đó là một cái giá nhỏ để trả cho sự tiện lợi mà việc đóng đại số mang lại cho số học dấu phẩy động.


Phụ lục: Nhiều nhà bình luận đã lập luận rằng sẽ hữu ích hơn khi duy trì tính phản xạ của sự bình đẳng và trichotomy với lý do áp dụng NaN! = NaN dường như không bảo tồn bất kỳ tiên đề quen thuộc nào. Tôi thú nhận có một số thiện cảm với quan điểm này, vì vậy tôi nghĩ rằng tôi sẽ xem lại câu trả lời này và cung cấp thêm một chút bối cảnh.

Sự hiểu biết của tôi khi nói chuyện với Kahan là NaN! = NaN bắt nguồn từ hai cân nhắc thực dụng:

  • Điều đó x == ytương đương với x - y == 0bất cứ khi nào có thể (ngoài việc là một định lý về số học thực, điều này làm cho việc thực hiện phần cứng so sánh hiệu quả hơn về mặt không gian, điều cực kỳ quan trọng tại thời điểm tiêu chuẩn được phát triển - tuy nhiên, điều này bị vi phạm cho x = y = vô cùng, vì vậy bản thân nó không phải là một lý do tuyệt vời; nó có thể bị uốn cong một cách hợp lý (x - y == 0) or (x and y are both NaN)).

  • Quan trọng hơn, không có isnan( )vị ngữ tại thời điểm NaN được chính thức hóa trong số học 8087; nó là cần thiết để cung cấp cho các lập trình viên một phương tiện thuận tiện và hiệu quả để phát hiện các giá trị NaN không phụ thuộc vào các ngôn ngữ lập trình cung cấp thứ gì isnan( )đó có thể mất nhiều năm. Tôi sẽ trích dẫn bài viết của Kahan về chủ đề này:

Không có cách nào để loại bỏ NaN, chúng sẽ vô dụng như Indefinites trên CRAYs; ngay khi gặp phải, tính toán sẽ được dừng lại tốt nhất thay vì tiếp tục trong một thời gian không xác định cho một kết luận không xác định. Đó là lý do tại sao một số hoạt động khi NaN phải cung cấp kết quả không phải NaN. Hoạt động nào? Các trường hợp ngoại lệ là các biến vị ngữ C, x x == x và x x = = x, tương ứng là 1 và 0 cho mọi số x vô hạn hoặc hữu hạn x nhưng ngược lại nếu x không phải là số (NaN); những điều này cung cấp sự phân biệt ngoại lệ đơn giản duy nhất giữa NaN và số trong các ngôn ngữ thiếu một từ cho NaN và một vị từ IsNaN (x).

Lưu ý rằng đây cũng là logic loại trừ việc trả lại một cái gì đó giống như một Not Not A-Boolean. Có lẽ chủ nghĩa thực dụng này đã bị đặt sai chỗ, và tiêu chuẩn cần phải cóisnan( ) , nhưng điều đó sẽ khiến NaN gần như không thể sử dụng hiệu quả và thuận tiện trong vài năm trong khi thế giới chờ đợi áp dụng ngôn ngữ lập trình. Tôi không tin rằng đó sẽ là một sự đánh đổi hợp lý.

Nói thẳng ra: kết quả của NaN == NaN sẽ không thay đổi ngay bây giờ. Tốt hơn để học cách sống với nó hơn là phàn nàn trên internet. Nếu bạn muốn lập luận rằng mối quan hệ thứ tự phù hợp với các container cũng tồn tại, tôi khuyên bạn nên ủng hộ rằng ngôn ngữ lập trình yêu thích của bạn triển khai totalOrdervị từ được chuẩn hóa trong IEEE-754 (2008). Thực tế là nó đã không nói lên tính hợp lệ của mối quan tâm của Kahan đã thúc đẩy tình trạng hiện tại.


16
Tôi đọc điểm 1 và 2. Sau đó tôi quan sát thấy rằng trong số học thực (mở rộng để cho phép NaN ở vị trí đầu tiên) NaN bằng chính nó - đơn giản vì trong toán học, bất kỳ thực thể nào cũng bằng chính nó, không có ngoại lệ. Bây giờ tôi bối rối: tại sao IEEE không "khớp với hành vi của số học thực", điều này sẽ khiến NaN == NaN? Tôi đang thiếu gì?
tối đa

12
Đã đồng ý; tính không phản xạ của NaN đã không tạo ra sự đau đớn cho các ngôn ngữ như Python, với ngữ nghĩa ngăn chặn dựa trên sự bình đẳng của nó. Bạn thực sự không muốn sự bình đẳng không trở thành một mối quan hệ tương đương khi bạn đang cố gắng xây dựng các thùng chứa trên nó. Và có hai khái niệm bình đẳng riêng biệt cũng không phải là một lựa chọn thân thiện, đối với một ngôn ngữ được cho là dễ học. Kết quả (trong trường hợp của Python) là một sự thỏa hiệp mong manh dễ chịu giữa sự tôn trọng đối với IEEE 754 và ngữ nghĩa ngăn chặn không bị phá vỡ. May mắn thay, thật hiếm khi đưa NaN vào thùng chứa.
Đánh dấu Dickinson

5
Một số quan sát thú vị ở đây: bertrandmeyer.com/2010/02/06/ trộm
Mark Dickinson

6
@StephenCanon: Theo cách nào (0/0) == (+ INF) + (-INF) sẽ vô lý hơn so với việc có 1f/3f == 10000001f/30000002f? Nếu các giá trị dấu phẩy động được coi là các lớp tương đương, thì a=bkhông có nghĩa là "Các tính toán mang lại ab, nếu được thực hiện với độ chính xác vô hạn, sẽ mang lại kết quả giống hệt nhau", mà là "Những gì đã biết về akhớp với những gì đã biết về b". Tôi tò mò nếu bạn biết bất kỳ ví dụ nào về mã trong đó có "Nan! = NaN" làm cho mọi thứ đơn giản hơn so với chúng?
supercat

5
Về mặt lý thuyết, nếu bạn có NaN == NaN và không có isNaN, bạn vẫn có thể kiểm tra NaN với !(x < 0 || x == 0 || x > 0), nhưng nó sẽ chậm hơn và vụng về hơn x != x.
user2357112 hỗ trợ Monica

50

NaN có thể được coi là một trạng thái / số không xác định. tương tự như khái niệm 0/0 không xác định hoặc sqrt (-3) (trong hệ thống số thực có dấu phẩy động).

NaN được sử dụng như một loại giữ chỗ cho trạng thái không xác định này. Về mặt toán học, không xác định không bằng không xác định. Bạn cũng không thể nói giá trị không xác định lớn hơn hoặc nhỏ hơn giá trị không xác định khác. Do đó mọi so sánh đều trả về sai.

Hành vi này cũng thuận lợi trong trường hợp bạn so sánh sqrt (-3) với sqrt (-2). Cả hai sẽ trả lại NaN nhưng chúng không tương đương mặc dù chúng trả về cùng một giá trị. Do đó, có sự bình đẳng luôn trả về sai khi xử lý NaN là hành vi mong muốn.


5
Kết quả của sqrt (1.00000000000000022) == sqrt (1.0) là gì? Làm thế nào về (1E308 + 1E308-1E308-1E308-1E308) == (1E308 + 1E308)? Ngoài ra, chỉ năm trong số sáu so sánh trả về sai. Các !=nhà điều hành trả về true. Có NaN==NaNNaN!=NaNcả hai trả về false sẽ cho phép mã so sánh x và y để chọn điều gì sẽ xảy ra khi cả hai toán hạng là NaN bằng cách chọn một trong hai ==hoặc !=.
supercat

38

Để ném vào một tương tự khác. Nếu tôi đưa cho bạn hai hộp và nói với bạn rằng cả hai hộp đều không chứa táo, bạn có nói với tôi rằng các hộp chứa cùng một thứ không?

NaN không chứa thông tin về những gì là một cái gì đó, chỉ là những gì nó không. Do đó, các yếu tố này không bao giờ có thể được nói là bằng nhau.


6
Tất cả các bộ trống đều bằng nhau, theo định nghĩa.
MSalters

28
Các hộp bạn được cung cấp KHÔNG được biết là trống.
John Smith

7
Bạn có thể cho tôi biết các hộp không chứa cùng một thứ? Tôi có thể hiểu lý do cho (NaN==Nan)==false. Những gì tôi không hiểu là lý do cho (Nan!=Nan)==true.
supercat

3
Tôi giả sử NaN! = NaN là đúng vì x! = Y được định nghĩa là! (X == y). Cấp, tôi không biết nếu thông số kỹ thuật của IEEE định nghĩa theo cách đó.
Kef Schecter

6
Nhưng trong sự tương tự này, nếu bạn đưa cho tôi một hộp, nói rằng nó không chứa táo, sau đó hỏi tôi liệu nó có bằng chính nó không, bạn có muốn tôi nói không? Bởi vì đó là những gì tôi sẽ phải nói theo IEEE.
dấu chấm phẩy

12

Từ bài viết trên wikipedia về NaN , các thực tiễn sau có thể gây ra NaN:

  • Tất cả các phép toán> với NaN là ít nhất một toán hạng
  • Các phép chia 0/0, / ∞, ∞ / -∞, -∞ / ∞ và -∞ / -∞
  • Phép nhân 0 × và 0 × -∞
  • Các phép cộng + (-∞), (-∞) + và phép trừ tương đương.
  • Áp dụng hàm cho các đối số bên ngoài miền của nó, bao gồm lấy căn bậc hai của số âm, lấy logarit của số âm, lấy tiếp tuyến của bội số lẻ 90 độ (hoặc π / 2 radian) hoặc lấy sin nghịch đảo hoặc cosin của một số nhỏ hơn -1 hoặc lớn hơn +1.

Vì không có cách nào để biết hoạt động nào trong số các hoạt động này đã tạo ra NaN, nên không có cách nào để so sánh chúng có ý nghĩa.


3
Hơn nữa, ngay cả khi bạn biết hoạt động nào, nó sẽ không giúp ích gì. Tôi có thể xây dựng bất kỳ số lượng công thức nào về 0/0 tại một số điểm, trong đó có (nếu chúng ta giả sử tính liên tục) được xác định rõ và các giá trị khác nhau tại điểm đó.
David Thornley

4

Tôi không biết lý do thiết kế, nhưng đây là một đoạn trích từ tiêu chuẩn IEEE 754-1985:

"Có thể so sánh các số dấu phẩy động trong tất cả các định dạng được hỗ trợ, ngay cả khi các định dạng của toán hạng khác nhau. So sánh là chính xác và không bao giờ tràn cũng không tràn. Bốn quan hệ loại trừ lẫn nhau là có thể: nhỏ hơn, bằng, lớn hơn và không được sắp xếp Trường hợp cuối cùng phát sinh khi có ít nhất một toán hạng là NaN. Mỗi NaN sẽ so sánh không có thứ tự với mọi thứ, kể cả chính nó. "


2

Nó chỉ trông đặc biệt bởi vì hầu hết các môi trường lập trình cho phép NaN cũng không cho phép logic 3 giá trị. Nếu bạn ném logic 3 giá trị vào hỗn hợp, nó sẽ trở nên nhất quán:

  • (2.7 == 2.7) = đúng
  • (2.7 == 2.6) = sai
  • (2.7 == NaN) = không xác định
  • (NaN == NaN) = không xác định

Ngay cả .NET cũng không cung cấp bool? operator==(double v1, double v2)toán tử, vì vậy bạn vẫn bị mắc kẹt với (NaN == NaN) = falsekết quả ngớ ngẩn .


1

Tôi đoán rằng NaN (Không phải là Số) có nghĩa chính xác là: Đây không phải là một số và do đó so sánh nó không thực sự có ý nghĩa.

Nó hơi giống số học trong SQL với nulltoán hạng: Tất cả đều có kết quả null.

Các so sánh cho số dấu phẩy động so sánh các giá trị số. Vì vậy, chúng không thể được sử dụng cho các giá trị không phải là số. Do đó NaN không thể được so sánh theo nghĩa số.


3
"Đây không phải là một con số và do đó so sánh nó không thực sự có ý nghĩa." Chuỗi không phải là số nhưng so sánh chúng có ý nghĩa.
jason

2
vâng, so sánh một chuỗi với một chuỗi có ý nghĩa. Nhưng so sánh một chuỗi với, nói, táo, không có nhiều ý nghĩa. Vì táo và lê không phải là số, nên có ý nghĩa gì khi so sánh chúng? Cái nào lớn hơn?
Daren Thomas

@DarenThomas: Trong SQL, không "IF NULL = NULL THEN FOO;" cũng không phải "Null Null <> Null THEN GỌI FOO;" [hoặc bất cứ cú pháp nào] sẽ thực thi FOO. Đối với NaN là tương đương if (NaN != NaN) foo();không nên thực hiện foo, nhưng nó.
supercat

1

Câu trả lời đơn giản hóa là NaN không có giá trị số, vì vậy không có gì trong đó để so sánh với bất cứ điều gì khác.

Bạn có thể xem xét thử nghiệm và thay thế NaN của mình bằng + INF nếu bạn muốn chúng hoạt động như + INF.


0

Mặc dù tôi đồng ý rằng việc so sánh NaN với bất kỳ số thực nào sẽ không được sắp xếp theo thứ tự, tôi nghĩ rằng có lý do để so sánh NaN với chính nó. Làm thế nào, ví dụ, người ta phát hiện ra sự khác biệt giữa tín hiệu NaN và NaN yên tĩnh? Nếu chúng ta nghĩ về các tín hiệu như một tập hợp các giá trị Boolean (tức là một vectơ bit), người ta cũng có thể hỏi liệu các vectơ bit giống nhau hay khác nhau và sắp xếp các tập hợp cho phù hợp. Ví dụ: khi giải mã số mũ sai lệch tối đa, nếu ý nghĩa được dịch chuyển trái để căn chỉnh bit đáng kể nhất của ý nghĩa trên bit quan trọng nhất của định dạng nhị phân, giá trị âm sẽ là NaN yên tĩnh và bất kỳ giá trị dương nào cũng sẽ là một NaN báo hiệu. Tất nhiên là không dành riêng cho vô hạn và sự so sánh sẽ không có thứ tự. Căn chỉnh MSB sẽ cho phép so sánh trực tiếp các tín hiệu ngay cả từ các định dạng nhị phân khác nhau. Do đó, hai NaN có cùng bộ tín hiệu sẽ tương đương và có ý nghĩa đối với sự bình đẳng.


-1

Đối với tôi, cách dễ nhất để giải thích nó là:

Tôi có một cái gì đó và nếu nó không phải là một quả táo thì nó có phải là một quả cam không?

Bạn không thể so sánh NaN với một thứ khác (thậm chí là chính nó) vì nó không có giá trị. Ngoài ra nó có thể là bất kỳ giá trị (ngoại trừ một số).

Tôi có một cái gì đó và nếu nó không bằng một số thì nó có phải là một chuỗi không?


Bạn có ý nghĩa gì "nó có thể là bất kỳ giá trị nào ngoại trừ một số"?
Pushkin

-2

Bởi vì toán học là lĩnh vực mà con số "chỉ tồn tại". Trong điện toán, bạn phải khởi tạo những con số đó và giữ trạng thái của chúng theo nhu cầu của bạn. Vào những ngày xưa, việc khởi tạo bộ nhớ hoạt động theo những cách mà bạn không bao giờ có thể dựa vào. Bạn không bao giờ có thể cho phép mình nghĩ về điều này "ồ, điều đó sẽ được khởi tạo với 0xCD mọi lúc, thuật toán của tôi sẽ không bị phá vỡ" .

Vì vậy, bạn cần dung môi không trộn thích hợp , đủ dính để không để thuật toán của bạn bị hút vào và phá vỡ. Các thuật toán tốt liên quan đến các con số chủ yếu sẽ hoạt động với các mối quan hệ và những thuật toán nếu () sẽ bị bỏ qua.

Đây chỉ là mỡ mà bạn có thể đưa vào biến mới khi tạo, thay vì lập trình địa ngục ngẫu nhiên từ bộ nhớ máy tính. Và thuật toán của bạn cho dù đó là gì, sẽ không phá vỡ.

Tiếp theo, khi bạn vẫn đột nhiên phát hiện ra rằng thuật toán của bạn đang tạo ra NaN, có thể xóa sạch nó, xem xét từng nhánh một. Một lần nữa, quy tắc "luôn luôn sai" đang giúp rất nhiều trong việc này.


-4

Câu trả lời rất ngắn:

Bởi vì: nan / nan = 1 KHÔNG được giữ. Nếu không thìinf/inf sẽ là 1.

(Do đó, nankhông thể bằng nan. Vì , >hoặc <nếu nantôn trọng bất kỳ mối quan hệ đơn hàng nào trong một tập hợp thỏa mãn thuộc tính Archimedean, chúng ta sẽ lại có nan / nan = 1giới hạn).


2
Không, điều đó không có ý nghĩa. Chúng tôi có inf = infinf / inf = nan, vì vậy nan = nansẽ không ngăn chặn nan / nan = nan.
starblue

@starblue Ý bạn là nan / nan = 1gì? Dù sao ... lý luận của bạn có ý nghĩa nếu inf và nan cũng giống như bất kỳ số nào khác. Thực tế không phải là như vậy. Lý do tại sao inf/infphải nan(hoặc dạng không xác định trong toán học) và không 1tinh tế hơn thao tác đại số đơn giản (xem định lý De L'Hospital).
SeF
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.