2012-01-19 37 views
8

Trong một ứng dụng hiệu suất rất cao, chúng tôi tìm thấy CPU có thể tính toán số học dài nhanh hơn đáng kể sau đó với tăng gấp đôi. Tuy nhiên, trong hệ thống của chúng tôi nó đã được xác định rằng chúng tôi không bao giờ cần nhiều hơn 9 chữ số thập phân của độ chính xác. Vì vậy, chúng tôi sử dụng thời gian dài cho tất cả các số học dấu chấm động với độ chính xác 9 điểm được hiểu.Chuyển đổi mantissa và exponent thành đôi

Tuy nhiên, ở một số phần của hệ thống, điều này thuận tiện hơn do khả năng đọc được để làm việc với đôi. Vì vậy, chúng tôi phải chuyển đổi giữa giá trị dài mà giả định 9 chữ số thập phân thành hai lần.

Chúng tôi thấy đơn giản là lấy dài và chia cho 10 thành lũy thừa 9 hoặc nhân với 1 chia cho 10 thành lũy thừa 9 cho phép biểu diễn không chính xác trong một đôi.

Để giải quyết chúng tôi sử dụng Math.Round(value,9) để cung cấp các giá trị chính xác.

Tuy nhiên, Math.Round() có hiệu suất rất chậm. Vì vậy, ý tưởng của chúng tôi tại thời điểm này là trực tiếp chuyển đổi phần định trị và số mũ thành định dạng nhị phân của một đôi vì - theo cách đó, sẽ không cần làm tròn.

Chúng tôi đã học trực tuyến cách kiểm tra các bit của một đôi để có được phần mềm và số mũ nhưng khó hiểu để tìm ra cách đảo ngược để lấy một phần mềm và số mũ và chế tạo một đôi bằng cách sử dụng các bit.

Mọi đề xuất?

[Test] 
public unsafe void ChangeBitsInDouble() 
{ 
    var original = 1.0D; 
    long bits; 
    double* dptr = &original; 
    //bits = *(long*) dptr; 
    bits = BitConverter.DoubleToInt64Bits(original); 
    var negative = (bits < 0); 
    var exponent = (int) ((bits >> 52) & 0x7ffL); 
    var mantissa = bits & 0xfffffffffffffL; 
    if(exponent == 0) 
    { 
     exponent++; 
    } 
    else 
    { 
     mantissa = mantissa | (1L << 52); 
    } 
    exponent -= 1075; 

    if(mantissa == 0) 
    { 
     return; 
    } 

    while ((mantissa & 1) == 0) 
    { 
     mantissa >>= 1; 
     exponent++; 
    } 

    Console.WriteLine("Mantissa " + mantissa + ", exponent " + exponent); 

} 
+3

Bạn có chắc chắn giá trị bạn có chính xác được thể hiện bằng 'double' không? – Justin

+0

có thể điều này sẽ giúp ích, tôi không muốn đọc tất cả chỉ để giúp bạn: P http://steve.hollasch.net/cgindex/coding/ieeefloat.html – MrFox

Trả lời

1

Bạn không nên sử dụng hệ số tỷ lệ 10^9, bạn nên sử dụng 2^30 thay thế.

+0

Thansk chúng tôi vừa nhận ra rằng mã trên không phải là ' t hoàn tất. Và tìm hiểu rằng số mũ kép được biểu diễn bằng bit sử dụng số mũ nhị phân thay vì số mũ thập phân. Vì vậy, câu trả lời của bạn làm cho một thế giới có ý nghĩa tốt. – Wayne

+0

Điều này sẽ làm cho đại diện dài không thể đọc được nhưng sẽ cải thiện đáng kể hiệu suất và chuyển đổi trở lại gấp đôi sẽ được làm sáng nhanh. Vì vậy, nó là một sự cân bằng tuyệt vời. Cảm ơn! – Wayne

+0

Cũng có vẻ hợp lý nhưng trong thử nghiệm sau khi chia cho các yếu tố để chuyển đổi trở lại gấp đôi nó đi ra không chính xác: var chuyển đổi = 1 << 30; giá gấp đôi = 45.454945768D; kết quả dài1 = (dài) (giá * chuyển đổi); kết quả kép2 = ((gấp đôi) kết quả1)/chuyển đổi; Assert.AreEqual (kết quả2, giá); – Wayne

0

Như bạn đã nhận ra theo câu trả lời khác, tăng gấp đôi công việc bằng dấu phẩy động nhị phân thay vì dấu phẩy động thập phân, và do đó cách tiếp cận ban đầu không hoạt động. Nó cũng không rõ ràng nếu nó có thể làm việc với một công thức cố ý đơn giản, bởi vì nó không rõ ràng phạm vi tối đa bạn cần là gì, vì vậy làm tròn trở nên không thể tránh khỏi.

Sự cố thực hiện nhanh như vậy nhưng chính xác được nghiên cứu kỹ và thường được hỗ trợ bởi các hướng dẫn CPU. Cơ hội duy nhất của bạn để đánh bại chuyển đổi được tích hợp sẵn là:

  1. Bạn đạt bước đột phá toán học xứng đáng với một số giấy tờ nghiêm túc được viết về nó.
  2. Bạn loại trừ đủ trường hợp sẽ không xảy ra trong ví dụ của riêng bạn mà trong khi các trình cắm sẵn thì tốt hơn nói chung là của bạn được tối ưu hóa để sử dụng cho riêng bạn.

Trừ khi phạm vi giá trị bạn sử dụng rất hạn chế, khả năng cắt ngắn chuyển đổi giữa độ chính xác kép IEEE 754 và số nguyên dài trở nên nhỏ hơn và nhỏ hơn.

Nếu bạn đang ở điểm mà bạn phải trang trải hầu hết các trường hợp bao gồm IEEE 754 hoặc thậm chí là một tỷ lệ lớn trong số đó, thì cuối cùng bạn sẽ làm mọi việc chậm hơn.

Tôi muốn khuyên bạn nên ở lại với những gì bạn có, di chuyển các trường hợp trong đó double thuận tiện hơn để gắn bó lâu mặc dù có sự bất tiện hoặc nếu cần thiết sử dụng decimal.Bạn có thể tạo một decimal từ một long dễ dàng với:

private static decimal DivideByBillion (long l) 
{ 
    if(l >= 0) 
    return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, false, 9); 
    l = -l; 
    return new decimal((int)(l & 0xFFFFFFFF), (int)(uint)(l >> 32), 0, true, 9); 
} 

Bây giờ, decimal là độ lớn chậm hơn để sử dụng trong số học hơn double (chính xác bởi vì nó thực hiện một cách tiếp cận tương tự như của bạn trong câu hỏi mở, nhưng với một số mũ khác nhau và mantissa lớn hơn). Nhưng nếu bạn chỉ cần một cách thuận tiện để có được giá trị để hiển thị hoặc hiển thị chuỗi, thì việc chuyển đổi thủ công thành decimal có lợi thế hơn việc chuyển đổi thủ công thành double, do đó, có thể xem giá trị.

+0

Hiệu suất vẫn còn rất quan trọng trong mã nơi đôi được sử dụng. Nó chỉ là họ là người sử dụng bổ sung và người dùng mong đợi cho người sử dụng số dấu chấm động. Sử dụng số thập phân chắc chắn là thiết thực cho người dùng nhưng đáng thất vọng vì hiệu suất của bất kỳ thứ gì họ làm với họ. Vì tất cả đầu vào của chúng tôi là từ đôi (từ chuỗi hoặc tăng gấp đôi ghi âm dưới dạng nhị phân) thì tôi đang nghĩ đến việc giải nén câu chú ... làm toán với điều đó ... sau đó chỉ cần thay thế mantissa cho đầu ra. Điều đó hy vọng sẽ tránh làm tròn và thiếu chính xác. – Wayne

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