2

这段简单的代码是我的问题:

扩展汇编(gcc);英特尔语法(-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

内联汇编的实际问题是您r仅声明为输出,因此编译器将优化初始化。您应该使用"+r"约束而不是"=r"它应该可以工作。

更好的优化版本可能如下所示:

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

请注意,此函数涉及浮点->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 中有两个 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++ 建议,您可以让您的编译器进行优化:

  • 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 回答