4つの複合バイトから32ビットの浮動小数点を構築しようとしています。これを行うには、次の方法よりも優れた(または移植性の高い)方法がありますか?
#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;
}
memcpy
( Result )を使用できます
_float f;
uchar b[] = {b3, b2, b1, b0};
memcpy(&f, &b, sizeof(f));
return f;
_
またはユニオン*( 結果 )
_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;
_
ただし、プラットフォームがリトルエンディアンである、またはfloat
がIEEEバイナリ32またはsizeof(float) == 4
を使用しているという保証がないため、これはコードよりも移植性が高くありません。
(注*: @ James で説明されているように、標準(C++§[class.union]/1)では、ユニオンメンバー_u.f
_にアクセスすることは技術的に許可されていません。)
以下の関数は、ネットワークバイトオーダーでバッファとの間の単精度浮動小数点値を表すバイトをパック/アンパックします。アンパックメソッドは、適切な量をビットシフトし、それらを一緒にOR結合することによって、個々のバイトから32ビット値を明示的に構築するため、パックメソッドのみがエンディアンを考慮する必要があります。これらの関数は、32ビットで浮動小数点数を格納するC/C++実装でのみ有効です。これは IEEE 754-1985 浮動小数点の実装に当てはまります。
// 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;
}
_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;
}
_
これにより、言語で技術的に許可されていないユニオンハックが回避されます。また、よく使用されるreinterpret_cast<float*>(byte_array)
も回避されます。これは、厳密なエイリアス規則に違反します(オブジェクトをchar
の配列として再解釈することが許可されているため、このソリューションの_reinterpret_cast
_ sは厳密なエイリアシング規則に違反していない)。
それでも、float
は幅が4バイトであり、実装の浮動小数点形式で有効な浮動小数点数である4バイトに依存していますが、これらの仮定を行うか、特別な処理コードを記述する必要があります変換を行います。
さまざまなプラットフォームで使用できるため、これをポータブルにする方法はありません。
また、これらの4バイトはどこから取得するのでしょうか。
別のシステムから取得したと想定し、両方のシステムがまったく同じメソッドを使用してメモリに浮動小数点値を格納することを保証できる場合は、ユニオントリックを使用できます。それ以外の場合、コードは移植不可能であることがほぼ保証されています。
これを行うポータブルな方法が必要な場合は、システムのエンディアンを検出するためのコードを少し記述する必要があります。
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;
}