36

4つの複合バイトから32ビットのfloatを構築しようとしています。次の方法よりも優れた(またはよりポータブルな)方法はありますか?

#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;
}
4

6 に答える 6

44

memcpy結果)を使用できます

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 binary32またはを使用しているという保証はないため、これはコードよりも移植性が高くありませんsizeof(float) == 4

(注*:@ Jamesで説明されているように、標準(C++§[class.union]/ 1)では、ユニオンメンバーにアクセスすることは技術的に許可されていませんu.f

于 2010-10-21T20:19:38.267 に答える
19

次の関数は、ネットワークバイトオーダーでバッファとの間で単精度浮動小数点値を表すバイトをパック/アンパックします。unpackメソッドは、個々のバイトを適切な量だけビットシフトしてからOR演算することにより、個々のバイトから32ビット値を明示的に構築するため、エンディアンを考慮する必要があるのはpackメソッドのみです。これらの関数は、floatを32ビットで格納するC /C++実装にのみ有効です。これは、 IEEE754-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;
}
于 2010-10-21T20:41:28.777 に答える
16

あなたが使用することができます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このソリューションのは厳密なエイリアシングルールに違反しません)。

それでもfloat幅が4バイトであることに依存し、実装の浮動小数点形式で有効な浮動小数点数である4バイトに依存しますが、これらの仮定を行うか、変換を行うために特別な処理コードを作成する必要があります。

于 2010-10-21T20:27:40.650 に答える
4

さまざまなプラットフォームで次のものを使用できるため、このポータブルを実行する方法はありません。

  • 異なるバイト順序(ビッグエンディアンとリトルエンディアン)
  • 浮動小数点値のさまざまな表現(例については、 http://en.wikipedia.org/wiki/IEEE_754-1985を参照してください)
  • 浮動小数点値のさまざまなサイズ

また、これらの4バイトはどこから取得するのでしょうか。

別のシステムから取得し、両方のシステムがまったく同じ方法を使用して浮動小数点値をメモリに格納することを保証できる場合は、ユニオントリックを使用できます。それ以外の場合、コードは移植性がないことがほぼ保証されます。

于 2010-10-21T20:34:43.800 に答える
3

これを行うためのポータブルな方法が必要な場合は、システムのエンディアンを検出するためのコードを少し作成する必要があります。

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;
}
于 2010-10-21T20:34:33.383 に答える
2

私は通常これをCで使用します-いいえmemcpyまたはunion必須です。C++のエイリアシングルールに違反する可能性があります。わかりません。

float bytesToFloat(uint8_t *bytes, bool big_endian) {
    float f;
    uint8_t *f_ptr = (uint8_t *) &f;
    if (big_endian) {
        f_ptr[3] = bytes[0];
        f_ptr[2] = bytes[1];
        f_ptr[1] = bytes[2];
        f_ptr[0] = bytes[3];
    } else {
        f_ptr[3] = bytes[3];
        f_ptr[2] = bytes[2];
        f_ptr[1] = bytes[1];
        f_ptr[0] = bytes[0];
    }
    return f;
}

フロートとして再解釈する必要のあるバイトの配列全体がある場合、必要に応じて、配列内の4バイトの連続するシーケンスごとに次のプロシージャを呼び出して、バイト順序を切り替えることができます(たとえば、リトルエンディアンのマシンですが、バイトはビッグエンディアンの順序です)。uint8_t *次に、配列ポインタをにキャストし、float *floatの配列としてメモリにアクセスできます。

void switchEndianness(uint8_t *bytes) {
    uint8_t b0 = bytes[0];
    uint8_t b1 = bytes[1];
    uint8_t b2 = bytes[2];
    uint8_t b3 = bytes[3];
    bytes[0] = b3;
    bytes[1] = b2;
    bytes[2] = b1;
    bytes[3] = b0;
}
于 2020-07-04T01:42:01.580 に答える