Lưu trữ tiền trong một cột thập phân - độ chính xác và quy mô là gì?


173

Tôi đang sử dụng một cột thập phân để lưu trữ các giá trị tiền trên cơ sở dữ liệu và hôm nay tôi đã tự hỏi nên sử dụng độ chính xác và tỷ lệ nào.

Vì các cột được cho là có chiều rộng cố định sẽ hiệu quả hơn, tôi đã nghĩ điều tương tự có thể đúng với các cột thập phân. Là nó?

Và độ chính xác và quy mô tôi nên sử dụng? Tôi đã suy nghĩ chính xác 24/8. Là quá mức, không đủ hoặc ok?


Đây là những gì tôi đã quyết định làm:

  • Lưu trữ tỷ lệ chuyển đổi (khi áp dụng) trong chính bảng giao dịch, dưới dạng thả nổi
  • Lưu trữ tiền tệ trong bảng tài khoản
  • Số tiền giao dịch sẽ là một DECIMAL(19,4)
  • Tất cả các tính toán sử dụng tỷ lệ chuyển đổi sẽ được ứng dụng của tôi xử lý để tôi kiểm soát các vấn đề làm tròn

Tôi không nghĩ rằng tỷ lệ chuyển đổi nổi là một vấn đề, vì chủ yếu là để tham khảo và dù sao tôi cũng sẽ chuyển nó thành số thập phân.

Cảm ơn tất cả các đầu vào có giá trị của bạn.


2
Hãy tự hỏi mình câu hỏi: Có thực sự cần thiết để lưu trữ dữ liệu ở dạng thập phân? Tôi không thể lưu trữ dữ liệu dưới dạng Cents / Pennies -> số nguyên?
Terence

5
DECIMAL(19, 4) là một lựa chọn phổ biến, kiểm tra cái này cũng kiểm tra ở đây Định dạng tiền tệ thế giới để quyết định sử dụng bao nhiêu số thập phân, hy vọng sẽ giúp.
shaijut

Câu trả lời:


181

Nếu bạn đang tìm kiếm một kích cỡ phù hợp với tất cả, tôi đề nghị DECIMAL(19, 4)là một lựa chọn phổ biến (Google nhanh chóng thực hiện điều này). Tôi nghĩ rằng điều này bắt nguồn từ loại dữ liệu VBA / Access / Jet Money cũ, là loại thập phân điểm cố định đầu tiên trong ngôn ngữ; Decimalchỉ xuất hiện theo kiểu 'phiên bản 1.0' (nghĩa là không được triển khai đầy đủ) trong VB6 / VBA6 / Jet 4.0.

Nguyên tắc để lưu trữ các giá trị thập phân điểm cố định là lưu trữ ít nhất một vị trí thập phân nhiều hơn số bạn thực sự yêu cầu để cho phép làm tròn. Một trong những lý do để ánh xạ Currencyloại cũ ở mặt trước để DECIMAL(19, 4)nhập vào mặt sau là Currencydo tính chất làm tròn của các nhân viên ngân hàng, trong khiDECIMAL(p, s) được làm tròn bằng cách cắt ngắn.

Một vị trí thập phân bổ sung trong lưu trữ cho DECIMALphép thực hiện thuật toán làm tròn tùy chỉnh thay vì lấy mặc định của nhà cung cấp (và làm tròn số ngân hàng là đáng báo động, để nói rằng, ít nhất, đối với một nhà thiết kế hy vọng tất cả các giá trị kết thúc bằng 0,5 sẽ làm tròn từ 0) .

Vâng, DECIMAL(24, 8)âm thanh như quá mức cần thiết với tôi. Hầu hết các loại tiền tệ được trích dẫn đến bốn hoặc năm chữ số thập phân. Tôi biết các tình huống trong đó tỷ lệ thập phân 8 (hoặc nhiều hơn) cần thiết nhưng đây là nơi một số lượng tiền tệ 'bình thường' (nói Bốn chữ số thập phân) đã ủng hộ rata'd, ngụ ý sự chính xác thập phân nên giảm cho phù hợp (cũng xem xét một loại điểm nổi trong trường hợp như vậy). Và không ai có nhiều tiền như vậy ngày nay để yêu cầu độ chính xác thập phân là 24 :)

Tuy nhiên, thay vì cách tiếp cận một kích cỡ phù hợp với tất cả, một số nghiên cứu có thể theo thứ tự. Hỏi nhà thiết kế hoặc chuyên gia tên miền của bạn về các quy tắc kế toán có thể áp dụng: GAAP, EU, v.v. Tôi mơ hồ nhớ lại một số chuyển khoản nội bộ của EU với các quy tắc rõ ràng để làm tròn đến năm chữ số thập phân, do đó sử dụng DECIMAL(p, 6)để lưu trữ. Kế toán nói chung dường như ủng hộ bốn chữ số thập phân.


PS Tránh MONEYloại dữ liệu của SQL Server vì nó có vấn đề nghiêm trọng về độ chính xác khi làm tròn, trong số các cân nhắc khác như tính di động, v.v. Xem blog của Aaron Bertrand .


Microsoft và các nhà thiết kế ngôn ngữ đã chọn làm tròn ngân hàng vì các nhà thiết kế phần cứng đã chọn nó [trích dẫn?]. Ví dụ, nó được quy định trong các tiêu chuẩn của Viện Kỹ sư Điện và Điện tử (IEEE). Và các nhà thiết kế phần cứng đã chọn nó bởi vì các nhà toán học thích nó. Xem Wikipedia ; để diễn giải: Phiên bản năm 1906 của Xác suất và Lý thuyết về Lỗi gọi đây là 'quy tắc của máy tính' ("máy tính" nghĩa là con người thực hiện tính toán).


1
Xem câu trả lời nàytrang này để biết thêm về lý do tại sao các nhà thiết kế ngôn ngữ chọn làm tròn Ngân hàng.
Nick Chammas

1
onedaywhen: Làm tròn ngân hàng không phải là do Microsoft. @NickChammas: cũng không phải là một phát minh của nhà thiết kế ngôn ngữ. Microsoft và các nhà thiết kế ngôn ngữ về cơ bản đã chọn nó vì các nhà thiết kế phần cứng đã chọn nó; ví dụ, nó được quy định trong các tiêu chuẩn của Viện Kỹ sư Điện và Điện tử (IEEE). Và các nhà thiết kế phần cứng đã chọn nó bởi vì các nhà toán học thích nó. Xem en.wikipedia.org/wiki/Rounding#History ; để diễn giải: Phiên bản năm 1906 của Xác suất và Lý thuyết về Lỗi gọi đây là 'quy tắc của máy tính' ("máy tính" nghĩa là con người thực hiện tính toán).
phoog

9
Vậy tôi nên sử dụng gì cho Bitcoin?
Bộ công cụ

1
@encedaywhen tại sao DECIMAL(19, 4)phổ biến hơn DECIMAL(19, 2)? Hầu hết các loại tiền tệ thế giới chỉ có hai chữ số thập phân.
cokedude

3
@ zypA13510: yeah, khẳng định của tôi là như vậy mười năm trước! Nhưng đây là lần thứ ba tôi được bình chọn nhiều nhất cho câu trả lời và chiếm một phần lớn sự thay đổi liên quan đến đại diện SO của tôi vì vậy tôi rất thích :)
onedaywhen

105

Gần đây chúng tôi đã triển khai một hệ thống cần xử lý các giá trị bằng nhiều loại tiền tệ và chuyển đổi giữa chúng và tìm ra một vài điều khó khăn.

KHÔNG BAO GIỜ SỬ DỤNG SỐ ĐIỂM NỔI BẬT CHO TIỀN

Số học dấu phẩy động giới thiệu những điểm không chính xác có thể không được chú ý cho đến khi chúng làm hỏng điều gì đó. Tất cả các giá trị phải được lưu dưới dạng số nguyên hoặc loại thập phân cố định và nếu bạn chọn sử dụng loại thập phân cố định thì hãy đảm bảo bạn hiểu chính xác loại đó làm gì trong phần mềm (nghĩa là bên trong sử dụng số nguyên hoặc dấu phẩy động kiểu).

Khi bạn cần thực hiện tính toán hoặc chuyển đổi:

  1. Chuyển đổi giá trị thành dấu phẩy động
  2. Tính giá trị mới
  3. Làm tròn số và chuyển đổi lại thành số nguyên

Khi chuyển đổi số dấu phẩy động trở lại thành số nguyên ở bước 3, đừng chỉ sử dụng nó - hãy sử dụng hàm toán học để làm tròn số đó trước. Điều này thường sẽ làround , mặc dù trong trường hợp đặc biệt nó có thể floorhoặc ceil. Biết sự khác biệt và lựa chọn cẩn thận.

Lưu trữ loại số bên cạnh giá trị

Điều này có thể không quan trọng đối với bạn nếu bạn chỉ xử lý một loại tiền tệ, nhưng điều quan trọng đối với chúng tôi là xử lý nhiều loại tiền tệ. Chúng tôi đã sử dụng mã 3 ký tự cho một loại tiền tệ, chẳng hạn như USD, GBP, JPY, EUR, v.v.

Tùy thuộc vào tình huống, nó cũng có thể hữu ích để lưu trữ:

  • Cho dù số đó là trước hay sau thuế (và thuế suất là bao nhiêu)
  • Số có phải là kết quả của một chuyển đổi (và nó được chuyển đổi từ đâu)

Biết giới hạn chính xác của các số bạn đang xử lý

Đối với các giá trị thực, bạn muốn chính xác như đơn vị tiền tệ nhỏ nhất. Điều này có nghĩa là bạn không có giá trị nào nhỏ hơn một xu, một xu, yên, fen, v.v. Đừng lưu trữ các giá trị với độ chính xác cao hơn mà không có lý do.

Trong nội bộ, bạn có thể chọn xử lý các giá trị nhỏ hơn, trong trường hợp đó là một loại giá trị tiền tệ khác . Hãy chắc chắn rằng mã của bạn biết cái nào và không bị lẫn lộn. Tránh sử dụng các giá trị dấu phẩy động ngay cả ở đây.


Cộng tất cả các quy tắc đó lại với nhau, chúng tôi quyết định các quy tắc sau. Trong mã đang chạy, tiền tệ được lưu trữ bằng một số nguyên cho đơn vị nhỏ nhất.

class Currency {
   String code;       //  eg "USD"
   int value;         //  eg 2500
   boolean converted;
}

class Price {
   Currency grossValue;
   Currency netValue;
   Tax taxRate;
}

Trong cơ sở dữ liệu, các giá trị được lưu trữ dưới dạng một chuỗi theo định dạng sau:

USD:2500

Điều đó lưu trữ giá trị $ 25,00. Chúng tôi chỉ có thể làm điều đó vì mã liên quan đến tiền tệ không cần phải nằm trong chính cơ sở dữ liệu, vì vậy tất cả các giá trị có thể được chuyển đổi thành bộ nhớ trước tiên. Các tình huống khác sẽ không nghi ngờ cho vay chính mình cho các giải pháp khác.


Và trong trường hợp tôi không làm rõ sớm hơn, đừng sử dụng float!


1
Không bao giờ nói không bao giờ: đôi khi số tiền là pro rata'd và cần phải bổ sung lại sau. Ví dụ: chia tổng cổ tức (tương đối nhỏ) được chia theo số lượng cổ phiếu đang phát hành (tương đối nhỏ) để đưa ra ròng trên mỗi cổ phiếu. Đôi khi vòng nổi tốt hơn :)
onedaywhen

23
Tôi đứng bên cạnh tôi không bao giờ. Thông số dấu phẩy động có những điểm không chính xác sẽ cộng thêm nhiều phép tính bạn làm. Nếu bạn cần lưu trữ các giá trị nhỏ hơn một xu hoặc xu, thì hãy xác định mức độ chính xác bạn cần và tuân theo nó. Đừng dùng phao. Nghiêm túc. Đó là một ý tưởng tồi.
Marcus Downing

4
Câu trả lời này cũng phù hợp với các thực tiễn tốt nhất về javascript được Douglas Crockford nêu ra trong "Chuỗi Crockford trên JavaScript" của mình, trong đó ông khuyên nên thực hiện tất cả các tính toán tiền tệ trong PENNIES để tránh lỗi máy. Vì vậy, nếu bạn đang làm việc với các loại tiền tệ trong javascript, sẽ rất có ý nghĩa để lưu trữ giá trị theo cách này.
giấy tờ

1
@encedaywhen Những trường hợp chia sẻ trên mỗi cổ phần, bạn có thể tính tổng số tiền ủng hộ và so sánh với tổng số ban đầu và đưa ra chiến lược xử lý phần còn lại (khi sử dụng loại số thập phân / số nguyên).
Shiv

2
Đối với Mysql, tôi khuyên bạn nên lưu trữ số nguyên (2500) bigintnếu bạn có ý định sắp xếp theo số lượng. Và đừng lãng phí thời gian của bạn với PHP 32bit khi giao dịch với các số nguyên lớn, hãy nâng cấp lên 64 bit hoặc Node.JS;)
Ricky Boyce

4

Khi xử lý tiền trong MySQL, hãy sử dụng DECIMAL (13,2) nếu bạn biết độ chính xác của các giá trị tiền của mình hoặc sử dụng NHÂN ĐÔI nếu bạn chỉ muốn một giá trị gần đúng đủ nhanh. Vì vậy, nếu ứng dụng của bạn cần xử lý các giá trị tiền lên tới một nghìn tỷ đô la (hoặc euro hoặc bảng Anh), thì điều này sẽ hoạt động:

DECIMAL(13, 2)

Hoặc, nếu bạn cần tuân thủ GAAP thì hãy sử dụng:

DECIMAL(13, 4)

3
Bạn có thể liên kết đến phần cụ thể của hướng dẫn GAAP thay vì nội dung của tài liệu 2500 trang không? Cảm ơn.
ReactingToAngularVues

@ReactingToAngularVues có vẻ như trang đã thay đổi. Xin lỗi
pollux1er

2

4 vị trí thập phân sẽ cung cấp cho bạn độ chính xác để lưu trữ các đơn vị tiền tệ nhỏ nhất thế giới. Bạn có thể hạ nó xuống hơn nữa nếu bạn cần độ chính xác của micropayment (nanopayment?!).

Tôi cũng thích DECIMALcác loại tiền dành riêng cho DBMS, bạn sẽ an toàn hơn khi giữ loại logic đó trong ứng dụng IMO. Một cách tiếp cận khác cùng dòng đơn giản là sử dụng số nguyên [dài], với định dạng thành ¤unit.subunit cho khả năng đọc của con người (= ký hiệu tiền tệ) được thực hiện ở cấp ứng dụng.


1

Kiểu dữ liệu tiền trên SQL Server có bốn chữ số sau số thập phân.

Từ SQL Server 2000 Sách trực tuyến:

Dữ liệu tiền tệ đại diện cho số tiền tích cực hoặc tiêu cực. Trong Microsoft® SQL Server ™ 2000, dữ liệu tiền tệ được lưu trữ bằng cách sử dụng các loại dữ liệu tiền và loại tiền nhỏ. Dữ liệu tiền tệ có thể được lưu trữ đến độ chính xác của bốn chữ số thập phân. Sử dụng loại dữ liệu tiền để lưu trữ các giá trị trong phạm vi từ -922,337,203,685,477.5808 đến +922,337,203,685,477.5807 (yêu cầu 8 byte để lưu trữ giá trị). Sử dụng kiểu dữ liệu smallmoney để lưu trữ các giá trị trong phạm vi từ -214,748.3648 đến 214,748.3647 (yêu cầu 4 byte để lưu trữ giá trị). Nếu cần số lượng lớn hơn các vị trí thập phân, thay vào đó, hãy sử dụng loại dữ liệu thập phân.


1

Đôi khi bạn sẽ cần phải đi đến ít hơn một xu và có những loại tiền tệ quốc tế sử dụng số lượng rất lớn. Ví dụ: bạn có thể tính phí khách hàng 0,088 xu cho mỗi giao dịch. Trong cơ sở dữ liệu Oracle của tôi, các cột được định nghĩa là SỐ (20,4)


1

Nếu bạn sẽ thực hiện bất kỳ loại hoạt động số học nào trong DB (nhân tỷ lệ thanh toán, v.v.), có lẽ bạn sẽ muốn chính xác hơn nhiều so với những người ở đây đang đề xuất, vì những lý do tương tự mà bạn không bao giờ muốn sử dụng bất cứ thứ gì nhỏ hơn giá trị dấu phẩy động có độ chính xác kép trong mã ứng dụng.


Đó là những gì tôi đã nghĩ, nhưng về tỷ giá tiền tệ (nghĩa là chuyển đổi đô la Zimbawe sang USD). Tôi sẽ thực hiện một số thử nghiệm trên cơ sở dữ liệu mà tôi đang sử dụng (psql, sqlite) để xem cách chúng xử lý làm tròn số thập phân rất nhỏ.
Ivan

Ngoài ra, phao không có vấn đề chính xác trong một số dbms / ngôn ngữ?
Ivan

2
phao có vấn đề chính xác trong TẤT CẢ các ngôn ngữ.
Marcus Downing

Khuyến cáo phổ biến nhất hiện nay là sử dụng độ chính xác tùy ý (nghĩ BigDecimal), nhưng trong một thời gian dài, nó có độ chính xác kép (nghĩ doublethay vì float). Ngoài ra, độ chính xác tùy ý có hình phạt hiệu suất đáng kể trong một số trường hợp. Kiểm tra chắc chắn là phương pháp đúng đắn.
Hank Gay

0

Nếu bạn đang sử dụng Máy chủ động IBM Informix, bạn sẽ có một loại TIỀN là một biến thể nhỏ trên loại DECIMAL hoặc NUMERIC. Nó luôn là loại điểm cố định (trong khi DECIMAL có thể là loại điểm nổi). Bạn có thể chỉ định thang đo từ 1 đến 32 và độ chính xác từ 0 đến 32 (mặc định là thang đo 16 và độ chính xác là 2). Vì vậy, tùy thuộc vào những gì bạn cần lưu trữ, bạn có thể sử dụng DECIMAL (16,2) - vẫn đủ lớn để giữ Thiếu hụt Liên bang Hoa Kỳ, đến phần trăm gần nhất - hoặc bạn có thể sử dụng phạm vi nhỏ hơn hoặc nhiều chữ số thập phân hơn.


0

Tôi nghĩ rằng đối với phần lớn các yêu cầu của bạn hoặc khách hàng của bạn nên đưa ra mức độ chính xác và tỷ lệ sử dụng. Ví dụ: đối với trang web thương mại điện tử mà tôi đang làm việc chỉ giao dịch bằng tiền bằng GBP, tôi đã được yêu cầu giữ nó ở mức thập phân (6, 2).


0

Một câu trả lời muộn ở đây, nhưng tôi đã sử dụng

DECIMAL(13,2)

mà tôi nghĩ đúng nên cho phép tối đa 99.999.999.999,99.

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.