6

次の Python コードを C++ に変換しようとしています。

import struct
import binascii


inputstring = ("0000003F" "0000803F" "AD10753F" "00000080")
num_vals = 4

for i in range(num_vals):
    rawhex = inputstring[i*8:(i*8)+8]

    # <f for little endian float
    val = struct.unpack("<f", binascii.unhexlify(rawhex))[0]
    print val

    # Output:
    # 0.5
    # 1.0
    # 0.957285702229
    # -0.0

そのため、16 進数でエンコードされた 32 ビットの文字列を読み取り、unhexlifyメソッドを使用してそれをバイト配列に変換し、リトルエンディアンの float 値として解釈します。

以下はほとんど機能しますが、コードはちょっとお粗末です (そして、最後の00000080構文は正しく解析されません)。

#include <sstream>
#include <iostream>


int main()
{
    // The hex-encoded string, and number of values are loaded from a file.
    // The num_vals might be wrong, so some basic error checking is needed.
    std::string inputstring = "0000003F" "0000803F" "AD10753F" "00000080";
    int num_vals = 4;


    std::istringstream ss(inputstring);

    for(unsigned int i = 0; i < num_vals; ++i)
    {
        char rawhex[8];

// The ifdef is wrong. It is not the way to detect endianness (it's
// always defined)
#ifdef BIG_ENDIAN
        rawhex[6] = ss.get();
        rawhex[7] = ss.get();

        rawhex[4] = ss.get();
        rawhex[5] = ss.get();

        rawhex[2] = ss.get();
        rawhex[3] = ss.get();

        rawhex[0] = ss.get();
        rawhex[1] = ss.get();
#else
        rawhex[0] = ss.get();
        rawhex[1] = ss.get();

        rawhex[2] = ss.get();
        rawhex[3] = ss.get();

        rawhex[4] = ss.get();
        rawhex[5] = ss.get();

        rawhex[6] = ss.get();
        rawhex[7] = ss.get();
#endif

        if(ss.good())
        {
            std::stringstream convert;
            convert << std::hex << rawhex;
            int32_t val;
            convert >> val;

            std::cerr << (*(float*)(&val)) << "\n";
        }
        else
        {
            std::ostringstream os;
            os << "Not enough values in LUT data. Found " << i;
            os << ". Expected " << num_vals;
            std::cerr << os.str() << std::endl;
            throw std::exception();
        }
    }
}

(OS X 10.7/gcc-4.2.1 でコンパイル、単純なg++ blah.cpp.

特に、この投稿で説明さBIG_ENDIANれているように、これを行うためのより良い方法があると確信しているため、マクロのものを取り除きたいと思います。

その他のランダムな詳細はほとんどありません - Boost を使用できません (プロジェクトの依存関係が大きすぎます)。文字列には通常、1536 (8 3 *3) から 98304 個の float 値 (32 3 *3) が含まれ、多くても 786432 (64 3 *3)です。

(edit2: 別の値を追加00000080== -0.0)

4

3 に答える 3

1

#ifdef BIG_ENDIAN以下は、ブロックを削除するように変更された更新済みコードです。これは、ホストのバイト順序に依存しない読み取り技術を使用します。これは、16 進バイト (ソース文字列ではリトル エンディアン) を、iostream std::hex 演算子と互換性のあるビッグ エンディアン文字列形式に読み取ることによって行われます。この形式になったら、ホストのバイト順は問題になりません。

さらに、場合によっては末尾のガベージなしrawhexで挿入するためにゼロで終了する必要があるというバグを修正します。convert

テストするビッグ エンディアン システムがないので、プラットフォームで確認してください。これは、Cygwin でコンパイルおよびテストされています。

#include <sstream>
#include <iostream>

int main()
{
    // The hex-encoded string, and number of values are loaded from a file.
    // The num_vals might be wrong, so some basic error checking is needed.
    std::string inputstring = "0000003F0000803FAD10753F00000080";
    int num_vals = 4;
    std::istringstream ss(inputstring);
    size_t const k_DataSize = sizeof(float);
    size_t const k_HexOctetLen = 2;

    for (uint32_t i = 0; i < num_vals; ++i)
    {
        char rawhex[k_DataSize * k_HexOctetLen + 1];

        // read little endian string into memory array
        for (uint32_t j=k_DataSize; (j > 0) && ss.good(); --j)
        {
            ss.read(rawhex + ((j-1) * k_HexOctetLen), k_HexOctetLen);
        }

        // terminate the string (needed for safe conversion)
        rawhex[k_DataSize * k_HexOctetLen] = 0;

        if (ss.good())
        {
            std::stringstream convert;
            convert << std::hex << rawhex;
            uint32_t val;
            convert >> val;

            std::cerr << (*(float*)(&val)) << "\n";
        }
        else
        {
            std::ostringstream os;
            os << "Not enough values in LUT data. Found " << i;
            os << ". Expected " << num_vals;
            std::cerr << os.str() << std::endl;
            throw std::exception();
        }
    }
}
于 2012-04-07T15:08:24.447 に答える
1

istringstringビジネス全体がやり過ぎだと思います。これを一度に 1 桁ずつ解析する方がはるかに簡単です。

まず、16 進数を整数に変換する関数を作成します。

signed char htod(char c)
{
  c = tolower(c);
  if(isdigit(c))
    return c - '0';

  if(c >= 'a' && c <= 'f')
    return c - 'a' + 10;

  return -1;
}

次に、文字列を整数に変換するだけです。以下のコードはエラーをチェックせず、ビッグ エンディアンを想定していますが、詳細を入力できるはずです。

unsigned long t = 0;
for(int i = 0; i < s.length(); ++i)
  t |= (t << 4) & htod(s[i]);

次に、あなたのフロートは

float f = * (float *) &t;
于 2012-04-07T14:42:37.287 に答える
-1

最終的な結果は、OpenColorIO/src/core/FileFormatIridasLook.cpp です。

(署名されていない修正を含むAmardeepの回答uint32_tも機能する可能性があります)

    // convert hex ascii to int
    // return true on success, false on failure
    bool hexasciitoint(char& ival, char character)
    {
        if(character>=48 && character<=57) // [0-9]
        {
            ival = static_cast<char>(character-48);
            return true;
        }
        else if(character>=65 && character<=70) // [A-F]
        {
            ival = static_cast<char>(10+character-65);
            return true;
        }
        else if(character>=97 && character<=102) // [a-f]
        {
            ival = static_cast<char>(10+character-97);
            return true;
        }

        ival = 0;
        return false;
    }

    // convert array of 8 hex ascii to f32
    // The input hexascii is required to be a little-endian representation
    // as used in the iridas file format
    // "AD10753F" -> 0.9572857022285461f on ALL architectures

    bool hexasciitofloat(float& fval, const char * ascii)
    {
        // Convert all ASCII numbers to their numerical representations
        char asciinums[8];
        for(unsigned int i=0; i<8; ++i)
        {
            if(!hexasciitoint(asciinums[i], ascii[i]))
            {
                return false;
            }
        }

        unsigned char * fvalbytes = reinterpret_cast<unsigned char *>(&fval);

#if OCIO_LITTLE_ENDIAN
        // Since incoming values are little endian, and we're on little endian
        // preserve the byte order
        fvalbytes[0] = (unsigned char) (asciinums[1] | (asciinums[0] << 4));
        fvalbytes[1] = (unsigned char) (asciinums[3] | (asciinums[2] << 4));
        fvalbytes[2] = (unsigned char) (asciinums[5] | (asciinums[4] << 4));
        fvalbytes[3] = (unsigned char) (asciinums[7] | (asciinums[6] << 4));
#else
        // Since incoming values are little endian, and we're on big endian
        // flip the byte order
        fvalbytes[3] = (unsigned char) (asciinums[1] | (asciinums[0] << 4));
        fvalbytes[2] = (unsigned char) (asciinums[3] | (asciinums[2] << 4));
        fvalbytes[1] = (unsigned char) (asciinums[5] | (asciinums[4] << 4));
        fvalbytes[0] = (unsigned char) (asciinums[7] | (asciinums[6] << 4));
#endif
        return true;
    }
于 2012-04-20T03:56:31.087 に答える