这段简单的代码是我的问题:
扩展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
答案 0 :(得分: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->浮点转换(通过内存),这可能会影响性能。
上述代码的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
(如果可用)以避免分支。需要一些基准测试。
答案 1 :(得分:4)
在C ++ 11中有两个C ++函数:
bool std::signbit (x);
http://en.cppreference.com/w/cpp/numeric/math/signbit
,或者
float f = std::copysign (1.0f, x);
答案 2 :(得分:1)
这似乎运作良好(AT&amp; 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()
。您要做的事情是不可移植的,因为它只与IA-32平台和可以执行此asm()
语句的C ++编译器相关联。
编辑1
BTW,以下版本的工作方式相同(并生成与上述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;
}
答案 3 :(得分: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
答案 4 :(得分:0)
您不需要为此使用asm。以下是您尝试执行的操作(即使是-0.0f的正确结果)。
float signf(float x) {
bool sign=(0!=(*(reinterpret_cast<uint32_t *>(&x)) & 0x80000000));
return sign? -1.0f : 1.0f;
}