2010-01-06 31 views
5

Có ai có các đoạn mã tiện dụng để chuyển đổi IEEE 754 double sang cấp thấp hơn (resp. Superior) float, mà không thay đổi hoặc giả định bất kỳ điều gì về vòng hiện tại của FPU chế độ?Chuyển đổi kép thành phao mà không dựa vào chế độ làm tròn FPU

Lưu ý: ràng buộc này có thể ngụ ý không sử dụng FPU. Tôi hy vọng cách đơn giản nhất để làm điều đó trong các điều kiện này là đọc các bit của đôi trong một 64-bit dài và làm việc với điều đó.

Bạn có thể giả định endianness của sự lựa chọn của bạn vì đơn giản, và rằng đôi trong câu hỏi đã có sẵn thông qua các d lĩnh vực công đoàn dưới đây:

union double_bits 
{ 
    long i; 
    double d; 
}; 

tôi sẽ cố gắng để làm điều đó bản thân mình nhưng tôi chắc chắn Tôi sẽ giới thiệu các lỗi khó nhận biết đối với các số không chuẩn hóa hoặc số âm.

+0

trên các hệ thống glibc bạn tìm thấy một ieee754.h tập tin tiêu đề, trong đó xác định các công đoàn cho các loại dấu chấm động và một cấu trúc bitfield, vì vậy bạn có thể làm việc với mantissa và số mũ dễ dàng hơn, xin lỗi nhưng tôi không thể cung cấp cho bạn thực mã. – quinmars

Trả lời

3

Tôi nghĩ rằng các công việc sau, nhưng tôi sẽ nêu giả định của tôi đầu tiên :

  • số dấu phẩy động được lưu trữ ở định dạng IEEE-754 khi bạn triển khai,
  • Không tràn,
  • Bạn có nextafterf() khả dụng (được chỉ định trong C99).

Ngoài ra, rất có thể, phương pháp này không hiệu quả lắm.

#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 

int main(int argc, char *argv[]) 
{ 
    /* Change to non-zero for superior, otherwise inferior */ 
    int superior = 0; 

    /* double value to convert */ 
    double d = 0.1; 

    float f; 
    double tmp = d; 

    if (argc > 1) 
     d = strtod(argv[1], NULL); 

    /* First, get an approximation of the double value */ 
    f = d; 

    /* Now, convert that back to double */ 
    tmp = f; 

    /* Print the numbers. %a is C99 */ 
    printf("Double: %.20f (%a)\n", d, d); 
    printf("Float: %.20f (%a)\n", f, f); 
    printf("tmp: %.20f (%a)\n", tmp, tmp); 

    if (superior) { 
     /* If we wanted superior, and got a smaller value, 
      get the next value */ 
     if (tmp < d) 
      f = nextafterf(f, INFINITY); 
    } else { 
     if (tmp > d) 
      f = nextafterf(f, -INFINITY); 
    } 
    printf("converted: %.20f (%a)\n", f, f); 

    return 0; 
} 

Trên máy tính của tôi, nó in:

Double: 0.10000000000000000555 (0x1.999999999999ap-4) 
Float: 0.10000000149011611938 (0x1.99999ap-4) 
tmp: 0.10000000149011611938 (0x1.99999ap-4) 
converted: 0.09999999403953552246 (0x1.999998p-4) 

Ý tưởng là tôi đang chuyển đổi giá trị double đến một giá trị float — này có thể nhỏ hơn hoặc lớn hơn giá trị gấp đôi phụ thuộc vào chế độ làm tròn. Khi được chuyển đổi trở lại double, chúng tôi có thể kiểm tra xem nó có nhỏ hơn hoặc lớn hơn giá trị ban đầu hay không. Sau đó, nếu giá trị của float không đúng hướng, chúng tôi xem số float tiếp theo từ số được chuyển đổi theo hướng của số ban đầu.

+0

Cảm ơn bạn rất nhiều vì mã này. Tôi dần dần bị thuyết phục rằng đây là giải pháp ít bị lỗi nhất. Cảm ơn bạn đã chỉ ra 'nextafterf' nữa, tốt hơn là giảm/giảm các bit của' float' như thể nó là 'int'. Để giảm bớt rủi ro của 'f + 1' bằng' f', tôi có thể viết 'nextafterf (f, INFINITY)' thay thế không? –

+0

Tôi chỉ đọc các trang của người đàn ông, bản nháp chuẩn C và thử nó, và trông giống như 'INFINITY' sẽ hoạt động. –

+0

OK, tôi đã chỉnh sửa bài đăng của mình. Cảm ơn bạn đã bình luận. –

3

Để làm công việc này một cách chính xác hơn chỉ cần tái kết hợp mantissa và số mũ chút của việc kiểm tra này ra:

http://www.mathworks.com/matlabcentral/fileexchange/23173

liên quan

+0

Cảm ơn. Hàm 'doubles2halfp' có phức tạp như tôi lo sợ, nhưng ít nhất nó có một nửa các hằng số, vì vậy nó là một điểm khởi đầu tốt. –

+0

Tôi sẽ sử dụng mã đã cho làm tham chiếu và viết lại một cách tiếp cận đơn giản hơn, sử dụng & >> follwed bởi hoặc, và sau đó kiểm tra các số rất nhỏ và rất lớn. Lấy số lượng ca và vị trí bit trong nháy mắt từ http://babbage.cs.qc.edu/IEEE-754/Decimal.html – stacker

2

Tôi đã đăng mã để thực hiện việc này tại đây: https://stackoverflow.com/q/19644895/364818 và sao chép mã dưới đây để thuận tiện cho bạn.

// d is IEEE double, but double is not natively supported. 
    static float ConvertDoubleToFloat(void* d) 
    { 
     unsigned long long x; 
     float f; // assumed to be IEEE float 
     unsigned long long sign ; 
     unsigned long long exponent; 
     unsigned long long mantissa; 

     memcpy(&x,d,8); 

     // IEEE binary64 format (unsupported) 
     sign  = (x >> 63) & 1; // 1 
     exponent = ((x >> 52) & 0x7FF); // 11 
     mantissa = (x >> 0) & 0x000FFFFFFFFFFFFFULL; // 52 
     exponent -= 1023; 

     // IEEE binary32 format (supported) 
     exponent += 127; // rebase 
     exponent &= 0xFF; 
     mantissa >>= (52-23); // left justify 

     x = mantissa | (exponent << 23) | (sign << 31); 
     memcpy(&f,&x,4); 

     return f; 
    } 
+0

Cảm ơn. Dòng 'exponent & = 0xFF;' có nghĩa là khi nó thích hợp để trả về '± FLT_MAX' hoặc' ± inf', một 'float' với một số mũ lạ được trả về thay vào đó (và các kết quả bất thường cũng bị tắt). –

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