Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/136.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 内联装配;浮点数位运算;什么';这里出问题了?_C++_Gcc_Assembly_Inline Assembly - Fatal编程技术网

C++ 内联装配;浮点数位运算;什么';这里出问题了?

C++ 内联装配;浮点数位运算;什么';这里出问题了?,c++,gcc,assembly,inline-assembly,C++,Gcc,Assembly,Inline Assembly,这段简单的代码是我的问题: 扩展asm(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)); retur

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

扩展asm(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

这个问题被标记为C++,所以我将提供两个C++建议,让编译器优化:

  • 返回x<0.0f-1.0f:1.0f
  • 返回x/std::abs(x);//我相信自除法不应该导致生成“几乎1.0”的数字

  • 在C++ 11中有两个C++函数:

    bool std::signbit (x);
    

    或者


    您不需要为此使用asm。下面执行您尝试执行的操作(即使是-0.0f的正确结果)

    float signf(float x){
    布尔符号=(0!=(*(重新解释投影(&x))&0x8000000));
    返回标志?-1.0f:1.0f;
    }
    
    内联asm的实际问题是您将
    r
    声明为仅输出,因此编译器将优化初始化。您应该使用
    “+r”
    约束,而不是
    “=r”
    ,它应该可以工作

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

    float signf(float x)
    {
        float r;
        __asm__  __volatile__ (
                "and %0, 0x80000000;"
                "or %0, 0x3f800000;"
                :"=r"(r):"0"(x));
        return r;
    }
    
    请注意,此函数涉及浮点->整数->浮点转换(通过内存),这可能会影响性能

    上述代码的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;
    }
    
    这会为我生成相同的代码(使用GCC4.4.5)

    简单的C方法
    返回x<0-1 : 1;
    生成完整的FPU代码,无需转换或内存访问(加载操作数除外),因此性能可能会更好。如果可以避免分支,它还使用
    fcmov
    。需要一些基准测试。

    这似乎很有效(AT&T语法):

    TBH,我会使用别人建议的
    copysignf()
    。你想做的是不可移植的,因为它只与IA32平台和C++编译器绑定,可以做这个代码> ASME()/<代码>语句。 编辑1

    顺便说一句,以下版本的工作原理相同(并且生成的指令与上面的
    asm()
    语句几乎相同),并且没有不可移植的内容和类型别名问题(与其他人建议的基于
    union
    reinterpret\u cast
    的版本不同)


    如果您查看所有涉及的数字(包括您试图生成的数字)的位表示形式@Plasmah,一个很好的地方是-1352353.3253:1100100110100101000010100000111-5.60519e-045:1000000000000000000000000000000001001:00111110000000000000000000000000000000000这就是二进制查找这些数字的地方。不过我不知道这个问题。@Eximius:很明显,返回的值包含来自其他地方的垃圾,因此程序集不会执行您认为它会执行的操作。查看已反汇编的函数可能会显示它与您认为提供的函数之间的偏差。这两个函数是否都捕获了
    -0.0f
    ?是否捕获了
    -0.0f
    的符号?我不这么认为:对于
    -0.0
    ,第一个将返回
    1.0f
    ,第二个
    NaN
    。我确实知道它们,但第一个是bool(并不比if(myfloat<0)好多少,第二个采用了一个多余的'magnity'参数。(另外,复制号似乎不存在于我的gcc上。)(它不老了)@Eximius与零的比较不能捕获
    -0.0f
    的特殊浮点值,尽管它与
    -INFINITY
    一起工作,并且与
    NAN
    有点假。即使“std::signbit”也应该可以工作。如果需要浮点值,只需使用“std::signbit(x)?-1.0f:1.0f”.std::signbit应该和我的第一行完全一样,如果设置了符号,则返回true,可以是-0.0或任何其他数字。-1,因为在大多数系统上float不是64位宽。而且因为它打破了别名规则,AFAIK。@wilx你是对的。我将64位固定为32位(这是float AFAIK最常见的大小)。这样就看不出有任何问题了(相反,它确实执行了eximius的asm代码应该执行的操作,包括由于0x8000000常量而对32位浮点的相同限制)。+1用于使用程序集可能会影响性能[负面]。对许多用户来说是一个惊喜。@Boperson:谢谢。我们甚至没有研究编译器可以进一步优化C代码的内联性能,而它必须将asm视为一个不可更改的黑盒。作为一个极端的例子,
    fabs(signf(x))
    可以优化为常量
    1
    ,但在使用asm版本时不能。
    float signf(float x)
    {
        float r;
        __asm__  __volatile__ (
                "and %0, 0x80000000;"
                "or %0, 0x3f800000;"
                :"=r"(r):"0"(x));
        return r;
    }
    
    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;
    }
    
    float signf(float x)
    {
      float r = 1;
      asm ("andl $0x80000000, %1\n"
           "\torl %1, %0\n"
           :"+r"(r):"r"(x));
      return r;
    }
    
    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;
    }