平方根联合和位移位

Square root union and Bit Shift

我发现了这段获得平方根的代码,令我惊讶的是它的工作方式,使用联合和位移这是代码:

float sqrt3(const float x)  
{
  union
  {
    int i;
    float x;
  } u;

  u.x = x;
  u.i = (1<<29) + (u.i >> 1) - (1<<22); 
  return u.x;
} 

首先保存在u.xx的值然后赋值给u.i 然后这个数的平方根神奇地出现了 u.x

谁给我解释一下这个算法?

你可以在维基百科上找到完美的解释:

Methods of computing square roots

请参阅部分:依赖于浮点表示的近似值

So for a 32-bit single precision floating point number in IEEE format (where notably, the power has a bias of 127 added for the represented form) you can get the approximate logarithm by interpreting its binary representation as a 32-bit integer, scaling it by 2^{-23}, and removing a bias of 127, i.e.

To get the square root, divide the logarithm by 2 and convert the value back.

以上代码存在 UB(未定义行为),因此不应相信它可以在任何平台上运行。这是因为它写入 union 的成员并从与上次用于写入 union 的成员不同的成员读回。它还在很大程度上取决于字节顺序(多字节整数中字节的顺序)。

但是,它通常会按预期执行,要了解为什么值得您阅读 IEEE 754 binary32 floating-point format

IEEE754 binary32 格式速成课程

IEEE754通常将一个32位的浮点数分成1个符号位、8个指数位和23个尾数位,从而得到

                   31 30-23           22-0
Bit#:               ||------||---------------------|
Bit Representation: seeeeeeeemmmmmmmmmmmmmmmmmmmmmmm
Value:              sign * 1.mantissa * pow(2, exponent-127)

数字基本上是“科学记数法,基数 2”。

作为一个细节,指数以“有偏差”的形式存储(也就是说,它的值高了 127 个单位)。这就是我们从编码指数中减去 127 以获得“真实”指数的原因。

简短说明

您的代码所做的是 指数 部分减半并损坏尾数 。这样做 因为一个数的平方根的指数大约是幅度的一半

以 10 为基数的示例

假设我们想要 4000000 = 4*10^6 的平方根。

4000000 ~ 4*10^6  <- Exponent is 6
4000    ~ 4*10^3  <- Divide exponent in half

只需将指数 6 除以 2,得到 3,并将其作为新指数,我们就已经在正确的数量级内,并且更接近真相,

2000 = sqrt(4000000)

.