Câu trả lời:
Đây là những gì tôi tìm thấy khi tôi có nghi ngờ này.
mysql> create table numbers (a decimal(10,2), b float);
mysql> insert into numbers values (100, 100);
mysql> select @a := (a/3), @b := (b/3), @a * 3, @b * 3 from numbers \G
*************************** 1. row ***************************
@a := (a/3): 33.333333333
@b := (b/3): 33.333333333333
@a + @a + @a: 99.999999999000000000000000000000
@b + @b + @b: 100
Số thập phân đã làm chính xác những gì phải làm trong trường hợp này, nó cắt ngắn phần còn lại, do đó mất phần 1/3.
Vì vậy, đối với các khoản tiền thập phân là tốt hơn, nhưng đối với sự phân chia thì float là tốt hơn, đến một lúc nào đó, tất nhiên. Ý tôi là, sử dụng DECIMAL sẽ không cung cấp cho bạn "số học bằng chứng không thành công" trong bất kỳ phương tiện nào.
Hi vọng điêu nay co ich.
@a
đưa ra 99.999999999000000000000000000000 KHAI THÁC? Đó là kỹ thuật chính xác.
"Phao" trong hầu hết các môi trường là loại dấu phẩy động nhị phân. Nó có thể lưu trữ chính xác các giá trị cơ sở 2 (đến một điểm nhất định), nhưng không thể lưu trữ chính xác nhiều giá trị cơ bản 10 (thập phân). Phao là thích hợp nhất cho các tính toán khoa học. Chúng không phù hợp với hầu hết các môn toán định hướng kinh doanh và việc sử dụng phao không phù hợp sẽ cắn bạn. Nhiều giá trị thập phân không thể được biểu diễn chính xác trong cơ sở 2. 0.1
không thể, ví dụ, và vì vậy bạn thấy kết quả lạ như thế nào 1.0 - 0.1 = 0.8999999
.
Số thập phân lưu trữ cơ sở 10 số. Thập phân là một loại tốt cho hầu hết các phép toán kinh doanh (nhưng bất kỳ loại "tiền" tích hợp nào phù hợp hơn cho các tính toán tài chính), trong đó phạm vi của các giá trị vượt quá mức được cung cấp bởi các loại số nguyên và các giá trị phân số là cần thiết. Số thập phân, như tên ngụ ý, được thiết kế cho các số cơ sở 10 - chúng có thể lưu trữ chính xác các giá trị thập phân (một lần nữa, đến một điểm nhất định).
MySQL gần đây đã thay đổi cách họ lưu trữ loại DECIMAL . Trước đây, họ đã lưu trữ các ký tự (hoặc nybble) cho mỗi chữ số bao gồm một đại diện ASCII (hoặc nybble) của một số - so với - một số nguyên bổ sung hai hoặc một số đạo hàm của chúng.
Định dạng lưu trữ hiện tại cho DECIMAL là một chuỗi các số nguyên 1,2,3 hoặc 4 byte có các bit được nối để tạo số bù hai với một dấu thập phân ngụ ý, được xác định bởi bạn và được lưu trữ trong lược đồ DB khi bạn khai báo cột và chỉ định kích thước DECIMAL và vị trí dấu thập phân của nó.
Ví dụ, nếu bạn lấy int 32 bit, bạn có thể lưu trữ bất kỳ số nào từ 0 - 4.294.967.295. Điều đó sẽ chỉ đáng tin cậy bao gồm 999.999.999, vì vậy nếu bạn đã ném ra 2 bit và sử dụng (1 << 30 -1) thì bạn sẽ không từ bỏ. Bao gồm tất cả các số có 9 chữ số chỉ có 4 byte sẽ hiệu quả hơn so với việc bao gồm 4 chữ số trong 32 bit bằng 4 ký tự ASCII hoặc 8 chữ số nybble. (một nybble là 4 bit, cho phép các giá trị 0-15, nhiều hơn mức cần thiết cho 0-9, nhưng bạn không thể loại bỏ sự lãng phí đó bằng cách chuyển đến 3 bit, vì chỉ bao gồm các giá trị 0-7)
Ví dụ được sử dụng trên các tài liệu trực tuyến của MySQL sử dụng DECIMAL (18,9) làm ví dụ. Đây là 9 chữ số trước và 9 chữ số phía sau dấu thập phân ngụ ý, như đã giải thích ở trên yêu cầu lưu trữ sau.
Như 18 ký tự 8 bit: 144 bit
Như 18 nybble 4 bit: 72 bit
Như 2 số nguyên 32 bit: 64 bit
Hiện tại, DECIMAL hỗ trợ tối đa 65 chữ số, vì DECIMAL (M, D) trong đó giá trị lớn nhất cho M được phép là 65 và giá trị lớn nhất của D được phép là 30.
Vì vậy, để không yêu cầu các khối 9 chữ số cùng một lúc, các số nguyên nhỏ hơn 32 bit được sử dụng để thêm các chữ số sử dụng số nguyên 1,2 và 3 byte. Vì một số lý do bất chấp logic, đã ký, thay vì ints không dấu được sử dụng và làm như vậy, 1 bit bị loại bỏ, dẫn đến các khả năng lưu trữ sau. Đối với int int 1,2 và 4 byte, bit bị mất không thành vấn đề, nhưng đối với int 3 byte thì đó là một thảm họa vì toàn bộ một chữ số bị mất do mất bit đơn lẻ đó.
Với int 7 bit: 0 - 99
Với int 15 bit: 0 - 9,999
Với int 23 bit: 0 - 999.999 (0 - 9,999.999 với int 24 bit)
Các số nguyên 1,2,3 và 4 byte được nối với nhau để tạo thành một "nhóm bit" DECIMAL sử dụng để biểu diễn số chính xác như một số nguyên bổ sung của hai. Dấu thập phân KHÔNG được lưu trữ, nó được ngụ ý.
Điều này có nghĩa là không yêu cầu chuyển đổi ASCII sang int của công cụ DB để chuyển đổi "số" thành thứ mà CPU nhận ra là số. Không làm tròn, không có lỗi chuyển đổi, đó là con số thực mà CPU có thể thao tác.
Các tính toán trên số nguyên lớn tùy ý này phải được thực hiện trong phần mềm, vì không có phần cứng hỗ trợ cho loại số này, nhưng các thư viện này rất cũ và được tối ưu hóa cao, đã được viết cách đây 50 năm để hỗ trợ dữ liệu điểm nổi chính xác tùy ý của IBM 370 Fortran . Chúng vẫn chậm hơn rất nhiều so với đại số nguyên có kích thước cố định được thực hiện với phần cứng số nguyên CPU hoặc các phép tính dấu phẩy động được thực hiện trên FPU.
Về hiệu quả lưu trữ, bởi vì số mũ của một số float được gắn vào mỗi float, chỉ định ngầm định dấu thập phân ở đâu, nó là dự phòng ồ ạt, và do đó không hiệu quả cho công việc DB. Trong DB bạn đã biết điểm thập phân sẽ đi lên phía trước ở đâu và mỗi hàng trong bảng có giá trị cho cột DECIMAL chỉ cần nhìn vào thông số 1 & chỉ nơi đặt dấu thập phân đó trong lược đồ như là các đối số cho một DECIMAL (M, D) là hàm ý của các giá trị M và D.
Nhiều nhận xét được tìm thấy ở đây về định dạng nào sẽ được sử dụng cho các loại ứng dụng khác nhau là chính xác, vì vậy tôi sẽ không tin vào điều đó. Tôi đã dành thời gian để viết nó ở đây bởi vì bất cứ ai đang duy trì tài liệu trực tuyến MySQL được liên kết đều không hiểu bất kỳ điều nào ở trên và sau những nỗ lực ngày càng bực bội để giải thích cho họ, tôi đã từ bỏ. Một dấu hiệu tốt cho thấy họ hiểu những gì họ viết kém đến mức nào là cách trình bày rất lầy lội và gần như không thể giải mã được về vấn đề này.
Như một suy nghĩ cuối cùng, nếu bạn cần tính toán dấu phẩy động có độ chính xác cao, đã có những tiến bộ to lớn về mã dấu phẩy động trong 20 năm qua và hỗ trợ phần cứng cho phao chính xác 96 bit và Quadruple ở ngay góc, nhưng có những thư viện chính xác tùy ý tốt ngoài kia nếu thao tác của giá trị được lưu trữ là quan trọng.
Không chỉ cụ thể đối với MySQL, sự khác biệt giữa các kiểu float và thập phân là cách chúng thể hiện các giá trị phân số. Các kiểu dấu phẩy động biểu thị các phân số trong nhị phân, chỉ có thể biểu thị các giá trị dưới dạng {m*2^n | m, n Integers}
. các giá trị như 1/5 không thể được biểu diễn chính xác (không có lỗi làm tròn). Số thập phân được giới hạn tương tự, nhưng đại diện cho số như {m*10^n | m, n Integers}
. Số thập phân vẫn không thể biểu thị các số như 1/3, nhưng thường là trong nhiều lĩnh vực phổ biến, như tài chính, kỳ vọng là các phân số thập phân nhất định luôn có thể được thể hiện mà không mất độ trung thực. Vì một số thập phân có thể đại diện cho một giá trị như $0.20
(một phần năm của một đô la), nên nó được ưu tiên trong các tình huống đó.
số thập phân dành cho số lượng cố định như tiền mà bạn muốn có một số vị trí thập phân cụ thể. Phao là để lưu trữ ... số chính xác điểm nổi.
Tôi thấy điều này hữu ích:
Nói chung, giá trị Float là tốt cho tính toán khoa học, nhưng không nên được sử dụng cho các giá trị tài chính / tiền tệ. Đối với môn toán định hướng kinh doanh, luôn luôn sử dụng số thập phân.
Nguồn: http://code.rohitink.com/2013/06/12/mysql-integer-float-decimal-data-types-differences/
mysql> CREATE TABLE num(id int ,fl float,dc dec(5,2));
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO num VALUES(1,13.75,13.75);
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO num VALUES(2,13.15,13.15);
Query OK, 1 row affected (0.00 sec)
mysql> SELECT * FROM num WHERE fl = 13.15;
Empty set (0.00 sec)
mysql> SELECT * FROM num WHERE dc = 13.15;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
mysql> SELECT SUM(fl) ,SUM(dc) FROM num;
+--------------------+---------+
| SUM(fl) | SUM(dc) |
+--------------------+---------+
| 26.899999618530273 | 26.90 |
+--------------------+---------+
1 row in set (0.00 sec)
mysql> SELECT * FROM num WHERE ABS(fl - 13.15)<0.01;
+------+-------+-------+
| id | fl | dc |
+------+-------+-------+
| 2 | 13.15 | 13.15 |
+------+-------+-------+
1 row in set (0.00 sec)
Các loại dấu phẩy động (Giá trị gần đúng) - FLOAT, NHÂN ĐÔI
Các loại FLOAT và NHÂN ĐÔI đại diện cho các giá trị dữ liệu số gần đúng . MySQL sử dụng bốn byte cho các giá trị độ chính xác đơn và tám byte cho các giá trị độ chính xác kép.
Đối với FLOAT, tiêu chuẩn SQL cho phép một đặc tả tùy chọn về độ chính xác (nhưng không phải là phạm vi của số mũ) theo các bit theo từ khóa FLOAT trong ngoặc đơn. MySQL cũng hỗ trợ đặc tả độ chính xác tùy chọn này, nhưng giá trị chính xác chỉ được sử dụng để xác định kích thước lưu trữ. Độ chính xác từ 0 đến 23 dẫn đến cột FLOAT có độ chính xác đơn 4 byte. Độ chính xác từ 24 đến 53 dẫn đến cột NHÂN ĐÔI độ chính xác kép 8 byte.
MySQL cho phép một cú pháp không chuẩn: FLOAT (M, D) hoặc REAL (M, D) hoặc PRECISION NHÂN ĐÔI (M, D). Ở đây, ngôn ngữ (M, D) có nghĩa là hơn các giá trị có thể được lưu trữ với tổng số chữ số M, trong đó chữ số D có thể nằm sau dấu thập phân. Ví dụ: một cột được xác định là FLOAT (7,4) sẽ trông giống như -999.9999 khi được hiển thị. MySQL thực hiện làm tròn khi lưu trữ giá trị, vì vậy nếu bạn chèn 999.00009 vào cột FLOAT (7.4), kết quả gần đúng là 999.0001.
Vì các giá trị dấu phẩy động là gần đúng và không được lưu trữ dưới dạng giá trị chính xác, nên các nỗ lực coi chúng là chính xác trong các so sánh có thể dẫn đến các vấn đề. Họ cũng phải phụ thuộc vào nền tảng hoặc phụ thuộc thực hiện.
Để có tính di động tối đa, mã yêu cầu lưu trữ các giá trị dữ liệu số gần đúng nên sử dụng FLOAT hoặc DOUBLE PRECISION mà không có thông số kỹ thuật về độ chính xác hoặc số chữ số.
https://dev.mysql.com/doc/refman/5.5/en/floating-point-types.html
Các vấn đề với giá trị dấu phẩy động
Số dấu phẩy động đôi khi gây nhầm lẫn vì chúng gần đúng và không được lưu dưới dạng giá trị chính xác . Giá trị dấu phẩy động như được viết trong câu lệnh SQL có thể không giống với giá trị được biểu thị bên trong. Nỗ lực coi các giá trị dấu phẩy động là chính xác trong các so sánh có thể dẫn đến các vấn đề. Họ cũng phải phụ thuộc vào nền tảng hoặc phụ thuộc thực hiện. Các kiểu dữ liệu FLOAT và NHÂN ĐÔI phải chịu các vấn đề này. Đối với các cột DECIMAL, MySQL thực hiện các hoạt động với độ chính xác 65 chữ số thập phân, điều này sẽ giải quyết hầu hết các vấn đề không chính xác phổ biến.
Ví dụ sau sử dụng NHÂN ĐÔI để trình bày cách tính toán được thực hiện bằng các phép toán dấu phẩy động có thể bị lỗi dấu phẩy động.
mysql> CREATE TABLE t1 (i INT, d1 DOUBLE, d2 DOUBLE);
mysql> INSERT INTO t1 VALUES (1, 101.40, 21.40), (1, -80.00, 0.00),
-> (2, 0.00, 0.00), (2, -13.20, 0.00), (2, 59.60, 46.40),
-> (2, 30.40, 30.40), (3, 37.00, 7.40), (3, -29.60, 0.00),
-> (4, 60.00, 15.40), (4, -10.60, 0.00), (4, -34.00, 0.00),
-> (5, 33.00, 0.00), (5, -25.80, 0.00), (5, 0.00, 7.20),
-> (6, 0.00, 0.00), (6, -51.40, 0.00);
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b
-> FROM t1 GROUP BY i HAVING a <> b;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
| 6 | -51.4 | 0 |
+------+-------+------+
Kết quả là chính xác. Mặc dù năm bản ghi đầu tiên trông giống như chúng không thỏa mãn so sánh (giá trị của a và b dường như không khác nhau), chúng có thể làm như vậy bởi vì sự khác biệt giữa các số hiển thị quanh thập phân thứ mười hoặc hơn, tùy thuộc vào các yếu tố chẳng hạn như kiến trúc máy tính hoặc phiên bản trình biên dịch hoặc mức tối ưu hóa. Ví dụ, các CPU khác nhau có thể đánh giá các số dấu phẩy động khác nhau.
Nếu các cột d1 và d2 đã được xác định là KHAI THÁC chứ không phải NHÂN ĐÔI, thì kết quả của truy vấn CHỌN sẽ chỉ chứa một hàng Hồi giáo một hàng cuối cùng được hiển thị ở trên.
Cách chính xác để thực hiện so sánh số dấu phẩy động là trước tiên quyết định mức dung sai chấp nhận được đối với sự khác biệt giữa các số và sau đó thực hiện so sánh với giá trị dung sai. Ví dụ: nếu chúng tôi đồng ý rằng các số có dấu phẩy động nên được coi là giống nhau nếu chúng giống nhau trong độ chính xác một phần mười (0,0001), thì nên so sánh để tìm sự khác biệt lớn hơn giá trị dung sai:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) > 0.0001;
+------+-------+------+
| i | a | b |
+------+-------+------+
| 6 | -51.4 | 0 |
+------+-------+------+
1 row in set (0.00 sec)
Ngược lại, để có được các hàng trong đó các số giống nhau, thử nghiệm phải tìm sự khác biệt trong giá trị dung sai:
mysql> SELECT i, SUM(d1) AS a, SUM(d2) AS b FROM t1
-> GROUP BY i HAVING ABS(a - b) <= 0.0001;
+------+------+------+
| i | a | b |
+------+------+------+
| 1 | 21.4 | 21.4 |
| 2 | 76.8 | 76.8 |
| 3 | 7.4 | 7.4 |
| 4 | 15.4 | 15.4 |
| 5 | 7.2 | 7.2 |
+------+------+------+
5 rows in set (0.03 sec)
Các giá trị dấu phẩy động tùy thuộc vào nền tảng hoặc phụ thuộc triển khai. Giả sử bạn thực hiện các câu lệnh sau:
CREATE TABLE t1(c1 FLOAT(53,0), c2 FLOAT(53,0));
INSERT INTO t1 VALUES('1e+52','-1e+52');
SELECT * FROM t1;
Trên một số nền tảng, câu lệnh SELECT trả về inf và -inf. Trên những cái khác, nó trả về 0 và -0.
Một hàm ý của các vấn đề trước đó là nếu bạn cố gắng tạo một nô lệ sao chép bằng cách đổ nội dung bảng với mysqldump trên bản gốc và tải lại tệp kết xuất vào nô lệ, các bảng chứa các cột dấu phẩy động có thể khác nhau giữa hai máy chủ.
https://dev.mysql.com/doc/refman/5.5/en/probols-with-float.html
Quy tắc cứng & nhanh
Nếu tất cả những gì bạn cần làm là cộng, trừ hoặc nhân các số bạn đang lưu trữ, thì DECIMAL là tốt nhất.
Nếu bạn cần phân chia hoặc thực hiện bất kỳ hình thức số học hoặc đại số nào khác trên dữ liệu, bạn gần như chắc chắn sẽ hạnh phúc hơn với float. Các thư viện dấu phẩy động và trên bộ xử lý Intel, chính bộ xử lý dấu phẩy động, có TON các thao tác để sửa, sửa, phát hiện và xử lý các trường hợp ngoại lệ xảy ra khi thực hiện các hàm toán học điển hình - đặc biệt là các hàm siêu việt.
Về tính chính xác, tôi đã từng viết một hệ thống ngân sách tính toán phần trăm đóng góp của mỗi hơn 3.000 tài khoản, cho 3.600 đơn vị ngân sách, theo tháng cho nút hợp nhất của đơn vị đó, sau đó dựa trên ma trận tỷ lệ phần trăm (3.000 + x 12 x 3.600) Tôi đã nhân số tiền được ngân sách bởi các nút tổ chức cao nhất xuống 3 cấp tiếp theo của các nút tổ chức và sau đó tính toán tất cả (3.000 + 12) cho tất cả 3.200 đơn vị chi tiết từ đó. Hàng triệu và hàng triệu triệu triệu phép tính dấu phẩy động chính xác kép, bất kỳ một trong số đó sẽ loại bỏ tất cả các dự đoán đó trong một hợp nhất từ dưới lên trở lại mức cao nhất trong tổ chức.
Tổng sai số dấu phẩy động sau tất cả các phép tính đó là ZERO . Đó là vào năm 1986, và các thư viện dấu phẩy động ngày nay tốt hơn nhiều so với trước đây. Intel thực hiện tất cả các tính toán trung gian tăng gấp đôi độ chính xác 80 bit, nhưng tất cả đều loại trừ lỗi làm tròn. Khi ai đó nói với bạn "đó là lỗi dấu phẩy động", điều đó gần như chắc chắn KHÔNG đúng.
declare @float as float(10)
declare @Decimal as decimal(10)
declare @Inetger as int
set @float =10.7
set @Decimal =10.7
set @Inetger=@Decimal
print @Inetger
trong float khi đặt giá trị thành số nguyên in 10 nhưng ở số thập phân 11
FLOAT(m,n)
, nó dẫn đến hai vòng; Trong khi đó, nó không cung cấp bất cứ thứ gì sử dụng.