`Vector <float> .Equals` nên phản xạ hay nó nên tuân theo ngữ nghĩa của IEEE 754?


9

Khi so sánh các giá trị dấu phẩy động cho đẳng thức, có hai cách tiếp cận khác nhau:

  • NaNkhông bằng chính nó, phù hợp với đặc điểm kỹ thuật của IEEE 754 .
  • NaNbằng chính nó, nó cung cấp tính chất toán học của tính phản xạ , điều cần thiết cho định nghĩa về mối quan hệ tương đương

Các kiểu dấu phẩy động tích hợp trong C # ( floatdouble) tuân theo ngữ nghĩa của IEEE cho ==!=(và các toán tử quan hệ như <) nhưng đảm bảo tính phản xạ cho object.Equals, IEquatable<T>.Equals(và CompareTo).

Bây giờ hãy xem xét một thư viện cung cấp các cấu trúc vector trên đầu float/ double. Một loại vectơ như vậy sẽ quá tải ==/ !=và ghi đè object.Equals/ IEquatable<T>.Equals.

Điều mà mọi người đồng ý là ==/ !=nên tuân theo ngữ nghĩa của IEEE. Câu hỏi đặt ra là, liệu một thư viện như vậy có nên thực hiện Equalsphương thức (tách biệt với các toán tử đẳng thức) theo cách phản xạ hoặc theo cách phù hợp với ngữ nghĩa của IEEE.

Đối số sử dụng ngữ nghĩa của IEEE cho Equals:

  • Nó tuân theo IEEE 754
  • Nó (có thể nhiều) nhanh hơn vì nó có thể tận dụng các hướng dẫn SIMD

    Tôi đã hỏi một câu hỏi riêng về stackoverflow về cách bạn thể hiện sự bình đẳng phản xạ bằng cách sử dụng các hướng dẫn SIMD và tác động hiệu suất của chúng: Hướng dẫn SIMD để so sánh đẳng thức dấu phẩy động

    Cập nhật: Có vẻ như có thể thực hiện hiệu quả phản xạ bằng cách sử dụng ba hướng dẫn SIMD.

  • Tài liệu cho Equalskhông yêu cầu tính phản xạ khi liên quan đến dấu phẩy động:

    Các tuyên bố sau phải đúng với tất cả các cài đặt của phương thức Equals (Object). Trong danh sách, x, y, và zđại diện cho tham chiếu đối tượng mà không phải là null.

    x.Equals(x)trả về true, trừ trường hợp liên quan đến các loại dấu phẩy động. Xem ISO / IEC / IEEE 60559: 2011, Công nghệ thông tin - Hệ thống vi xử lý - Số học dấu phẩy động.

  • Nếu bạn đang sử dụng phao làm khóa từ điển, bạn đang sống trong tình trạng tội lỗi và không nên mong đợi hành vi lành mạnh.

Đối số cho phản xạ:

  • Nó phù hợp với các loại hiện có, bao gồm Single, Double, TupleSystem.Numerics.Complex.

    Tôi không biết bất kỳ tiền lệ nào trong BCL, Equalstheo sau IEEE thay vì phản xạ. Ví dụ Counter bao gồm Single, Double, TupleSystem.Numerics.Complex.

  • Equalschủ yếu được sử dụng bởi các container và thuật toán tìm kiếm dựa trên tính phản xạ. Đối với các thuật toán này, hiệu suất đạt được là không liên quan nếu ngăn chúng hoạt động. Đừng hy sinh tính đúng đắn cho hiệu suất.
  • Nó phá vỡ tất cả các bộ băm dựa và từ điển Contains, Find, IndexOftrên các bộ sưu tập khác nhau / LINQ, hoạt động thiết lập dựa trên LINQ ( Union, Except, vv) nếu dữ liệu chứa NaNgiá trị.
  • Mã thực hiện các tính toán thực tế trong đó ngữ nghĩa của IEEE được chấp nhận thường hoạt động trên các loại cụ thể và sử dụng ==/ !=(hoặc nhiều khả năng so sánh epsilon).

    Hiện tại bạn không thể viết các tính toán hiệu suất cao bằng cách sử dụng tổng quát vì bạn cần các phép toán số học cho điều đó, nhưng chúng không có sẵn thông qua các giao diện / phương thức ảo.

    Vì vậy, một Equalsphương pháp chậm hơn sẽ không ảnh hưởng đến hầu hết các mã hiệu suất cao.

  • Bạn có thể cung cấp một IeeeEqualsphương thức hoặc một IeeeEqualityComparer<T>trường hợp trong trường hợp bạn cần ngữ nghĩa của IEEE hoặc bạn cần có lợi thế về hiệu suất.

Theo tôi những lập luận này ủng hộ mạnh mẽ việc thực hiện phản xạ.

Nhóm CoreFX của Microsoft có kế hoạch giới thiệu một loại vectơ như vậy trong .NET. Không giống như tôi, họ thích giải pháp của IEEE , chủ yếu là do các lợi thế về hiệu suất. Vì một quyết định như vậy chắc chắn sẽ không được thay đổi sau khi phát hành cuối cùng, tôi muốn nhận được phản hồi từ cộng đồng, về những gì tôi tin là một sai lầm lớn.


1
Tuyệt vời và suy nghĩ kích động câu hỏi. Đối với tôi (ít nhất), nó không có ý nghĩa ==Equalssẽ trả về các kết quả khác nhau. Nhiều lập trình viên cho rằng họ như vậy, và làm điều tương tự . Hơn nữa - nói chung, việc triển khai các toán tử đẳng thức gọi Equalsphương thức. Bạn đã lập luận rằng người ta có thể bao gồm một IeeeEquals, nhưng người ta cũng có thể làm điều đó theo cách khác và bao gồm một đối ReflexiveEqualsxứng. Kiểu Vector<float>-type có thể được sử dụng trong nhiều ứng dụng quan trọng về hiệu năng và nên được tối ưu hóa cho phù hợp.
chết

@diemaus Một số lý do tại sao tôi không thấy thuyết phục: 1) cho float/ doublevà một số loại khác, ==Equalsđã khác. Tôi nghĩ rằng sự không nhất quán với các loại hiện có thậm chí còn khó hiểu hơn so với sự không nhất quán giữa ==Equalsbạn sẽ vẫn phải đối phó với các loại khác. 2) Khá nhiều tất cả các thuật toán / bộ sưu tập chung sử dụng Equalsvà dựa vào tính phản xạ của nó để hoạt động (LINQ và từ điển), trong khi các thuật toán dấu phẩy động cụ thể thường sử dụng ==khi chúng có được ngữ nghĩa của chúng.
CodeInChaos

Tôi sẽ xem xét Vector<float>một "con thú" khác hơn là đơn giản floathoặc double. Theo biện pháp đó, tôi không thể thấy lý do Equalshoặc ==nhà điều hành tuân thủ các tiêu chuẩn của họ. Bạn tự nhủ: "Nếu bạn đang sử dụng phao làm khóa từ điển, bạn đang sống trong tình trạng tội lỗi và không nên mong đợi hành vi lành mạnh". Nếu một người lưu trữ NaNtrong một cuốn từ điển, thì đó là lỗi chết tiệt của chính họ vì đã sử dụng thực hành khủng khiếp. Tôi hầu như không nghĩ rằng nhóm CoreFX đã không nghĩ đến điều này. Tôi sẽ đi với một ReflexiveEqualshoặc tương tự, chỉ vì lợi ích hiệu suất.
chết

Câu trả lời:


5

Tôi sẽ lập luận rằng hành vi của IEEE là chính xác. NaNs không tương đương với nhau theo bất kỳ cách nào; chúng tương ứng với các điều kiện không xác định trong đó câu trả lời số không phù hợp.

Ngoài những lợi ích về hiệu suất đến từ việc sử dụng số học của IEEE mà hầu hết các bộ xử lý hỗ trợ nguyên bản, tôi nghĩ có một vấn đề ngữ nghĩa khi nói rằng nếu isnan(x) && isnan(y), thì x == y. Ví dụ:

// C++
double inf = std::numeric_limits<double>::infinity();
double x = 0.0 / 0.0;
double y = inf - inf;

Tôi sẽ lập luận rằng không có lý do có ý nghĩa tại sao một người sẽ xem xét xbằng y. Bạn khó có thể kết luận rằng chúng là những con số tương đương; chúng hoàn toàn không phải là số, vì vậy nó hoàn toàn giống như một khái niệm không hợp lệ.

Hơn nữa, từ góc độ thiết kế API, nếu bạn đang làm việc trên một thư viện đa năng được nhiều lập trình viên sử dụng, thì việc sử dụng ngữ nghĩa dấu phẩy động điển hình nhất trong ngành là điều hợp lý. Mục tiêu của một thư viện tốt là để tiết kiệm thời gian cho những người sử dụng nó, vì vậy việc xây dựng hành vi phi tiêu chuẩn đã chín muồi cho sự nhầm lẫn.


3
Điều đó NaN == NaNsẽ trả lại sai là không thể tranh cãi. Câu hỏi là .Equalsphương pháp nên làm. Ví dụ: nếu tôi sử dụng NaNlàm khóa từ điển, giá trị liên quan sẽ không thể sửa được nếu NaN.Equals(NaN)trả về false.
CodeInChaos

1
Tôi nghĩ bạn phải tối ưu hóa cho trường hợp phổ biến. Trường hợp phổ biến cho một vectơ số là tính toán số thông lượng cao (thường được tối ưu hóa với các hướng dẫn SIMD). Tôi sẽ lập luận rằng việc sử dụng một vectơ làm khóa từ điển là một trường hợp sử dụng cực kỳ hiếm và hầu như không đáng để thiết kế ngữ nghĩa của bạn. Các phản biện rằng có vẻ hợp lý nhất đối với tôi là nhất quán, vì hiện tại Single, Doublelớp học, vv đã có hành vi phản. IMHO, đó chỉ là quyết định sai lầm khi bắt đầu. Nhưng tôi sẽ không để sự tao nhã cản trở sự hữu ích / tốc độ.
Jason R

Nhưng các tính toán số thường sẽ sử dụng ==vốn luôn tuân theo IEEE, vì vậy họ sẽ nhận được mã nhanh cho dù Equalsđược triển khai như thế nào . IMO toàn bộ quan điểm có một Equalsphương pháp riêng biệt được sử dụng trong các thuật toán không quan tâm đến loại cụ thể, chẳng hạn như Distinct()chức năng của LINQ .
CodeInChaos

1
Tôi hiểu rồi Nhưng tôi sẽ tranh luận về một API có ==toán tử và Equals()hàm có ngữ nghĩa khác nhau. Tôi nghĩ rằng bạn đang trả chi phí cho sự nhầm lẫn tiềm ẩn từ góc độ nhà phát triển, không có lợi ích thực sự (tôi không gán bất kỳ giá trị nào để có thể sử dụng vectơ số làm khóa từ điển). Đó chỉ là ý kiến ​​của tôi; Tôi không nghĩ rằng có một câu trả lời khách quan cho câu hỏi trong tầm tay.
Jason R

0

Có một vấn đề: IEEE754 định nghĩa các hoạt động quan hệ và bình đẳng theo cách rất phù hợp với các ứng dụng số. Nó không phù hợp để phân loại và băm. Vì vậy, nếu bạn muốn sắp xếp một mảng dựa trên các giá trị số hoặc nếu bạn muốn thêm các giá trị số vào một tập hợp hoặc sử dụng chúng làm khóa trong từ điển, bạn có thể tuyên bố rằng các giá trị NaN không được phép hoặc bạn không sử dụng IEEE754 hoạt động tích hợp. Bảng băm của bạn sẽ phải đảm bảo rằng tất cả các NaN được khớp với cùng một giá trị và so sánh bằng nhau.

Nếu bạn xác định Vector thì bạn phải đưa ra quyết định thiết kế cho dù bạn muốn sử dụng nó cho mục đích số hay liệu nó có tương thích với việc sắp xếp và băm không. Cá nhân tôi nghĩ rằng mục đích số nên quan trọng hơn nhiều. Nếu cần sắp xếp / băm thì bạn có thể viết một lớp với Vector là thành viên và xác định băm và bình đẳng trong lớp đó theo cách bạn muốn.


1
Tôi đồng ý rằng mục đích số là quan trọng hơn. Nhưng chúng tôi đã có ==và các !=nhà khai thác cho họ. Theo kinh nghiệm của tôi, Equalsphương thức này được sử dụng khá nhiều bởi các thuật toán không phải là số.
CodeInChaos
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.