web-dev-qa-db-ja.com

フロートを4uint8_tに変換します

CANプロトコルを介して送信する必要があるfloat変数があります。そのためには、この32ビットのフロートを4 uint8_t変数

どうしたらいいのか全くわからない。私は最初にfloatをintに変換することを考えていましたが、インターネットで見つけたいくつかの答えは、castまたはunionが機能していないようです。

これが私がやろうとしていることの簡単な例です:

float f;
uint8_t ut1,ut2,ut3,ut4;

//8 first bits of f into ut1
//8 second bits of f in ut2
...

// Then I can send the uint8_t through CAN
...
12
Evans Belloeil

通常、これを行うには、floatをuint8_tの配列にキャストします。

Cでは、次のように実行できます。

uint8_t *array;
array = (unit8_t*)(&f);

c ++では、reinterpret_castを使用します

uint8_t *array;
array = reinterpret_cast<uint8_t*>(&f);

次に、array [0]、...、array [3]がバイトになります。

7
dohashi

まず、標準ではfloatに特定のサイズ制限が課されていないことに注意してください。 floatが、考えられるアーキテクチャでは4バイトに収まらない可能性があります(私は何も知りませんが)。何かを試みる前に、少なくとも(static_)それが適合することを表明する必要があります。

次に、最も簡単な方法は、CHAR_BIT8であることを表明し、unsigned char*への正当なエイリアシングをreinterpret_castで使用することだと思います。

static_assert(sizeof(float) == 4);
float f = 0; // whatever value
unsigned char* float_as_char = reinterpret_cast<unsigned char*>(&f);

ただし、これはエンディアンの問題を完全に無視するため、実際に必要なのは、バイトのコピーを作成して修正できるようにすることです。

static_assert(sizeof(float) == 4);
float f = 0; // whatever value
uint8_t bytes[4];
std::memcpy(bytes, &f);
// Fix up the order of the bytes in "bytes" now.
2
Mark B

これは、1つの配列ではなく整数部分に個別の名前を付けるユニオンアプローチです。

union {
    float f;
    struct {
        uint8_t ut1, ut2, ut3, ut4;
    } bytes;
} value;
value.f = 1.f;
uint8_t first = value.bytes.ut1;

私は当初、このunionの使用が標準に従って厳密に合法ではないことを懸念していました: C++未定義の振る舞い しかし、rashmatashの答えに対するコメントでのComicSansMSの議論は説得力があります。

1
eerorika

あなたはこの違法な操作をすることができます:

float f = someFloatValue;
uint8_t* i = reinterpret_cast<uint8_t*>(&f);

これはほとんどの場合機能しますが、c ++標準ではサポートされておらず、コンパイラーは未定義の動作でコードを生成する可能性があります。

別の解決策は、共用体を使用することです。

union{
    float f;
    uint8_t i[4];
}
f = someFloatValue;
// now i's contain the bit pattern of f

すべてのコンパイラが一貫した結果をもたらすかどうかは不明ですが、最初のアプローチよりも安全なようです。

fの値を32ビット整数にパックすることもできます。ただし、これにより精度が少し低下する可能性がありますが、fをどれだけ正確に保持するかによっては、最善の解決策になります。

1
rashmatash