2014-09-29 13 views
6

Có một số phép toán số học hoặc bitwise có thể kiểm tra liệu một đôi phù hợp với một phao không mất mát của độ chính xác.Làm thế nào nhanh chóng kiểm tra xem phù hợp với đôi trong phao? (Java)

Không chỉ kiểm tra rằng phạm vi kép là trong phạm vi thả nổi, mà còn không có bit mantissa bị mất.

Bye

P.S .: Điều này trả lời cho vấn đề nửa đường cho C#: How to check if a double can fit into a float without conversion to infinity Nhưng tôi cần một giải pháp mà làm việc cho Java.

+1

Tại sao điều này sẽ là một ý tưởng tốt? Chắc chắn bộ nhớ không thể là mối quan tâm của bạn. Các biện minh kỹ thuật cho nó là gì? – duffymo

+2

Ví dụ khi nối tiếp hàng triệu đôi. Nếu bạn có thể dự trữ byte bằng cách sử dụng phao, bạn có thể đạt được rất nhiều tốc độ hoặc giảm không gian. Đặc biệt thú vị trên mạng chậm hoặc phương tiện chậm – RobAu

Trả lời

5

Làm thế nào về điều này:

double d = ...; 
if ((double)(float)d == d) { 
    System.out.println(d + " fits into float!"); 
} 

Ý tưởng là khá đơn giản: Đầu tiên chúng ta đúc để float và sau đó trở lại double và kiểm tra xem kết quả là vẫn như nhau. Nếu d không vừa với phao, thì một số độ chính xác sẽ bị mất tại số (float)d và do đó kết quả sẽ khác.

Nói đúng cách, dàn diễn viên quay lại double là không cần thiết vì nhà điều hành so sánh sẽ thực hiện thao tác này một cách ngầm định, vì vậy, (float)d == d cũng không sao.

Nếu bạn đang lo lắng về hiệu suất của điều này, bởi vì nhiều hoạt động nổi chậm hơn rất nhiều so với các hoạt động int tương đương: Điều này là khá nhiều không vấn đề ở đây. Chuyển đổi giữa float và double cực kỳ hiệu quả trong các CPU hiện đại. Nó thậm chí có thể được vectorized! Có các hướng dẫn cvtpd2pscvtps2pd trong bộ hướng dẫn SSE2 thực hiện chuyển đổi từ kép sang phao và ngược lại (4 giá trị được chuyển đổi cùng một lúc). Các hướng dẫn có độ trễ 4 chu kỳ trên tất cả các CPU Intel hỗ trợ chúng. 4 chu kỳ cho 4 chuyển đổi cực kỳ nhanh.

+0

Bỏ phiếu vì đây là giải pháp. Nhưng vẫn còn một chút do dự khi sử dụng nó, điều này có nghĩa là hai FOP. –

+1

@CookieMonster: FPU của CPU của bạn thường khá nhanh. Cắt xén 'double' thành' float' là một nhiệm vụ khá dễ dàng cho nó. Tôi nghi ngờ rằng bất kỳ phép dịch chuyển nào (nếu nó tồn tại) sẽ nhanh hơn thế này. – gexicide

+1

@CookieMonster: Tôi đã tìm kiếm độ trễ của các hướng dẫn truyền véc tơ hóa. Chúng khá tốt (xem đoạn đã chỉnh sửa cuối cùng của tôi trong câu trả lời). Tôi không có độ trễ của các đối tác chưa được kiểm duyệt, nhưng tôi khá chắc rằng chúng tương tự nhanh. – gexicide

6

Một giải pháp thẳng về phía trước có thể trông như thế này:

public class Scribble { 
    public static void main(String[] args) { 
     for (int i = 1; i <= 10; i++) { 

      double d = 1d/((double)i); 
      float f = (float) d; 

      boolean lossless = d == f; 

      System.out.println(d + " can be converted " + (lossless ? "lossless" : "only with loss")); 
     } 
    } 
} 

nó ra:

1.0 can be converted lossless 
0.5 can be converted lossless 
0.3333333333333333 can be converted only with loss 
0.25 can be converted lossless 
0.2 can be converted only with loss 
0.16666666666666666 can be converted only with loss 
0.14285714285714285 can be converted only with loss 
0.125 can be converted lossless 
0.1111111111111111 can be converted only with loss 
0.1 can be converted only with loss 

chỉnh sửa: tốc độ chương trình so sánh, method2 rằng có vẻ là nhanh nhất:

method1 | method2 | method3 
237094654 | 209365345 | 468025911 
214129288 | 209917275 | 448695709 
232093486 | 197637245 | 448153336 
249210162 | 200163771 | 460200921 
240685446 | 200638561 | 447061763 
332890287 | 337870633 | 450452194 
247054322 | 199045232 | 449442540 
235533069 | 200767924 | 452743201 
256274670 | 199153775 | 453373979 
298277375 | 198659529 | 456672251 
229360115 | 205883096 | 454198291 
25268| 224850463 | 452860277 
246047739 | 200070587 | 458091501 
304270790 | 204517093 | 463688631 
235058620 | 204675812 | 448639390 
260565871 | 205834286 | 458372075 
256008432 | 242574024 | 498943242 
311210028 | 208080237 | 478777466 
242014926 | 208995343 | 457901380 
239893559 | 205111348 | 451616471 

mã:

public class Scribble { 

    static int size = 1024*1024*100; 
    static boolean[] results = new boolean[size]; 
    static double[] values = new double[size]; 

    public static void main(String[] args) { 

     // generate values 
     for (int i = 0; i < size; i++) 
      values[i] = 1d/((double)i); 

     long start; 
     long duration; 

     System.out.println(" method1 | method2 | method3 "); 
     for (int i = 0; i < 20; i++) { 
      start = System.nanoTime(); 
      method1(size); 
      duration = System.nanoTime() - start; 
      System.out.printf("%9d", duration); 

      start = System.nanoTime(); 
      method2(size); 
      duration = System.nanoTime() - start; 
      System.out.printf(" | %9d", duration); 

      start = System.nanoTime(); 
      method3(size); 
      duration = System.nanoTime() - start; 
      System.out.printf(" | %9d\n", duration); 
     } 
    } 

    private static void method1(int size) { 
     boolean[] results = new boolean[size]; 
     for (int i = 0; i < size; i++) { 
      double d = values[i]; 
      float f = (float) d; 

      boolean lossless = d == f; 
      results[i] = lossless; 
     } 
    } 

    private static void method2(int size) { 
     for (int i = 0; i < size; i++) { 
      double d = values[i]; 
      results[i] = d == (double)(float)d; 
     } 
    } 

    private static void method3(int size) { 
     for (int i = 0; i < size; i++) { 
      double d = values[i]; 
      results[i] = Double.compare(d, (float) d) == 0; 
     } 
    } 
} 
+1

Cung cấp d-f == 0 giống như d == f. Nên ở đây? –

+0

Có thể là các biến thể cần thiết nếu sử dụng d-f == 0. Xem thêm: https://randomascii.wordpress.com/2012/05/20/thats-not-normalthe-performance-of-odd-floats/ "Sự cần thiết cho denormals". Nhưng tôi vẫn không chắc liệu điều này cũng xảy ra trong trường hợp cụ thể đi giữa nổi và tăng gấp đôi. –

1

Tương tự như đúc số để float và trở lại double và kiểm tra đẳng thức (==), Double.compare() cũng có thể được sử dụng:

double d = 2/3.0; 

// 0 means OK, d fits into float 
if (Double.compare(d, (float) d) == 0) 
    System.out.println("OK, fits into float."); 

Hơn nữa, vì so sánh một float để double ngầm sẽ đúc số float đến double, chúng tôi chỉ cần viết:

if ((float) d == d) 
    System.out.println("OK, fits into float."); 
1

Nếu bạn muốn biết liệu giá trị kép của bạn có phù hợp với phạm vi MAX và MIN của float bạn không thể sử dụng như (float)d == d vì d có thể phù hợp với phạm vi float nhưng không cần thiết có cùng số thập phân sau khi truyền.

Trong hơn trường hợp bạn phải so sánh với Float.MAX_VALUEFloat.MIN_VALUE

`return d <= Float.MAX_VALUE && d>0 Float.MIN_VALUE`; 
+0

Không chỉ tăng gấp đôi số lượng lớn hơn/nhỏ hơn float, nó cũng có * độ chính xác cao hơn *. – slartidan

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