2010-10-21 32 views
18

Tôi đang cố gắng tạo ra một phao 32 bit trong số 4 byte tổng hợp của nó. Có cách nào tốt hơn (hoặc di động hơn) để làm điều này hơn với phương pháp sau đây?Xây dựng một phao 32 bit ra khỏi 4 byte tổng hợp [C++]

#include <iostream> 

typedef unsigned char uchar; 

float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    float output; 

    *((uchar*)(&output) + 3) = b0; 
    *((uchar*)(&output) + 2) = b1; 
    *((uchar*)(&output) + 1) = b2; 
    *((uchar*)(&output) + 0) = b3; 

    return output; 
} 

int main() 
{ 
    std::cout << bytesToFloat(0x3e, 0xaa, 0xaa, 0xab) << std::endl; // 1.0/3.0 
    std::cout << bytesToFloat(0x7f, 0x7f, 0xff, 0xff) << std::endl; // 3.4028234 × 10^38 (max single precision) 

    return 0; 
} 
+3

Xem xét đây là câu hỏi đầu tiên của tôi về Stack Overflow, tôi vui mừng trước các phản hồi khác nhau. Tôi cảm ơn tất cả mọi người cho đầu vào của họ. – Madgeek

Trả lời

26

Bạn có thể sử dụng một memcpy (Result)

float f; 
uchar b[] = {b3, b2, b1, b0}; 
memcpy(&f, &b, sizeof(f)); 
return f; 

hoặc một liên minh * (Result)

union { 
    float f; 
    uchar b[4]; 
} u; 
u.b[3] = b0; 
u.b[2] = b1; 
u.b[1] = b2; 
u.b[0] = b3; 
return u.f; 

Nhưng điều này là không cầm tay hơn mã của bạn, vì không có đảm bảo rằng nền tảng là nhỏ gọn hoặc float đang sử dụng IEEE binary32 hoặc thậm chí sizeof(float) == 4.

(Lưu ý *: Theo giải thích của @James, nó được về mặt kỹ thuật không được phép trong tiêu chuẩn (C++ § [class.union]/1) để truy cập các thành viên đoàn u.f.)

+3

Để giải quyết vấn đề 'sizeof (float)' bạn có thể khai báo thành viên 'b' là' uchar b [sizeof (float)]; '. –

+0

@Matteo: Đúng, nhưng sau đó đầu vào cũng cần được sửa đổi. – kennytm

12

Bạn có thể sử dụng std::copy:

float bytesToFloat(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    uchar byte_array[] = { b3, b2, b1, b0 }; 
    float result; 
    std::copy(reinterpret_cast<const char*>(&byte_array[0]), 
       reinterpret_cast<const char*>(&byte_array[4]), 
       reinterpret_cast<char*>(&result)); 
    return result; 
} 

Điều này tránh sự tấn công của công đoàn, vốn không được ngôn ngữ cho phép về mặt kỹ thuật. Nó cũng tránh được việc sử dụng phổ biến reinterpret_cast<float*>(byte_array), vi phạm quy tắc bí danh nghiêm ngặt (được phép diễn giải lại bất kỳ đối tượng nào dưới dạng mảng char, do đó, reinterpret_cast s trong giải pháp này không vi phạm quy tắc bí danh nghiêm ngặt).

Nó vẫn dựa trên float là bốn byte chiều rộng và dựa vào bốn byte của bạn là một số dấu phẩy động hợp lệ trong định dạng điểm nổi của triển khai của bạn, nhưng bạn phải đưa ra các giả định đó hoặc bạn phải viết mã xử lý đặc biệt thực hiện chuyển đổi.

+0

Điều này vẫn không phải là di động, phải không? Tôi chỉ tò mò ... – JoshD

+0

@JoshD: Không; nó vẫn dựa vào 'sizeof (float) == 4' và không tính đến endianness. Nó chỉ tránh 'reinterpret_cast (some_uchar_array)' và hack công đoàn. –

+0

Tôi khá chắc chắn rằng 'reinterpret_cast (byte_array)' phải được cho phép nếu byte_array (1) được căn chỉnh đúng và (2) thực sự chứa một phao. Tôi nghĩ như vậy bởi vì nếu không nó sẽ không thể 'memcpy' một' float' sang một 'float' khác (kể từ' memcpy' ghi vào một mảng byte), và một 'float' là kiểu POD nguyên mẫu. – MSalters

3

Nếu bạn muốn một cách di động để thực hiện việc này, bạn sẽ phải viết một chút mã để phát hiện endianess của hệ thống.

float bytesToFloatA(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    float output; 

    *((uchar*)(&output) + 3) = b0; 
    *((uchar*)(&output) + 2) = b1; 
    *((uchar*)(&output) + 1) = b2; 
    *((uchar*)(&output) + 0) = b3; 

    return output; 
} 


float bytesToFloatB(uchar b0, uchar b1, uchar b2, uchar b3) 
{ 
    float output; 

    *((uchar*)(&output) + 3) = b3; 
    *((uchar*)(&output) + 2) = b2; 
    *((uchar*)(&output) + 1) = b1; 
    *((uchar*)(&output) + 0) = b0; 

    return output; 
} 

float (*correctFunction)(uchar b0, uchar b1, uchar b2, uchar b3) = bytesToFloatA; 

if ((*correctFunction)(0x3e, 0xaa, 0xaa, 0xab) != 1.f/3.f) // horrifying, I know 
{ 
    correctFunction = bytesToFloatB; 
} 
+1

Điều đó sẽ không bằng nhau ở bất kỳ người dùng nào bởi vì '1./3.' là một' double', không phải là 'float'. Bạn nên sử dụng một cái gì đó như '1.0f/3'. – kennytm

4

Không có cách nào để làm kế nhỏ gọn, kể từ khi nền tảng khác nhau có thể sử dụng:

  • khác nhau byte đặt hàng (về cuối lớn so little endian)
  • cơ quan đại diện khác nhau cho các giá trị dấu chấm động (xem http://en.wikipedia.org/wiki/IEEE_754-1985 ví dụ)
  • kích thước khác nhau cho các giá trị điểm động

Tôi cũng tự hỏi bạn nhận được 4 byte từ đâu?

Nếu tôi giả định rằng bạn lấy chúng từ hệ thống khác và bạn có thể đảm bảo rằng cả hai hệ thống đều sử dụng chính xác cùng một phương pháp để lưu trữ các giá trị dấu phẩy động trong bộ nhớ, bạn có thể sử dụng lừa công đoàn. Nếu không, mã của bạn gần như được bảo đảm là không di động.

13

Các chức năng sau đóng gói/giải nén các byte đại diện cho một giá trị điểm nổi chính xác đơn đến/từ bộ đệm theo thứ tự byte mạng. Chỉ có phương thức gói cần tính đến endianness vì phương thức unpack giải quyết một cách rõ ràng giá trị 32 bit từ các byte riêng lẻ bằng cách bit chuyển chúng thành số tiền thích hợp và sau đó OR chúng lại với nhau. Các chức năng này chỉ hợp lệ cho việc triển khai C/C++ lưu trữ một float trong 32 bit.Điều này đúng cho IEEE 754-1985 triển khai điểm nổi.

// unpack method for retrieving data in network byte, 
// big endian, order (MSB first) 
// increments index i by the number of bytes unpacked 
// usage: 
// int i = 0; 
// float x = unpackFloat(&buffer[i], &i); 
// float y = unpackFloat(&buffer[i], &i); 
// float z = unpackFloat(&buffer[i], &i); 
float unpackFloat(const void *buf, int *i) { 
    const unsigned char *b = (const unsigned char *)buf; 
    uint32_t temp = 0; 
    *i += 4; 
    temp = ((b[0] << 24) | 
      (b[1] << 16) | 
      (b[2] << 8) | 
      b[3]); 
    return *((float *) temp); 
} 

// pack method for storing data in network, 
// big endian, byte order (MSB first) 
// returns number of bytes packed 
// usage: 
// float x, y, z; 
// int i = 0; 
// i += packFloat(&buffer[i], x); 
// i += packFloat(&buffer[i], y); 
// i += packFloat(&buffer[i], z); 
int packFloat(void *buf, float x) { 
    unsigned char *b = (unsigned char *)buf; 
    unsigned char *p = (unsigned char *) &x; 
#if defined (_M_IX86) || (defined (CPU_FAMILY) && (CPU_FAMILY == I80X86)) 
    b[0] = p[3]; 
    b[1] = p[2]; 
    b[2] = p[1]; 
    b[3] = p[0]; 
#else 
    b[0] = p[0]; 
    b[1] = p[1]; 
    b[2] = p[2]; 
    b[3] = p[3]; 
#endif 
    return 4; 
} 
+0

Tôi nghĩ rằng có một sai lầm trong dòng mã: return * ((float *) temp); Phải là: return * ((float *) &temp); –

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