2

この単純なコードが私の問題です。

拡張asm(gcc); Intel構文(-masm = intel); プラットフォーム-x86

何をすべきか:長さが1で、符号(+-)がxと同じフロートを返します。

    float signf(float x)
    {
      float r = 1;
      asm volatile (
            "and %1,0x80000000;"
            "or %0,%1;"
            :"=r"(r):"r"(x));
      return r;
    }

公正なサイコロの目で選ばれた任意の乱数でそれを呼び出すと、次のようになります。

    signf of -1352353.3253: -5.60519e-045
4

5 に答える 5

5

インライン asm の実際の問題は、r出力のみとして宣言することです。そのため、コンパイラは初期化を最適化します。"+r"代わりに制約を使用する必要が"=r"あり、機能するはずです。

より最適化されたバージョンは次のようになります。

float signf(float x)
{
    float r;
    __asm__  __volatile__ (
            "and %0, 0x80000000;"
            "or %0, 0x3f800000;"
            :"=r"(r):"0"(x));
    return r;
}

この関数には float->int->float 変換 (メモリ経由) が含まれるため、パフォーマンスに影響する可能性があることに注意してください。

上記のコードの C バージョンは次のとおりです。

float signf(float x)
{
    union { float f; int i; } tmp, res;
    tmp.f = x;
    res.f = 1;
    res.i |= tmp.i & 0x80000000;
    return res.f;
}

これにより、同じコードが生成されます(gcc 4.4.5を使用)。

単純な C アプローチreturn x < 0 ? -1 : 1;は、変換やメモリ アクセス (オペランドのロードを除く) なしで完全な FPU コードを生成するため、パフォーマンスが向上する可能性があります。またfcmov、分岐を避けるために利用可能な場合に使用します。ベンチマークが必要です。

于 2013-01-11T16:54:18.447 に答える
4

C++11にはこれに2つのC++関数があります。

bool std::signbit (x);

http://en.cppreference.com/w/cpp/numeric/math/signbit

また、

float f = std::copysign (1.0f, x);

http://en.cppreference.com/w/cpp/numeric/math/copysign

于 2013-01-11T15:32:32.743 に答える
1

これはうまくいくようです (AT&T 構文):

float signf(float x)
{
  float r = 1;
  asm ("andl $0x80000000, %1\n"
       "\torl %1, %0\n"
       :"+r"(r):"r"(x));
  return r;
}

TBH、copysignf()他の人が提案したように使用します。あなたがやろうとしていることは、このasm()ステートメントを実行できる IA-32 プラットフォームと C++ コンパイラにのみ関連付けられているため、移植できません。

編集1

ところで、次のバージョンは同じように機能し (上記のステートメントとほとんど同じ命令を生成しますasm())、移植性のないものや型のエイリアシングの問題はありません (他の人が提案したunionベースまたはreinterpret_cast<>ベースのバージョンとは異なります)。

float signf3(float x)
{
  unsigned u;
  std::memcpy(&u, &x, sizeof (u)) ;

  float r = 1.f;
  unsigned uone;
  std::memcpy(&uone, &r, sizeof (uone));

  uone |= u & 0x80000000;

  std::memcpy(&r, &uone, sizeof (r));
  return r;
}
于 2013-01-11T19:29:54.017 に答える
0

この質問は C++ とタグ付けされているため、コンパイラを最適化できる C++ の提案を 2 つ提供します。

  • return x < 0.0f ? -1.0f : 1.0f;
  • return x / std::abs(x); // I believe self-division shouldn't cause 'almost 1.0' numbers to be genereated
于 2013-01-11T15:27:08.713 に答える
0

これには asm を使用する必要はありません。以下は、あなたがしようとしたことを実行します (-0.0f の正しい結果であっても)。

float signf(float x) {
    bool sign=(0!=(*(reinterpret_cast<uint32_t *>(&x)) & 0x80000000));
    return sign? -1.0f : 1.0f;
}
于 2013-01-11T16:41:44.073 に答える