1/sqrt(x)快速神奇算法来龙去脉,浮点数二进制表示和int二进制输出

昨天研究了一天网络上著名的平方根倒数快速神奇算法。

代码如下:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;
    x2 = number * 0.5F;
    y   = number;
    i   = * ( long * ) &y;   // evil floating point bit level hacking
    i   = 0x5f3759df - ( i >> 1 ); // what the fuck?
    y   = * ( float * ) &i;
    y   = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
    // y   = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed

    #ifndef Q3_VM
    #ifdef __linux__
         assert( !isnan(y) ); // bk010122 - FPE?
    #endif
    #endif
    return y;
}  

简洁版:

float InvSqrt (float x) {  
     float xhalf = 0.5f * x;  
     long i = *(long*)&x;  
     i = 0x5f3759df - (i >> 1);  
     x = *(float *)&i;  
     x = x * (1.5f - xhalf * x * x);  
     return x;  
}  

我先是看了这个帖子,里面讲了一些来龙去脉,但是有些和Wiki上的信息冲突:

https://blog.csdn.net/qq_26499321/article/details/73724763

对于这个算法的历史,我比较倾向于中文wiki的说法:

https://zh.wikipedia.org/wiki/%E5%B9%B3%E6%96%B9%E6%A0%B9%E5%80%92%E6%95%B0%E9%80%9F%E7%AE%97%E6%B3%95#cite_note-Beyond3D-1

关于它的整个算法流程描述和数学分析,下面这篇博客译文说得更清楚:

https://blog.csdn.net/zdy0_2004/article/details/52477640

然后对于里面的这个转换:

i   = * ( long * ) &y;   // evil floating point bit level hacking
y   = * ( float * ) &i;

涉及到 int (或者long, 在32位编译器下都是32bit的长度)和float的表示方法,然后去阅读了下面这个博客,说得比较清楚:

浮点数的二进制表示 http://www.ruanyifeng.com/blog/2010/06/ieee_floating-point_representation.html

上述博客开篇里面提到一个程序, 我做了一些修改,用Mac sublime text 编译了一下

(注:在网上找了一个二进制输出函数)

/*
* @Author: simonliu
* @Date:   2018-04-25 13:56:38
* @Last Modified by:   simonliu
* @Last Modified time: 2018-04-26 11:58:21
*/
#include <stdio.h>
#include <stdlib.h>


void print_2(int val2);

int main()
{

    int num=9;
    float *pFloat = (float*)#
    printf("num的值是:%d\n",num);
    printf("*pFloat的值是:%f\n", *pFloat);
    printf("内存的二进制值是:\n");
    print_2(num);
    printf("\n---------------\n");
    *pFloat = 9.0;
    printf("num的值是:%d\n",num);
    printf("*pFloat的值是:%f\n", *pFloat);
    printf("内存的二进制值是:");
    print_2(num);

    		return 0;

}

void print_2(int val2)
{
unsigned char *p = (unsigned char*)&val2 + 3; //从低位到高位,低端字节计算机
for(int k = 0; k <= 3; k++)
{
int val2 = *(p-k);
for (int i = 7; i >= 0; i--)
{
if(val2 & (1 << i))
printf("1");
else
printf("0");
}
printf(" ");
}
}

延伸问题:一段内存的二进制的输出

printf()可以直接打印十进制,八进制,十六进制,输出控制符分别为%d, %o, %x, 但是它不支持二进制输出。

有些编译器支持 stdlib.h的 itoa()函数,但是Mac自带的clang编译器不支持。所以上例中使用了一个网上找来的二进制输出函数(print_2)。

关于二进制输出,还有另外一篇博客写了一个递归取模的函数:

https://blog.csdn.net/xzongyuan/article/details/29562371

我不喜欢递归,所以自己写了一个按位操作取二进制的函数,完整代码如下

1.适合4个字节的int ,float类型变量。

2. 如果是int负数,输出的是补码而不是原码。

//
//  main.c
//  binOutput
//
//  Created by SimonLiu on 2018/5/2.
//  Copyright © 2018年 SimonLiu. All rights reserved.
//

#include <stdio.h>
#include "string.h"


int main()
{
    float num;

    printf("Enter float number:");
    scanf("%f",&num);

    int newNumber = *(int *)(&num); //把它看做整型
    
    int b = sizeof(num);
    printf("The number has %d bytes.\n",b);
    
    for (int i = (b*8)-1; i>=0; i--)
    {
        printf("%d",(newNumber&(1<<i))>>i);  
    }
    
    printf("\n");
    return 0;
}

下面这个算法是更早写的一个,略显复杂,也是针对4字节的int:

/*
 * @Author: simonliu
 * @Date:   2018-04-25 21:48:58
 * @Last Modified by:   simonliu
 * @Last Modified time: 2018-04-25 22:24:15
 */
#define __Mylog__
#ifdef __Mylog__
#define Mylog(format,...) printf("File: " __FILE__ ", Line: %05d: " format "/n", __LINE__, ##__VA_ARGS__)
#else
#define Mylog(format,...)
#endif
#include <stdio.h>
#include "string.h"


void dec2bin(int num);

int main()
{ int decimalNum;
    
    printf("Enter number in decimal:");
    scanf("%d",&decimalNum);
     Mylog("\n Mylog enabled...\n");
     Mylog("\n Decimal%d:",decimalNum);
    printf("binary format is : \n");
    dec2bin(decimalNum);
    printf("\n");
    return 0;
}

void dec2bin(int num)
{
    int i = 0;
    int buffer[32];
    memset(buffer, 0, sizeof(buffer));
    Mylog("\n num= %d, ",num);
    if (num < 0)
    {
        num &= ~(1 << 31); //首位清0,后面按照正数操作
        buffer[31] = 1;    //首位记录为1

    }
    Mylog("\n new num= %d, ",num);
    while (num >0)
    {

        if (num&1)  //与00000001 按位&, 即检查最后一位是否为1
        {
        buffer[i] = 1;
        Mylog("   buffer[%d] = %d\n",i,buffer[i]);
        }
        else
        {
        buffer[i] = 0;
         Mylog("   buffer[%d] = %d\n",i,buffer[i]);
        }
        Mylog("\nnum= %d, ",num);
        Mylog("i= %d\n",i);
        num = num >> 1 ;
        i ++;
        
    }
    for (int i = 31; i>=0; i--)
    {
        printf("%d",buffer[i]);
        if (i%4 == 0)
            printf(" ");
        
    }
    
}



  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值