2010-10-05 30 views
9

Tôi đã kế thừa một dự án trong đó số tiền sử dụng loại kép.Tăng gấp đôi tiền của tôi: khung của tôi sử dụng gấp đôi số tiền tiền mặt

Tệ hơn nữa, khuôn khổ mà nó sử dụng và các lớp của riêng khung công tác, sử dụng gấp đôi tiền.

Khung ORM cũng xử lý truy xuất các giá trị từ (và lưu trữ đến) cơ sở dữ liệu. Trong các giá trị tiền cơ sở dữ liệu là kiểu số (19, 7), nhưng khung ORM ánh xạ chúng để tăng gấp đôi.

Ngắn hoàn toàn bỏ qua các lớp khung và ORM, có bất kỳ điều gì tôi có thể làm để tính giá trị tiền chính xác không?

Chỉnh sửa: vâng, tôi biết BigDecimal nên được sử dụng. Vấn đề là tôi gắn chặt với một khuôn khổ mà ở đó, ví dụ: lớp framework.commerce.pricing.ItemPriceInfo có các thành viên tăng gấp đôi mRawTotalPrice; và double mListPrice. Mã riêng của ứng dụng của công ty tôi mở rộng, ví dụ: ItemPriceInfoClass này.

Thực tế, tôi không thể nói với công ty của tôi, "phế liệu hai năm làm việc, và hàng trăm ngàn đô la chi tiêu, căn cứ mã trên khuôn khổ này, vì lỗi làm tròn"

+10

+1 cho tiêu đề "Tăng gấp đôi số tiền của tôi" – Marko

+5

Để hiểu tình huống của bạn. Nhưng: tiền mặt và kế toán thích hợp có xu hướng quan trọng đối với các tập đoàn. Vì vậy, làm các vụ kiện cẩu thả. Bạn đang ở một nơi khó khăn: một mặt, khuyên rằng công ty đốt cháy nhiều năm phát triển để sửa chữa những gì dường như là một vấn đề không. Mặt khác, mời các vấn đề lớn xuống đường. Nếu tôi là bạn, tôi sẽ cụm từ nó vào quản lý của tôi theo cách đó, và để họ thực hiện cuộc gọi. Bạn đã có một vấn đề trách nhiệm pháp lý ở đây, và đó là những gì sếp của bạn ở đó. Ghi lại bất kỳ quyết định nào. –

+2

Với những gì @Michael Petrotta nói, anh ấy đúng 100%. Tùy thuộc vào những gì bạn đang làm, họ có thể là vấn đề trách nhiệm lớn đến ống xuống từ quyết định này. Ngoài ra, khuôn khổ này - nó là một khuôn khổ thương mại? Thay vào đó, bạn có thể thuyết phục họ thay đổi thành BigDecimal hay có phiên bản nào không?Nếu không, bạn sẽ cần phải đầu con đường tái cấu trúc gia tăng, đó là một tình huống khủng khiếp nơi bạn đang có - Tôi cảm thấy cho bạn. – aperkins

Trả lời

10

Nếu có thể chấp nhận được, hãy coi loại tiền tệ là tích phân. Nói cách khác, nếu bạn đang làm việc ở Mỹ, hãy theo dõi xu thay vì đô la, nếu xu cung cấp mức độ chi tiết bạn cần. Các cặp đôi có thể biểu diễn chính xác các số nguyên lên đến a very large value (2^53) (không làm tròn lỗi đến giá trị đó).

Nhưng thực sự, điều đúng đắn cần làm là bỏ qua hoàn toàn khuôn khổ và sử dụng điều gì đó hợp lý hơn. Đó là một sai lầm nghiệp dư cho khung làm việc - ai biết điều gì khác đang ẩn nấp?

6

Tôi không thấy bạn đề cập đến tái cấu trúc. Tôi nghĩ đó là lựa chọn tốt nhất của bạn ở đây. Thay vì ném cùng một số hacks để có được những điều làm việc tốt hơn cho bây giờ, tại sao không sửa chữa nó đúng cách?

Dưới đây là một số thông tin về double vs BigDecimal. Bài đăng này đề xuất sử dụng BigDecimal mặc dù bài đăng chậm hơn.

+1

[Java hiệu quả] (http://java.sun.com/docs/books/effective/) cũng đề xuất nó (2nd Edition, Item 48). –

+2

Tôi nên hy vọng nó cho thấy 'BigDecimal' ... tốc độ không có nghĩa là nhiều nếu số và tính toán của bạn là _incorrect_ vì' double'. – ColinD

+0

Từ OP, có vẻ như anh ta * không thể * tái cấu trúc làm khung (mà tôi cho rằng anh ta không có mã nguồn) sử dụng số học FP. – gustafc

1

Vâng, bạn không có nhiều tùy chọn đó trong thực tế:

Bạn có thể cấu trúc lại dự án để sử dụng ví dụ: BigDecimal (hoặc cái gì đó phù hợp hơn với nhu cầu của nó) để đại diện cho tiền.

Phải hết sức cẩn thận khi tràn/tràn và mất độ chính xác, có nghĩa là thêm hàng nghìn lần kiểm tra và tái cấu trúc tỷ lệ lớn hơn của hệ thống một cách không cần thiết. Chưa kể có bao nhiêu nghiên cứu sẽ là cần thiết nếu bạn muốn làm điều đó.

Giữ mọi thứ theo cách của chúng và hy vọng không có thông báo nào (đây là trò đùa).

IMHO, giải pháp tốt nhất sẽ đơn giản là tái cấu trúc điều này. Nó có thể là một số tái cấu trúc nặng nề, nhưng cái ác đã được thực hiện và tôi tin rằng nó sẽ là lựa chọn tốt nhất của bạn.

nhất, Vassil

T.B. Oh và bạn có thể coi tiền là số nguyên (đếm xu), nhưng điều đó nghe có vẻ không hay nếu bạn định chuyển đổi tiền tệ, tính lãi, v.v.

+0

Mất chính xác có thể, nhưng "nghiêm trọng/tràn" là bạn nghiêm trọng? –

+0

tràn kép cho các giá trị trên 10^308 và dòng dưới cho các giá trị nhỏ hơn 10^-308. Loại tiền tệ nào có thứ tự sắp xếp này? –

+0

Ok, tràn ở mức cao không thể xảy ra, nhưng bạn không thể biết những hoạt động nào sẽ được thực hiện trên những con số này và sự cố tràn là một khả năng trong một số trường hợp. – vstoyanov

2

Rất nhiều người sẽ đề xuất sử dụng BigDecimal và nếu bạn không biết làm thế nào để sử dụng làm tròn trong dự án của bạn, đó là những gì bạn nên làm.

Nếu bạn biết cách sử dụng số thập phân làm tròn chính xác, hãy sử dụng gấp đôi. Nhiều đơn đặt hàng của nó về cường độ nhanh hơn, rõ ràng hơn và đơn giản hơn và do đó ít lỗi hơn dễ bị IMHO. Nếu bạn sử dụng đô la và xu (hoặc cần hai chữ số thập phân), bạn có thể nhận được kết quả chính xác cho các giá trị lên đến 70 nghìn tỷ đô la.

Về cơ bản, bạn sẽ không gặp lỗi tròn nếu bạn sửa lỗi bằng cách sử dụng làm tròn approriate.

BTW: Ý nghĩ về các lỗi làm tròn gây khủng bố vào trung tâm của nhiều nhà phát triển, nhưng không phải là ngẫu nhiên lỗi và bạn có thể quản lý chúng một cách dễ dàng.

EDIT: xem xét ví dụ đơn giản này về lỗi làm tròn.

double a = 100000000.01; 
    double b = 100000000.09; 
    System.out.println(a+b); // prints 2.0000000010000002E8 

Có một số chiến lược làm tròn có thể có. Bạn có thể làm tròn kết quả khi in/hiển thị. ví dụ.

System.out.printf("%.2f%n", a+b); // prints 200000000.10 

hoặc tròn kết quả toán học

double c = a + b; 
    double r= (double)((long)(c * 100 + 0.5))/100; 
    System.out.println(r); // prints 2.000000001E8 

Trong trường hợp của tôi, tôi làm tròn kết quả khi gửi từ máy chủ (bằng văn bản cho một ổ cắm và một tập tin), nhưng sử dụng thói quen của riêng tôi để tránh bất kỳ tạo đối tượng.

Một chức năng vòng chung hơn như sau, nhưng nếu bạn có thể sử dụng printf hoặc DecimalFormat, có thể đơn giản hơn.

private static long TENS[] = new long[19]; static { 
    TENS[0] = 1; 
    for (int i = 1; i < TENS.length; i++) TENS[i] = 10 * TENS[i - 1]; 
} 

public static double round(double v, int precision) { 
    assert precision >= 0 && precision < TENS.length; 
    double unscaled = v * TENS[precision]; 
    assert unscaled > Long.MIN_VALUE && unscaled < Long.MAX_VALUE; 
    long unscaledLong = (long) (unscaled + (v < 0 ? -0.5 : 0.5)); 
    return (double) unscaledLong/TENS[precision]; 
} 

lưu ý: bạn có thể sử dụng BigDecimal để thực hiện làm tròn cuối cùng. esp nếu bạn cần một phương pháp vòng specifc.

+2

Tôi đã không downvote, nhưng không thực sự cung cấp một số hướng dẫn về cách quản lý các lỗi làm tròn, câu trả lời chỉ là không hữu ích. – Yishai

+0

Nếu bạn đang đối phó với số tiền lớn, điều này là không thực tế (nghĩ rằng hàng triệu và hàng tỷ đô la được thêm/trừ, vv). Ngoài ra, tùy thuộc vào phần cứng, các lỗi làm tròn có thể khác nhau, điều này sẽ cung cấp các điểm đau tiềm ẩn trong tương lai và chắc chắn có thể gây ra hành vi không thể đoán trước. Nếu độ chính xác là cần thiết, bạn không sử dụng các cấu trúc dữ liệu ước tính điểm động (double/float) và thay vào đó tập trung vào độ chính xác thay thế. – aperkins

+0

Điều đáng nói là tôi cũng không downvote. – vstoyanov

0

Tôi nghĩ rằng tình huống này ít nhất có thể bán được cho mã của bạn. Bạn nhận được giá trị gấp đôi thông qua khung công tác ORM. Sau đó bạn có thể chuyển đổi nó sang BigDecimal bằng cách sử dụng phương thức static valueOf (xem here vì lý do) trước khi thực hiện bất kỳ phép tính/tính toán nào trên nó, và sau đó chuyển đổi nó thành gấp đôi chỉ để lưu trữ nó.

Vì bạn vẫn đang mở rộng các lớp này, bạn có thể thêm getters cho giá trị gấp đôi của bạn để nhận chúng dưới dạng BigDecimal khi bạn cần.

Điều này có thể không bao gồm 100% các trường hợp (tôi sẽ đặc biệt lo lắng về những gì trình điều khiển ORM hoặc JDBC đang thực hiện để chuyển đổi gấp đôi về kiểu số), nhưng tốt hơn rất nhiều so với thực hiện toán học trên nguyên đôi.

Tuy nhiên, tôi không được thuyết phục rằng cách tiếp cận này thực sự rẻ hơn cho công ty trong thời gian dài.

Các vấn đề liên quan