2009-02-04 28 views
8

Tôi đã viết một phương pháp để chuyển đổi một số lượng nhất định từ ngày đến mili giây:Java hành vi hoạt động nhân

private long expireTimeInMilliseconds; 
... 
public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = expireTimeInDays * 24 * 60 * 60 * 1000; 
} 

Tôi đã có một thời gian khó khăn để tìm ra những gì tôi đã làm sai. Bây giờ câu hỏi của tôi: Lỗi đó có rõ ràng không?

Phương pháp điều chỉnh:

private long expireTimeInMilliseconds; 
... 
public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = ((long) expireTimeInDays) * 24 * 60 * 60 * 1000; 
} 

Nếu tôi không chuyển đổi số nguyên để lâu trước khi tính toán, tôi nhận được một kết quả sai hoàn toàn.

+2

Bạn cũng có thể nối thêm L vào hằng số. – starblue

+0

Tại sao mọi người muốn đóng cửa sổ này? Có vẻ như một câu hỏi hợp pháp có thể giúp người khác. Nếu đó là một bản sao chính xác, hãy nói như vậy. –

+1

Bạn cũng có thể khai báo các hằng số của bạn miễn là: 24L * 60L ... –

Trả lời

8

Có hiển nhiên không? Tôi đoán nó phụ thuộc vào bao lâu bạn đã sử dụng Java và bao nhiêu lần bạn đã phải đối phó với mili giây. Tất nhiên, sẽ ổn trong tối đa khoảng 24 ngày ...

Tôi nghĩ rằng gợi ý lớn nhất nên là System.currentTimeMillis() trả về một long. Đó là một dấu hiệu tốt cho thấy một số mili giây có thể lớn. Loại biến bạn đang đặt cũng phải là một gợi ý hay.

Tất nhiên, bạn cũng có cũng phải biết rằng nếu bạn thực hiện phép tính số học với int, kết quả sẽ là int với ngắt xung quanh. Cho dù đó là đủ rõ ràng hay không có thể được tranh luận, nhưng nó sẽ là một cuộc thảo luận khá vô nghĩa. Trong C# nếu bạn bật kiểm tra tràn trên, bạn đã tìm thấy lỗi khá nhanh chóng - nhưng sau đó không nhiều nhà phát triển làm điều đó (thực sự, tôi không mặc dù tôi có lẽ nên).

+0

Tôi đã mong đợi một thời gian dài, tôi đang sử dụng để làm việc với System.currentTimeMillis(). Bởi vì đó không phải là một mã mới, tôi không nhớ những gì tôi đã suy nghĩ khi tôi viết nó (có lẽ tôi đã mong đợi một số phép thuật từ trình biên dịch) ... –

+0

... Tôi nghĩ rằng sai lầm lớn của tôi, như bạn đã nói, là làm các phép toán số học với ints, mà không chú ý đến tràn int.Tôi quên các khái niệm cơ bản (trên thực tế tôi đã không tìm hiểu nó cho java, nhưng tôi đã học cho C/C++) –

2

Không, không rõ ràng.

Nhưng hãy tin tôi đi, sau vài năm thực hành và sửa lỗi như thế này bạn trở nên rất nhạy cảm về số nguyên tràn và chỉ làm điều đúng mà không hề nghĩ đến nó.

Đó là điều khiến mọi người vui vẻ. Chắc chắn không có dấu hiệu của thực hành mã xấu, sự thiếu hiểu biết hay như vậy.

+0

Điều thú vị là tôi đã viết mã cho hơn 20 năm nay, và đó là lần đầu tiên tôi phạm sai lầm cụ thể đó. Khi tôi là một nhà phát triển C/C++, tôi đã từng chú ý đến từng chi tiết nhỏ trên mã của mình. Tôi nghĩ rằng bây giờ tôi đang mong các trình biên dịch để làm một số "ma thuật" cho tôi ... –

7

Có, nó khá rõ ràng nếu bạn đã thực hiện nó trước đây. Bất cứ lúc nào bạn nhìn thấy một chuỗi các số nhân ra bạn sẽ tự động bắt đầu suy nghĩ về lỗi tràn số nguyên. Trong trường hợp này, bạn sẽ bị tràn nếu expireTimeInDays lớn hơn 24. Về mặt kỹ thuật, bạn nên suy nghĩ về lỗi tràn bất kỳ lúc nào bạn đang làm việc với số nguyên, nhưng nhân một nhóm chúng như thế này sẽ là một lá cờ đỏ rất lớn .

3

Biến toán hạng của bạn và số thứ tự là loại int. Kiểu dữ liệu int có giá trị lớn nhất là 2^31 -1. Do đó với số lượng lớn như vậy, kiểu dữ liệu của các luồng tràn int dẫn đến một câu trả lời dường như không chính xác.

Trong ví dụ đầu tiên của bạn, int chỉ được gán cho một phép gán dài cho biến xảy ra sau khi tính toán. Kết quả tính toán là int.

Ví dụ thứ hai, thực hiện phép toán đầu tiên trong một thời gian dài, khiến việc tính toán thành dài. Trong trường hợp này, kết quả tính toán là dài, do quảng cáo. Loại dữ liệu dài hơn đủ lớn để tính toán của bạn.

+0

có vẻ như Java có hành vi tương tự cho int tràn mà C. Tôi nghĩ rằng tôi đã mong đợi trình biên dịch để chuyển đổi nó (int to long) cho tôi. Đó là loại mã trông khá đơn giản (và nó là) ... Tôi chỉ phải chú ý hơn. –

3

Bạn có thể muốn biết rằng điều này được đề cập trong "Puzzlers Java" của Joshua Bloch và Neal Gafter.

alt text http://www.javapuzzlers.com/lg-puzzlers-cropped.jpg

Bạn sẽ tìm thấy nhiều cạm bẫy khác Java, bẫy và các trường hợp góc trong cuốn sách đó.

Tôi đồng ý với starblue đã để lại nhận xét. Nối một L vào số.

+0

Cảm ơn, đó là một cuốn sách hay. –

+0

bạn có thể chỉ ra chương liên quan không? – Pacerier

1

Chỉ để thêm vào các câu trả lời khác, tôi đã thấy nó hữu ích trong quá khứ để xác định các hằng số (public static final long) chẳng hạn như MILLISECS_DAY hoặc MILLISECS_HOUR. Dễ đọc và hữu ích hơn nhiều.

0

Tôi không cố gắng biện minh cho sai lầm của mình, nhưng sẽ rất tuyệt nếu trình biên dịch java đủ thông minh để quảng bá một thời gian dài trước khi tính toán (khi tính toán được gán cho một biến dài)

Nhân tiện, tôi đã từng làm việc với C/C++ và nếu đó là một chương trình C, tôi đã gặp vấn đề tương tự, nhưng vài năm trước, tôi đã cẩn thận hơn với loại hoạt động này.

tôi sẽ chú ý nhiều thời gian tiếp theo (hoặc chuyển sang python) ...: D

1

Một cách khác để viết những dòng này là

public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = (long) expireTimeInDays * 24 * 60 * 60 * 1000; 
} 

hoặc

public void setExpireTimeInDays(int expireTimeInDays) 
{ 
    expireTimeInMilliseconds = expireTimeInDays * 24L * 60 * 60 * 1000; 
} 
+0

Bằng cách này là tốt hơn, tôi thích để tránh đúc không cần thiết. cảm ơn –

1

Nếu bạn sử dụng FindBugs trên mã của bạn, nó sẽ phát hiện vấn đề chính xác này. "ICAST: Kết quả của phép nhân số nguyên thành dài." Ví dụ FindBugs là chính xác những gì bạn đang làm; tính số ngày tính bằng mili giây.

Vấn đề này không rõ ràng đối với tôi lần đầu tiên tôi gặp phải.

+0

Tôi hoàn toàn quên về FindBugs ... cảm ơn –

1

Có một số công cụ phân tích tĩnh (findbugs) sẽ tìm thấy loại lỗi này.

Toán số học trên máy tính có thể khó. Thứ tự của các vấn đề hoạt động có thể ảnh hưởng đến độ chính xác và chính xác theo những cách mà bạn không mong đợi. Ngày toán cũng có thể đáng ngạc nhiên khó khăn. Thường thì tốt hơn là nên sử dụng các thói quen Ngày/Lịch thay vì cố gắng tự làm toán nhưng các thường trình đó không phải là những thứ được thiết kế tốt nhất trong thư viện lớp java.

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