浮動小数点数をリトル エンディアンからビッグ エンディアンに変換したいのですが、できません。int 数値のエンディアンを正常に変換しましたが、誰かが float 数値を手伝ってくれませんか
2 に答える
#include <cstring> // for std::memcpy
#include <algorithm> // for std::reverse
#include <iterator> // For C++11 std::begin() and std::end()
// converting from float to bytes for writing out
float f = 10.0;
char c[sizeof f];
std::memcpy(c,&f,sizeof f);
std::reverse(std::begin(c),std::end(c)); // begin() and end() are C++11. For C++98 say std::reverse(c,c + sizeof f);
// ... write c to network, file, whatever ...
他の方向に行く:
char c[] = { 41, 36, 42, 59 };
static_assert(sizeof(float) == sizeof c,"");
std::reverse(std::begin(c),std::end(c));
float f;
std::memcpy(&f,c,sizeof f);
浮動小数点値の表現は実装定義であるため、この結果の値は実装によって異なる場合があります。つまり、スワップされた 10.0 バイトは 1.15705e-041 などになるか、有効な浮動小数点数ではない可能性があります。
ただし、IEEE 754 を使用する実装 (ほとんどの場合std::numeric_limits<float>.is_iec559
、true かどうかを確認できます) では、同じ結果が得られるはずです。(std::numeric_limits
から#include <limits>
です。)
上記のコードは、float をバイトに変換し、バイトを変更してから、それらのバイトを float に変換します。float として読み取りたいバイト値がある場合は、char 配列の値をバイトに設定し、memcpy()
上記のように ( の後の行でstd::reverse()
) を使用して、それらのバイトを に入れることができますf
。
多くの場合、この種のことには reinterpret_cast を使用することをお勧めしますが、キャストを避けるのは良いことだと思います。多くの場合、人々はそれらを誤って使用し、気付かないうちに未定義の動作を取得します。この場合、reinterpret_cast は合法的に使用できますが、それでも避けたほうがよいと思います。
4行を1行に減らしますが...
std::reverse(reinterpret_cast<char*>(&f),reinterpret_cast<char*>(&f) + sizeof f);
reinterpret_cast を使用してはいけない理由の例を次に示します。以下はおそらく機能しますが、未定義の動作になる可能性があります。それが機能するので、おそらくあなたが何か間違ったことをしたことに気付かないでしょう。これは可能な限り最も望ましくない結果の 1 つです。
char c[] = { 41, 36, 42, 59 };
static_assert(sizeof(float) == sizeof c,"");
float f = *reinterpret_cast<float*>(&c[0]);
そのようなことを行う正しい方法は、ユニオンを使用することです。
union float_int {
float m_float;
int32_t m_int;
};
そうすれば、浮動小数点数を整数に変換できます。整数のエンディアンを変換する方法は既にわかっているので、問題ありません。
ダブルの場合は次のようになります。
union double_int {
double m_float;
int64_t m_int;
};
int32_t と int64_t は通常 stdint.h で利用でき、boost はそのようなものを提供し、Qt には独自の定義セットがあります。整数のサイズが浮動小数点のサイズと正確に等しいことを確認してください。一部のシステムでは、long double も定義されています。
union double_int {
long double m_float;
int128_t m_int;
};
int128_t が機能しない場合は、構造体を次のように使用できます。
union long_double_int {
long double m_float;
struct {
int32_t m_int_low;
int32_t m_int_hi;
};
};
どのような場合でも、int を使用する代わりにバイトを使用できると思われる可能性があります。
union float_int {
float m_float;
unsigned char m_bytes[4];
};
そして、そのような変換を行うときに使用される通常のシフトをすべて必要としないことに気付くのは、次のように宣言することもできるからです。
union char_int {
int m_int;
unsigned char m_bytes[4];
};
これで、コードは非常にシンプルに見えます:
float_int fi;
char_int ci;
fi.m_float = my_float;
ci.m_bytes[0] = fi.m_bytes[3];
ci.m_bytes[1] = fi.m_bytes[2];
ci.m_bytes[2] = fi.m_bytes[1];
ci.m_bytes[3] = fi.m_bytes[0];
// now ci.m_int is the float in the other endian
fwrite(&ci, 1, 4, f);
[...snip...]
fread(&ci, 1, 4, f);
// here ci.m_int is the float in the other endian, so restore:
fi.m_bytes[0] = ci.m_bytes[3];
fi.m_bytes[1] = ci.m_bytes[2];
fi.m_bytes[2] = ci.m_bytes[1];
fi.m_bytes[3] = ci.m_bytes[0];
my_float = fi.m_float;
// now my_float was restored from the file
この例では明らかにエンディアンが入れ替わっています。また、プログラムを LITTLE_ENDIAN と BIG_ENDIAN の両方のコンピューターでコンパイルする場合、実際にそのようなスワップを行う必要があるかどうかを知る必要もあります (BYTE_ENDIAN を確認してください)。