2012-10-10 53 views
5

Làm cách nào để chuyển đổi integer thành half precision float (được lưu trữ thành mảng unsigned char[2]). Phạm vi cho int đầu vào sẽ là từ 1-65535. Độ chính xác thực sự không phải là một mối quan tâm.Chuyển đổi int thành 16bit float (một nửa điểm chính xác) trong C++

Tôi đang làm điều gì đó tương tự để chuyển đổi thành 16bit int thành một unsigned char[2], nhưng tôi hiểu không có half precision float kiểu dữ liệu C++. Ví dụ về điều này bên dưới:

int16_t position16int = (int16_t)data; 
memcpy(&dataArray, &position16int, 2); 
+2

lẽ rele vant: http://gamedev.stackexchange.com/a/17410/9333 – slaphappy

+0

Với mẹo từ Goz có thể trợ giúp: [32-bit đến 16-bit Floating Point Conversion] (http://stackoverflow.com/q/ 1659440/237483) –

Trả lời

4

Đó là một điều rất đơn giản, tất cả các thông tin mà bạn cần là trong Wikipedia . thực hiện

mẫu:

#include <stdio.h> 

unsigned int2hfloat(int x) 
{ 
    unsigned sign = x < 0; 
    unsigned absx = ((unsigned)x^-sign) + sign; // safe abs(x) 
    unsigned tmp = absx, manbits = 0; 
    int exp = 0, truncated = 0; 

    // calculate the number of bits needed for the mantissa 
    while (tmp) 
    { 
    tmp >>= 1; 
    manbits++; 
    } 

    // half-precision floats have 11 bits in the mantissa. 
    // truncate the excess or insert the lacking 0s until there are 11. 
    if (manbits) 
    { 
    exp = 10; // exp bias because 1.0 is at bit position 10 
    while (manbits > 11) 
    { 
     truncated |= absx & 1; 
     absx >>= 1; 
     manbits--; 
     exp++; 
    } 
    while (manbits < 11) 
    { 
     absx <<= 1; 
     manbits++; 
     exp--; 
    } 
    } 

    if (exp + truncated > 15) 
    { 
    // absx was too big, force it to +/- infinity 
    exp = 31; // special infinity value 
    absx = 0; 
    } 
    else if (manbits) 
    { 
    // normal case, absx > 0 
    exp += 15; // bias the exponent 
    } 

    return (sign << 15) | ((unsigned)exp << 10) | (absx & ((1u<<10)-1)); 
} 

int main(void) 
{ 
    printf(" 0: 0x%04X\n", int2hfloat(0)); 
    printf("-1: 0x%04X\n", int2hfloat(-1)); 
    printf("+1: 0x%04X\n", int2hfloat(+1)); 
    printf("-2: 0x%04X\n", int2hfloat(-2)); 
    printf("+2: 0x%04X\n", int2hfloat(+2)); 
    printf("-3: 0x%04X\n", int2hfloat(-3)); 
    printf("+3: 0x%04X\n", int2hfloat(+3)); 
    printf("-2047: 0x%04X\n", int2hfloat(-2047)); 
    printf("+2047: 0x%04X\n", int2hfloat(+2047)); 
    printf("-2048: 0x%04X\n", int2hfloat(-2048)); 
    printf("+2048: 0x%04X\n", int2hfloat(+2048)); 
    printf("-2049: 0x%04X\n", int2hfloat(-2049)); // first inexact integer 
    printf("+2049: 0x%04X\n", int2hfloat(+2049)); 
    printf("-2050: 0x%04X\n", int2hfloat(-2050)); 
    printf("+2050: 0x%04X\n", int2hfloat(+2050)); 
    printf("-32752: 0x%04X\n", int2hfloat(-32752)); 
    printf("+32752: 0x%04X\n", int2hfloat(+32752)); 
    printf("-32768: 0x%04X\n", int2hfloat(-32768)); 
    printf("+32768: 0x%04X\n", int2hfloat(+32768)); 
    printf("-65504: 0x%04X\n", int2hfloat(-65504)); // legal maximum 
    printf("+65504: 0x%04X\n", int2hfloat(+65504)); 
    printf("-65505: 0x%04X\n", int2hfloat(-65505)); // infinity from here on 
    printf("+65505: 0x%04X\n", int2hfloat(+65505)); 
    printf("-65535: 0x%04X\n", int2hfloat(-65535)); 
    printf("+65535: 0x%04X\n", int2hfloat(+65535)); 
    return 0; 
} 

Output (ideone):

0: 0x0000 
-1: 0xBC00 
+1: 0x3C00 
-2: 0xC000 
+2: 0x4000 
-3: 0xC200 
+3: 0x4200 
-2047: 0xE7FF 
+2047: 0x67FF 
-2048: 0xE800 
+2048: 0x6800 
-2049: 0xE800 
+2049: 0x6800 
-2050: 0xE801 
+2050: 0x6801 
-32752: 0xF7FF 
+32752: 0x77FF 
-32768: 0xF800 
+32768: 0x7800 
-65504: 0xFBFF 
+65504: 0x7BFF 
-65505: 0xFC00 
+65505: 0x7C00 
-65535: 0xFC00 
+65535: 0x7C00 
+0

đơn giản: D ... – UmNyobe

+1

@UmNyobe đơn giản nếu bạn biết đủ toán học. –

+0

Cần lưu ý rằng mã này có hành vi làm tròn bất thường. Phần lớn, nó cắt ngắn chứ không phải là vòng đến gần nhất (mà là phổ biến hơn) nhưng là bất thường ở phía trên. Các đầu vào lớn hơn giá trị hữu hạn có thể biểu diễn tối đa được chuyển đổi thành vô cùng, thay vì bị cắt ngắn như các đầu vào khác, ngay cả khi chúng chỉ lớn hơn một chút so với giá trị cực đại. Ví dụ 0xfff (4095) được chuyển thành 0x6bff (4094), nhưng 0xfff0 (65520) hoặc 0xffe1 (65505) được chuyển thành 0x7c00 (vô cực) thay vì 0x7bff (65504). –

2

Tôi đã hỏi câu hỏi về cách chuyển đổi điểm nổi 32 bit thành điểm nổi 16 bit.

Float32 to Float16

Vì vậy, từ đó bạn có thể dễ dàng chuyển đổi các int đến một phao và sau đó sử dụng các câu hỏi ở trên để tạo ra một phao 16-bit. Tôi sẽ đề nghị này có lẽ dễ dàng hơn nhiều so với đi từ int trực tiếp đến 16-bit float. Hiệu quả bằng cách chuyển đổi sang phao 32 bit, bạn đã thực hiện hầu hết các công việc khó khăn và sau đó bạn chỉ cần thay đổi một vài bit xung quanh.

Chỉnh sửa: Nhìn vào câu trả lời tuyệt vời của Alexey Tôi nghĩ rằng rất có khả năng sử dụng phần cứng int để chuyển đổi float và sau đó bit chuyển nó xung quanh có khả năng là một chút công bằng nhanh hơn phương pháp của mình. Có thể có giá trị profiling cả hai phương pháp và so sánh chúng.

0

Sau @kbok bình luận câu hỏi tôi đã sử dụng phần đầu của this answer để có được nửa phao và sau đó để có được những mảng:

uint16_t position16float = float_to_half_branch(data); 
memcpy(&dataArray, &position16float, 2); 
Các vấn đề liên quan