Tổng số không xác định của phao


10

Hãy để tôi nói rõ nắm đấm: Tôi hoàn toàn hiểu rằng các loại dấu phẩy động không thể biểu diễn chính xác các giá trị thập phân . Đây không phải là về điều đó! Tuy nhiên, tính toán dấu phẩy động được cho là xác định .

Bây giờ điều này đã được thực hiện, hãy để tôi chỉ cho bạn thấy trường hợp tò mò mà tôi quan sát thấy ngày hôm nay. Tôi có một danh sách các giá trị dấu phẩy động và tôi muốn tổng hợp chúng:

CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);

SELECT STR(SUM(#someFloats.val), 30, 15) FROM #someFloats;

DROP TABLE #someFloats;

-- yields:
--   13.600000000000001

Cho đến nay, rất tốt - không có bất ngờ ở đây. Chúng ta đều biết rằng 1.2không thể được biểu diễn chính xác trong biểu diễn nhị phân, do đó, kết quả "không chính xác" được mong đợi.

Bây giờ điều kỳ lạ sau đây xảy ra khi tôi rời khỏi - tham gia một bảng khác:

CREATE TABLE #A (a int);
INSERT INTO #A (a) VALUES (1), (2);

CREATE TABLE #someFloats (val float);
INSERT INTO #someFloats (val) VALUES (1), (1), (1.2), (1.2), (1.2), (3), (5);

SELECT #A.a, STR(SUM(#someFloats.val), 30, 15)
  FROM #someFloats LEFT JOIN #A ON 1 = 1
 GROUP BY #A.a;

DROP TABLE #someFloats;
DROP TABLE #A;

-- yields
--   1   13.600000000000001
--   2   13.599999999999998

( fiddle sql , bạn cũng có thể thấy kế hoạch thực hiện ở đó)

Tôi có cùng một tổng trên các giá trị giống nhau , nhưng một lỗi dấu phẩy động khác nhau . Nếu tôi thêm nhiều hàng vào bảng #A, chúng ta có thể thấy rằng giá trị xen kẽ giữa hai giá trị đó. Tôi chỉ có thể tái tạo vấn đề này với một LEFT JOIN; INNER JOINhoạt động như mong đợi ở đây.

Điều này là bất tiện, bởi vì nó có nghĩa là a DISTINCT, GROUP BYhoặc PIVOTxem chúng là các giá trị khác nhau (đó thực sự là cách chúng tôi phát hiện ra vấn đề này).

Giải pháp rõ ràng là làm tròn giá trị, nhưng tôi tò mò: Có lời giải thích hợp lý nào cho hành vi này không?

Câu trả lời:


15

Trên thực tế, liên kết mà bạn đề cập không nói rằng các phép tính số học dấu phẩy động luôn mang tính xác định. Trong thực tế, trong một trong những câu trả lời, đề cập rằng bổ sung không liên quan (nghĩa là (a + b) + ckhông nhất thiết phải bằng nhau a + (b + c)), điều này cũng được nói trong câu trả lời này .

Nếu tập hợp luồng xảy ra để xử lý các hàng của mỗi nhóm theo thứ tự khác nhau - mà SQL Server thường được tự do thực hiện; nếu không có ORDER BYtrong mệnh đề thích hợp, thì trình tối ưu hóa sẽ chọn bất kỳ thao tác quét hoặc tìm kiếm nào hoặc toán tử truy vấn khác sẽ nhanh nhất, bất kể thứ tự nào thực hiện bổ sung trong - thì điều này có thể giải thích hành vi bạn quan sát.

Bổ sung luôn luôn mang tính quyết định: bạn đặt trong hai phao giống nhau, bạn nhận được cùng một số float. Nhưng việc thêm các phao lại với nhau theo một thứ tự khác nhau có thể cho một kết quả khác.


Associativity không có liên quan đến tính xác định, do đó bit là sai lệch.
Vịt Mooing

Tính không kết hợp của phép cộng dấu phẩy động dẫn đến hành vi không xác định của hàm tổng hợp SQL Server SUM(), bạn có đồng ý @MooingDuck không?
mustaccio

Không? Bộ phận Integer là một ví dụ rõ ràng. Nó không liên kết, nhưng hoàn toàn xác định. Tương tự như vậy, phân chia điểm trôi nổi nên không liên kết và vẫn mang tính quyết định. Từ đó, chúng tôi kết luận rằng việc bổ sung là không liên kết và vẫn mang tính quyết định là hợp lý. Điều đó đang được nói, nếu thứ tự bổ sung không mang tính quyết định, thì kết quả cũng sẽ không mang tính quyết định, vì vậy câu đầu tiên và câu cuối của bạn vẫn đúng bất kể.
Vịt quay

Phân chia số nguyên là một ví dụ mẫu cho Máy chủ SQL SUM()qua các đối số dấu phẩy động, chính xác như thế nào?
mustaccio

1
Phân chia số nguyên là không liên kết và xác định. Do đó, tính toán kết hợp hoạt động số học không liên quan đến tính xác định. Do đó, bất kỳ sự không kết hợp nào SUM()cũng phải không liên quan đến tính quyết định của nó. Tôi đồng ý rằng điều đó SUMdường như không mang tính quyết định, nhưng bạn nên loại bỏ đề cập đến tính kết hợp, vì điều đó không liên quan.
Mooing Duck
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.