今週、仕事場で協業している某社からリリースされてきたコードを見ていて。次のような雰囲気のコードを見つけました。実際に目にしたものからだいぶ書き換えてますが、こんな雰囲気ということで。
// double value = ...; UINT64* p = reinterpret_cast<UINT64*>(&value); do_something((*p >> 56) & 0xff) do_something((*p >> 48) & 0xff) do_something((*p >> 40) & 0xff) do_something((*p >> 32) & 0xff) do_something((*p >> 24) & 0xff) do_something((*p >> 16) & 0xff) do_something((*p >> 8) & 0xff) do_something( *p & 0xff)
double
型の値のビット表現を上位バイトから順に操作しようとしているわけです。なんとも危なっかしいことしてるなぁ、と。
ここでUINT64
型というのはいまの環境ではunsigned long long int
型の別名になっているんですが、設定によってはクラスとして定義される場合もあって、このような書き方はだいぶ危険に見えます(double
型もついても、IEEEの倍精度(64ビット)を採用している場合が多いようですが、そうでない場合もあるわけですから)。
これを見て、じゃぁ安全にdouble
型の値を意図した順序のバイト列に変換できるか考えてみたんですが、これが意外と難しい。
一番単純なのは、char
型(あるいはunsigned char
型)の配列にキャストすることですが…
unsigned char (&a)[sizeof(double)] = reinterpret_cast<unsigned char (&)[sizeof(double)]>(value);
…今度はエンディアンの影響を受けます。
次のようなコードを書けばエンディアンを識別できるので、ビッグエンディアンかリトルエンディアンかでキャストされた配列にアクセスする順序を変えればいいのですが、スマートでない。
bool isBigendian() { static const int n = 1; return reinterpret_cast<const char&>(n) == 0; }
ふだん浮動小数点数値を使わないので気がつきませんでしたが、有効桁数以外でも扱いに気をつかうことに気がついた一件でした。
おまけ。
任意のデータをビッグエンディアンでバイト列にするイテレータを書いてみた。以下コード。
class Iterator { public: enum Endian { big, little }; static Endian getEndian() { static const int n = 1; return (reinterpret_cast<const char&>(n) == 0) ? big : little; } template<typename T> explicit Iterator(const T& value) : value_(reinterpret_cast<const unsigned char*>(&value)), size_(sizeof(T)), pos_(0), endian_(getEndian()) { } bool hasNext() const { return pos_ < size_; } unsigned char next() { if(endian_ == big) { return value_[pos_++]; } else { return value_[size_ - ++pos_]; } } private: const unsigned char* value_; const int size_; int pos_; const Endian endian_; }; #include <iostream> #include <iomanip> int main(int, char* []) { std::cout << (Iterator::getEndian() == Iterator::big ? "BIG" : "LITTLE") << std::endl; std::cout << std::hex; double r = 0.12345678; for(Iterator i(r); i.hasNext(); ) { std::cout << std::setw(2) << static_cast<unsigned int>(i.next()) << " "; } std::cout << std::endl; int n = 0x12345678; for(Iterator i(n); i.hasNext(); ) { std::cout << std::setw(2) << static_cast<unsigned int>(i.next()) << " "; } std::cout << std::endl; return 0; }
実行結果。
LITTLE 3f bf 9a dd 10 91 c8 95 12 34 56 78
BIG 3f bf 9a dd 10 91 c8 95 12 34 56 78