昨天研究了一天网络上著名的平方根倒数快速神奇算法。
代码如下:
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(" ");
}
}