2009-10-01 14 views

Trả lời

26

Đó là vì giá trị float gần nhất là 1,3 không giống giá trị kép gần nhất với 1,3. Giá trị sẽ không phải là chính xác 1.3 - không thể được biểu diễn chính xác trong biểu diễn nhị phân không lặp lại.

Để cung cấp một sự hiểu biết khác nhau về việc tại sao điều này xảy ra, giả sử chúng ta có hai chữ số thập phân loại dấu chấm động - decimal5decimal10, nơi số lượng đại diện cho số chữ số có nghĩa. Bây giờ giả sử chúng tôi đã cố gắng gán giá trị "một phần ba" cho cả hai. Bạn sẽ kết thúc với

decimal5 oneThird = 0.33333 
decimal10 oneThird = 0.3333333333 

Rõ ràng các giá trị đó không bằng nhau. Đó chính là điều tương tự ở đây, chỉ với những căn cứ khác nhau liên quan.

Tuy nhiên nếu bạn hạn chế các giá trị để loại kém chính xác, bạn sẽ thấy họ bằng trong trường hợp đặc biệt này:

double d = 1.3d; 
float f = 1.3f; 
System.out.println((float) d == f); // Prints true 

Đó là không đảm bảo được các trường hợp, tuy nhiên. Đôi khi sự xấp xỉ từ chữ số thập phân đến biểu diễn kép, và sau đó tính xấp xỉ của giá trị đó với đại diện nổi, kết thúc là kém chính xác hơn so với số thập phân thẳng để xấp xỉ xấp xỉ. Một ví dụ về điều này 1.0000001788139343 (nhờ stephentyrone để tìm ví dụ này).

Somewaht an toàn hơn, bạn có thể làm việc so sánh giữa đôi, nhưng sử dụng một float đen trong việc bố trí ban đầu:

double d = 1.3f; 
float f = 1.3f; 
System.out.println(d == f); // Prints true 

Trong trường hợp sau, đó là một chút như nói rằng:

decimal10 oneThird = 0.3333300000 

Tuy nhiên, như được chỉ ra trong các nhận xét, bạn gần như chắc chắn không nên được so sánh các giá trị dấu phẩy động với ==. Đó là gần như không bao giờ điều phải làm, bởi vì chính xác loại điều này. Thông thường, nếu bạn muốn so sánh hai giá trị bạn thực hiện với một số loại so sánh bình đẳng "mờ", kiểm tra xem hai số đó có "đủ gần" cho mục đích của bạn hay không. Xem trang Java Traps: double để biết thêm thông tin.

Nếu bạn thực sự cần kiểm tra tính bình đẳng tuyệt đối, thường cho biết bạn nên sử dụng định dạng số khác ở địa điểm đầu tiên - ví dụ, đối với dữ liệu tài chính, bạn nên sử dụng BigDecimal.

+0

Rất đẹp giải thích, mặc dù nó có thể cho một giá trị thập phân cẩn thận crafted để thất bại so sánh '((float) decimalValueAsDouble == decimalValueAsFloat)', do làm tròn đôi. –

+0

@stephentyrone: Có, tôi nghi ngờ nó có thể là - mặc dù tôi chưa đưa ra bất kỳ ví dụ nào. –

+1

Điều duy nhất làm tôi lo lắng với bài viết này là bằng cách nào đó nó cho OP ấn tượng rằng anh ấy có thể sử dụng và sử dụng == nếu anh ta không hiểu các bình luận. Một người khác đã đăng liên kết này: http://firstclassthoughts.co.uk/java/traps/java_double_traps.html giải thích điều này khá tốt. – Fredrik

2

Không bao giờ kiểm tra sự bình đẳng giữa các số dấu phẩy động. Cụ thể, để trả lời câu hỏi của bạn, con số 1,3 rất khó thể hiện trong điểm nổi binar và các linh hồn và biểu diễn nổi khác nhau.

+0

Bạn sẽ nhận được +1 từ tôi nếu bài đăng của bạn không bị lỗi chính tả. –

11

Một phao là một số điểm nổi chính xác duy nhất. Một đôi là một số điểm nổi chính xác gấp đôi.Thông tin chi tiết tại đây: http://www.concentric.net/~Ttwang/tech/javafloat.htm

Lưu ý: Bạn nên kiểm tra sự bình đẳng chính xác cho các số dấu phẩy động. Hầu hết thời gian, bạn muốn làm một so sánh dựa trên một giá trị đồng bằng hoặc khoan dung.

Ví dụ:

float a = 1.3f; 
double b = 1.3; 
float delta = 0.000001f; 
if (Math.abs(a - b) < delta) 
{ 
    System.out.println("Close enough!"); 
} 
else 
{ 
    System.out.println("Not very close!"); 
} 

Một số số điện thoại không thể được biểu diễn chính xác tại điểm (ví dụ 0.01) nổi vì vậy bạn có thể nhận được kết quả bất ngờ khi bạn so sánh bình đẳng.

2

Đọc this article.

Bài viết trên minh họa rõ ràng với các ví dụ về kịch bản của bạn khi sử dụng các loại gấp đôi và nổi.

2
float a=1.3f; 
double b=1.3; 

Tại thời điểm này, bạn có hai biến chứa xấp xỉ nhị phân với số thực 1.3. Phép tính xấp xỉ đầu tiên là chính xác đến khoảng 7 chữ số thập phân và số thứ hai chính xác đến khoảng 15 chữ số thập phân.

if(a==b) { 

Biểu thức a==b được đánh giá theo hai giai đoạn. Đầu tiên, giá trị của a được chuyển đổi từ một số float thành số double bằng cách đệm biểu diễn nhị phân. Kết quả vẫn chỉ chính xác với khoảng 7 chữ số thập phân như một biểu diễn của Real 1.3. Tiếp theo bạn so sánh hai xấp xỉ khác nhau. Vì chúng khác nhau, kết quả của a==bfalse.

Có hai bài học để học:

  1. Floating point (và đôi) literals là hầu như luôn luôn xấp xỉ; ví dụ. số thực tế tương ứng với số 1.3f theo nghĩa đen không chính xác bằng số thực 1.3.

  2. Mỗi lần bạn thực hiện tính toán dấu phẩy động, lỗi sẽ xuất hiện. Các lỗi này có xu hướng tích tụ. Vì vậy, khi bạn so sánh các dấu phẩy động/số kép, thường là một sai lầm khi sử dụng "==" đơn giản, "<", v.v. Thay vào đó, bạn nên sử dụng |a - b| < delta trong đó delta được chọn phù hợp. (Và tìm ra những gì là thích hợp delta không phải lúc nào cũng thẳng về phía trước một trong hai.)

  3. Bạn nên đã lấy rằng khóa học trong Numerical Analysis :-)

+0

Vấn đề, về cơ bản, là trong khi có nhiều 'double' giá trị mà một giá trị' float' cụ thể có thể đại diện, hệ thống thực hiện so sánh 'double'-to-'float' bằng cách chọn một giá trị' double' cụ thể. Nếu tôi có các druthers của tôi, các chuyển đổi kép-to-float ngầm thường được cho phép và tiềm ẩn float-to-double nói chung bị cấm, nhưng các toán tử dấu phẩy động chỉ có thể được so sánh với các kiểu khớp chính xác (một số trường hợp có thể yêu cầu cả toán hạng tới ' float', nhưng những thứ khác, 'double'; không đủ chi phối để là mặc định" an toàn "). – supercat

0

Vấn đề là Java (và than ôi .NET cũng không nhất quán về việc liệu giá trị float có thể đại diện cho một số lượng chính xác hoặc một loạt các đại lượng hay không. Nếu số float được coi là đại diện cho số lượng chính xác bằng số của biểu mẫu Mant * 2^Exp, trong đó Mant là một số nguyên 0 đến 2^25 và Exp là số nguyên), thì cố gắng truyền bất kỳ số nào không thuộc dạng đó đến float .Nếu nó được coi là đại diện cho "locus của các con số mà một số đại diện cụ thể trong hình thức trên đã được coi là tốt nhất", sau đó một đôi-to-float cast sẽ là chính xác ngay cả đối với double giá trị không phải của các hình thức trên [ đúc số double đại diện tốt nhất cho số lượng float sẽ luôn mang lại số lượng float tốt nhất đại diện cho số lượng đó, mặc dù trong một số trường hợp góc (ví dụ: số lượng trong phạm vi 8888888.500000000001 đến 8888888.500000000932) float được chọn có thể là một vài phần nghìn tỷ tệ hơn so với số lượng thực hiện tốt nhất có thể của float của số lượng thực tế].

Để sử dụng sự tương tự, giả sử hai người có một đối tượng dài mười centimet và họ đo nó. Bob sử dụng một bộ calibers đắt tiền và xác định rằng đối tượng của anh ta là 3.937008 "dài. Joe sử dụng thước đo băng và xác định rằng đối tượng của anh dài 3 15/16". Các vật thể có cùng kích thước không? Nếu người ta chuyển đổi phép đo của Joe thành một phần triệu của một inch (3.937500 ") thì các phép đo sẽ xuất hiện khác nhau, nhưng một phép đo chuyển đổi phép đo của Bob thành 1/256" gần nhất, chúng sẽ xuất hiện như nhau. Mặc dù so sánh trước đây có vẻ có vẻ "chính xác" hơn, nhưng so sánh trước lại có ý nghĩa hơn. Phép đo của Joe nếu 3 15/16 "không thực sự có nghĩa là 3.937500" - nó có nghĩa là "khoảng cách mà, sử dụng thước đo băng, không thể phân biệt được từ ngày 15/16". Và 3.937008 ", giống như kích thước của vật thể của Joe, khoảng cách sử dụng thước đo băng sẽ không thể phân biệt được từ ngày 15/16.

Thật không may, mặc dù sẽ có ý nghĩa hơn khi so sánh các phép đo bằng độ chính xác thấp hơn, Các quy tắc so sánh dấu chấm động của Java giả định rằng một số float đại diện cho một số lượng chính xác duy nhất và thực hiện các so sánh trên cơ sở đó. đến double sẽ khớp với giá trị bắt đầu), trong các so sánh bình đẳng trực tiếp chung giữa floatdouble không có ý nghĩa. Mặc dù Java không yêu cầu nó, bạn phải luôn luôn cast các toán hạng của một float-p so sánh bình đẳng oint là cùng loại. Các ngữ nghĩa là kết quả từ việc đúc double thành float trước khi so sánh khác với việc đúc float đến double và hành vi Java chọn theo mặc định (bỏ float đến double) thường sai về mặt ngữ nghĩa.

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