Biểu diễn giá trị tiền tệ trong Java [đã đóng]


94

Tôi hiểu rằng BigDecimal là phương pháp hay nhất được khuyến nghị để biểu diễn các giá trị tiền tệ trong Java. Bạn dùng gì? Có thư viện nào tốt hơn mà bạn thích sử dụng hơn không?


4
hãy xem JSR 354
yegor256,

1
Dưới đây là một lớp ngoại tệ mà bạn có thể sao chép và mở rộng: java-articles.info/articles/?p=254
Gilbert Le Blanc

Cũng xem triển khai tham chiếu của JSR-354 github.com/JavaMoney/jsr354-ri
đánh bại

Câu trả lời:


81

BigDecimaltất cả các cách. Tôi đã nghe nói về việc một số người tạo ra các lớp Cashhoặc Moneylớp học của riêng họ , đóng gói một giá trị tiền mặt với tiền tệ, nhưng dưới da nó vẫn là một BigDecimal, có thể là với BigDecimal.ROUND_HALF_EVENviệc làm tròn.

Chỉnh sửa: Như Don đã đề cập trong câu trả lời của mình , có những dự án có nguồn mở như timeandmoney và trong khi tôi hoan nghênh chúng vì đã cố gắng ngăn các nhà phát triển phải phát minh lại bánh xe, tôi chỉ không đủ tin tưởng vào một thư viện tiền alpha để sử dụng nó trong môi trường sản xuất. Bên cạnh đó, nếu bạn đào xung quanh dưới mui xe, bạn sẽ thấy họ sử dụng BigDecimalquá .


4
+1. Chúng tôi đã quyết định thêm một lớp vùng chứa sử dụng một loại tiền tệ. Điều này rất hữu ích khi hiển thị các giá trị tiền tệ trong bảng.
Daniel Hiller

1
vâng, đó là một cách tiếp cận khá phổ biến và nó có rất nhiều ý nghĩa. Một lưu ý cho điều này là khi bạn phải giao dịch với Yên Nhật, vì chúng không có mệnh giá tiền tệ nhỏ như xu, vì vậy nó cần các quy tắc làm tròn riêng.
ninesided

3
@ninesided đưa ra một ví dụ tuyệt vời về lý do tại sao tự cuốn sách của bạn là một câu trả lời tồi. "Ồ, nhân tiện, nó không hoạt động với $ CURRENCY_X." Đó là một dấu hiệu tốt cho thấy nó cũng không hoạt động đối với nhiều loại tiền tệ khác.
James Moore

1
@JamesMoore Tôi không đồng ý rằng "tự mình lăn xả" là một cách tiếp cận không tốt, bạn chỉ cần nhận thức được những hạn chế có thể có của cách tiếp cận mà bạn chọn, đó là lý do để tôi đề cập đến nó. Việc thực hiện các quy tắc làm tròn khác nhau cho mỗi loại tiền tệ là điều tầm thường, nhưng nếu hệ thống của bạn chỉ cần giao dịch bằng USD hoặc EUR thì bạn không cần phải thiết kế nhiều thứ.
ninesided

1
Hãy xem stackoverflow.com/questions/5134237/… để biết một lý do tại sao BigDecimal lại là một vấn đề. Kế toán toàn hành tinh chỉ là một đầm lầy của các trường hợp đặc biệt và cố gắng quét tất cả chúng dưới tấm thảm của BigDecimal chỉ không hiệu quả.
James Moore

52

Nó có thể hữu ích cho những người đến đây bằng các công cụ tìm kiếm để biết về JodaMoney: http://www.joda.org/joda-money/ .


Cảm ơn. Tôi có ý định thêm một ghi chú tiếp theo về Joda Money. Bạn đã sử dụng nó chưa?
dshaw

2
+1 trông thú vị, vui mừng khi thấy nó BigDecimalđược nâng cấp!
ninesided


8

Một thư viện tiện lợi mà tôi đã gặp trước đó là thư viện Joda-Money . Một trong những cách triển khai của nó thực sự dựa trên BigDecimal. Nó dựa trên đặc điểm kỹ thuật ISO-4217 cho tiền tệ và có thể hỗ trợ danh sách tiền tệ tùy chỉnh (được tải qua CVS).

Thư viện này có một số lượng nhỏ các tệp mà người ta có thể xem nhanh nếu cần sửa đổi. Joda-Money được xuất bản theo giấy phép Apache 2.0.


7

Nếu bạn chỉ sử dụng đô la và xu, tôi sẽ sử dụng giá trị dài (bù đắp bằng 2 chữ số thập phân). Nếu bạn cần thêm chi tiết, số thập phân lớn có thể là cách để đi.

Dù bằng cách nào, tôi có thể sẽ mở rộng lớp để có .toString () sử dụng định dạng chính xác và như một nơi để đặt các phương thức khác có thể xuất hiện (Trong một thời gian dài, nhân và chia sẽ trở nên tồi tệ nếu số thập phân không phải là không điều chỉnh)

Ngoài ra, nếu bạn sử dụng định nghĩa lớp và giao diện của riêng mình, thì bạn có thể thay thế việc triển khai theo ý muốn.


2
Hãy coi chừng, thậm chí dài cũng có thể là quá ngắn để có thể nắm giữ Khoản nợ Liên bang Hoa Kỳ bằng đồng xu ... nếu không phải là bây giờ thì trong vài năm tới.
Ingo

3
Tôi đồng ý - bất kỳ số lượng đô la khổng lồ nào (hoặc có thể nếu bạn đang theo dõi tiền bằng đồng Yên) thì bạn nên sử dụng BigDecimal - nhưng ngay cả khi đó tôi cũng sẽ nghiêm túc xem xét sử dụng một lớp container cho nó. Tôi nghĩ rằng hầu hết sự phức tạp của lập trình đến từ việc mọi người không xác định các lớp nhỏ, đơn giản xung quanh các tập hợp và các kiểu nội tại.
Bill K

3

BigDecimal hoặc một đại diện điểm cố định khác là những gì thường cần cho tiền.

Các biểu diễn và tính toán dấu phẩy động ( Double, Float) không chính xác, dẫn đến kết quả sai.


7
Nói một cách chính xác, BigDecimal cũng không chính xác; nó chỉ tương ứng tốt hơn với cách làm tròn số thập phân mà chúng ta quen dùng trong cuộc sống hàng ngày và cho phép bạn chỉ định các chế độ làm tròn.
Michael Borgwardt

1
@Michael Borgwardt BigDecimal khác với IEEE FP ở chỗ tỷ lệ rõ ràng được chỉ định. Mặc dù không phải tất cả các hoạt động đều chính xác, nhưng điều này đảm bảo rằng một tập hợp các hoạt động và hành vi luôn chính xác và tỷ lệ là không đổi trong khi tỷ lệ cho IEEE FP giảm dần theo giá trị.

1
Điều đó có liên quan gì đến tiền bạc? Các tổ chức kế toán trên khắp thế giới thường có những yêu cầu rất cụ thể về cách bạn tính toán bằng đơn vị tiền tệ của họ. BigDecimal có khớp chính xác với từng tiêu chuẩn này không? Nó sẽ làm như vậy vào năm tới, khi những tiêu chuẩn đó thay đổi? Và BigDecimal thậm chí không tiến gần đến việc chỉ định các quy tắc làm tròn hữu ích cho tiền tệ.
James Moore

2

Bạn phải rất cẩn thận khi đối phó với thời gian và tiền bạc.

Khi bạn đang làm việc với tiền, tôi hy vọng mọi người nên biết không bao giờ sử dụng phao hoặc kép.

Nhưng tôi không chắc về BigDecimal.

Trong hầu hết các trường hợp, bạn sẽ ổn nếu bạn chỉ theo dõi các xu trong một int hoặc dài. Bằng cách này, bạn sẽ không bao giờ xử lý một chữ số thập phân.

Bạn chỉ hiển thị đô la khi bạn in nó. Luôn làm việc với xu bên trong bằng cách sử dụng số nguyên. Điều này có thể phức tạp nếu cần chia hoặc cần sử dụng Math.abs ().

Tuy nhiên, bạn có thể quan tâm đến nửa xu, hoặc thậm chí một trăm xu. Tôi không biết cách tốt để làm điều này là gì. Bạn có thể chỉ cần giao dịch với phần nghìn xu và sử dụng lâu dài. Hoặc có thể bạn sẽ buộc phải sử dụng BigDecimal

Tôi sẽ đọc nhiều hơn về điều này, nhưng bỏ qua tất cả những người bắt đầu nói về việc sử dụng float hoặc double để đại diện cho tiền. Họ chỉ đang yêu cầu rắc rối.

Tôi cảm thấy lời khuyên của tôi không đầy đủ, vì vậy xin vui lòng thêm vào nó. Bạn đang đối phó với các loại nguy hiểm!


2
Tại sao bạn cần phải "buộc" sử dụng BigDecimal? Bạn không chắc chắn về điều gì? Nó rõ ràng là ưu việt hơn khi làm việc với xu, vì nó cho phép bạn chỉ định rõ ràng các chế độ làm tròn.
Michael Borgwardt

1
@MichaelBorgwardt: vâng, nó cho phép bạn chỉ định một tập hợp con nhỏ của các chế độ làm tròn mà bạn cần cho tiền tệ. Vì thế? (Gợi ý: làm tròn đơn vị tiền tệ, thường là do các tổ chức kế toán quốc gia quyết định. Họ hoàn toàn sẵn lòng ném vào những trường hợp đặc biệt kỳ lạ. Xem stackoverflow.com/questions/5134237/… để biết một trong nhiều lý do thú vị tại sao BigDecimal làm tròn hoàn toàn vô dụng ở đây.)
James Moore

@James: Chính xác thì nó "vô dụng" như thế nào? Làm thế nào việc thực hiện các trường hợp đặc biệt với BigDecimal sẽ khó hơn với thứ gì đó khác?
Michael Borgwardt

1
OK, hoàn toàn vô dụng là quá mạnh. Trong lớp phức tạp tóm tắt tiền tệ, các quy tắc làm tròn của BigDecimal có thể hữu ích trong một số trường hợp cụ thể để xây dựng một tập hợp con các cách làm tròn tiền tệ xảy ra. Nhưng trường hợp chung là các quy tắc làm tròn tiền tệ yêu cầu các cơ chế có thể thay đổi theo thời gian (vì các cơ quan kế toán của con người tạo ra các quy tắc và có thể tự do thay đổi chúng). Câu hỏi không phải về Euro (hoặc bất cứ thứ gì thay thế Euro vào tháng tới ...), hay đô la trong năm 2011, mà là về tiền tệ, vì vậy bạn phải đối mặt với rất nhiều phức tạp khó chịu.
James Moore

2

Tạo một lớp Tiền là cách để đi. Sử dụng BigDecimal (hoặc thậm chí là số nguyên) bên dưới. Sau đó sử dụng lớp Currency để xác định quy ước làm tròn.

Thật không may nếu không có toán tử quá tải Java làm cho nó khá khó chịu khi tạo các kiểu cơ bản như vậy.


2

Có một thư viện tốt hơn, timeandmoney . IMO, nó vượt trội hơn nhiều so với các thư viện do JDK cung cấp để đại diện cho 2 khái niệm này.


3
Câu trả lời này đã được đăng cách đây ba năm. Ngày nay, dự án timeandmoney vẫn là pre-alpha theo liên kết đó.
James Moore

1
@JamesMoore Cuộc gọi tốt. Câu trả lời hiện đã 7 năm và dự án vẫn chưa ổn định.
Navin

1

Chắc chắn không phải là BigDecimal. Có rất nhiều quy tắc đặc biệt để làm tròn và trình bày mà bạn phải lo lắng.

Martin Fowler khuyến nghị triển khai một lớp Tiền chuyên dụng để đại diện cho số lượng tiền tệ và cũng thực hiện các quy tắc chuyển đổi tiền tệ.


6
và kiểu dữ liệu cơ bản của lớp Tiền của anh ấy? BigDecimal.
ninesided

1
Đo không phải sự thật. Bạn có thể sử dụng Integer trong lớp tiền, đó là những gì Martin làm. Tôi đã làm việc này nhiều lần rồi.
egervari

Tuy nhiên, khuyến nghị là đúng; các phép tính liên quan đến tiền là một đầm lầy rộng lớn của các trường hợp đặc biệt thay đổi theo thời gian. BigDecimal có thể hữu ích như một phần nhỏ của giải pháp, nhưng nó chắc chắn không chung chung.
James Moore

1

Này, đây là một bài viết rất thú vị trên BigDecimal và một ví dụ minh họa về lý do tại sao đôi khi nó được sử dụng thay vì dùng gấp đôi. Hướng dẫn BigDecimal .


0

Bạn có thể sử dụng lớp DecimalFormat khi cuối cùng hiển thị giá trị tiền tệ. Nó cung cấp hỗ trợ bản địa hóa và khá dễ mở rộng.


0

Tôi sẽ gói BigDecimal trong lớp Money cũng có một loại tiền tệ giống như ai đó đã đề cập ở trên. Điều quan trọng là bạn phải thực hiện rất nhiều bài kiểm tra đơn vị và đặc biệt là nếu làm việc với các loại tiền tệ khác nhau. Ngoài ra, đó là một ý tưởng hay nếu bạn thêm một hàm tạo thuận tiện lấy một chuỗi hoặc một phương thức gốc thực hiện tương tự để bạn có thể viết các bài kiểm tra của mình như sau:

   assertEquals(Money.create("100.0 USD").add("10 GBP"),Money.create("116 USD"));

0

Luôn có những ràng buộc và chi tiết cụ thể liên quan. Bất kỳ ai không có đủ kinh nghiệm để đánh giá cao các vấn đề tinh vi được nêu trong bài viết sau đây nên nghiêm túc xem xét lại trước khi xử lý dữ liệu tài chính trong thế giới thực:

http://lemnik.wordpress.com/2011/03/25/bigdecimal-and-your-money

BigDecimal hầu như không phải là đại diện chính xác duy nhất hoặc phần duy nhất của câu đố. Với một số điều kiện nhất định, việc sử dụng loại Tiền được hỗ trợ bằng xu được lưu trữ dưới dạng số nguyên có thể là đủ và sẽ nhanh hơn nhiều so với BigDecimal. Đúng, điều đó ngụ ý việc sử dụng đô la làm tiền tệ và giới hạn số lượng nhưng những ràng buộc như vậy hoàn toàn có thể chấp nhận được đối với nhiều trường hợp sử dụng và tất cả các loại tiền tệ đều có các trường hợp đặc biệt để làm tròn và mệnh giá phụ, vì vậy không có giải pháp "chung".


1
Đây dường như là một bình luận trên một bài đăng khác thay vì một câu trả lời thực tế. Nó cũng quá tinh khiết. Hãy cố gắng cư xử văn minh hơn trong tương lai.
Slater Victoroff,
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.