平方根的C语言实现(三) ——最终程序实现
版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/7223254.html 作者:窗户 QQ:6679072 E-mail:6679072@qq.com
了解了浮点数的存储以及手算平方根的原理,我们可以考虑程序实现了。
先实现一个64位整数的平方根,根据之前的手算平方根,程序也不是那么难写了。
#include <stdint.h>
uint64_t _sqrt_u64(uint64_t a)
{
int i;
uint64_t res;
uint64_t remain; //0的平方根是0,特殊处理一下
if(a == 0ull)
return 0ull; //找到最高位的1,并且产生平方根结果最高位的1
for(i=62;;i-=2)
if(a&(3ull<<i)) {
res = 1ull;
remain = ((a&(3ull<<i))>>i) - 1ull;
i -= 2;
break;
} //根据手算平方根的原理,依次产生各位结果
for(;i>=0;i-=2) {
//右移动两位,并把a接着的两位并入remain
remain = (remain<<2)|((a&(3ull<<i))>>i);
if(((res<<2)|1ull) <= remain) {
//产生新一位的1
remain = remain - ((res<<2)|1ull);
res = (res<<1)|1ull;
} else {
//产生新一位的0
res <<= 1;
}
} return res;
}
其实,可以合在一起写,代码会短一些,但效率会低那么一点点,而且编译器应该不太容易优化。
#include <stdint.h>
uint64_t _sqrt_u64(uint64_t a)
{
int i;
uint64_t res;
uint64_t remain; res = remain = 0ull; for(i=62;i>=0;i-=2) {
remain = (remain<<2)|((a&(3ull<<i))>>i);
if(((res<<2)|1ull) <= remain) {
remain = remain - ((res<<2)|1ull);
res = (res<<1)|1ull;
} else {
res <<= 1;
}
} return res;
}
不过,我们不需要这个结果。
为了验证其正确性,我们来写个C语言的main
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
uint64_t _sqrt_u64(uint64_t a); int main()
{
uint64_t a, b;
scanf("%" PRIu64, &a);
b = _sqrt_u64(a);
printf("%" PRIu64 "\n",b);
return 0;
}
我们shell程序测试一下,我们当然不可能测试过每一个64bits的数,这个运算量太大,不现实。我们可以用随机取一部分来测试。
#!/bin/bash #编译
gcc -O2 -s sqrt_u64.c main_sqrt_u64.c -o a.out #随机测试10000个数
for((i=;i<;i++));do
#随机产生bits ~,如果是0,代表测试的数就是0
#如果不是0,则代表要产生的数二进制可以有多少位
let bits=RANDOM%
if [ $bits -eq ]; then
x=
y=
else
#产生一个bits位的二进制数x
x=$({
#最高位1
echo -n
#之后每位随机产生
for((j=;j<bits;j++));do
let x=RANDOM%
echo -n $x
done
})
#用bc将x转换成十进制
x=$(echo 'obase=10;ibase=2;'"$x" | bc)
#用bc计算x的平方根取整,理论上和我们的C语言计算一致
y=$(echo 'sqrt('"$x"')' | bc)
fi
#z是我们的C语言计算结果
z=$(echo $x | ./a.out)
#比较,如果不一致,就报错
if [ $y -ne $z ];then
echo $x $y $z error
exit
fi
done
echo OK
测试结果表明,我们的C语言还是可以得到正确的结果的。
再来回忆下第一节里讲过的浮点数结构,
S(1bits) | N(8bits) | A(23bits)
对于浮点数a*2n,
1<=a<2,n为整数,
如果n是偶数,
那么a*2n的平方根是sqrt(a)*2n/2,也满足1<=sqrt(a)<2,n/2是整数;
如果n为奇数,
那么a*2n的平方根是sqrt(2*a)*2(n-1)/2,也满足1<=sqrt(2*a)<2,(n-1)/2是整数。
所以此处要用a或者2*a来开平方根,
回忆一下浮点数的结构,单精度浮点数的精度是23位。
表示的是科学计数法a*2n的a减去1的部分,那么加上整数1可以用二进制24位表示。
于是,我们就想,一个二进制48位或47位长的数,平方根是二进制24位。那么,我们就可以用一个48位或47位的二进制整数的平方根计算结果的小数部分。
nan/inf/-inf以及负数的平方根都是nan,
0.0的平方根是0.0,
-0.0的平方根是-0.0(可能只是某些库里是这样的),
以上都可以在计算的时候特殊化一下。
规格数(就是用科学计数法表示的浮点数)的平方根也是规格数,
S=0,N=0,A>0代表的是A*2-149,也就是(A*2)*2-150,
我们稍微计算一下,可以明白,所有的此类数的平方根都在规格数表示的范围内。
于是,有了以下的代码。
#include <stdint.h>
static uint32_t _sqrt_(uint64_t a)
{
int i;
uint64_t res;
uint64_t remain; res = remain = 0ull; //之前整数平方根被直接优化,我们只需要求47位或者48位整数的平方根
for(i=46;i>=0;i-=2) {
remain = (remain<<2)|((a&(3ull<<i))>>i);
if(((res<<2)|1ull) <= remain) {
remain = remain - ((res<<2)|1ull);
res = (res<<1)|1ull;
} else {
res <<= 1;
}
} return (uint32_t)res;
} float mysqrtf(float f)
{
union {
float f;
uint32_t u;
} n;
uint32_t N,A;
int _N, i;
uint64_t _A; n.f = f;
if(n.u == 0x80000000 || n.u == 0x00000000) /* 0.0/-0.0 */
return n.f;
N = (n.u&(0xff<<23))>>23;
if(N==0xff||(n.u&0x80000000)) { /* inf/-inf/nan/ f < 0.0*/
n.u = 0x7fc00000; /* nan */
return n.f;
}
if(N!=0x0) { /* 用科学计数法表示的规格数 */
A = (n.u&0x7fffff)|0x800000;
_N = (int)N - 127;
if(N&0x1) {
_A = (uint64_t)A<<23;
} else {
_A = (uint64_t)A<<24;
_N--;
}
} else { //A*2^(-149)这种表示方式的浮点数
//还是需要找最高位
for(i=22;;i--)
if(n.u&((0x1)<<i))
break;
//然后需要移位,要区分奇数和偶数
if(i&0x1) {
_N = i-149;
_A = (uint64_t)n.u << (46-i);
} else {
_N = i-150;
_A = (uint64_t)n.u << (47-i);
}
}
//小数部分
A = _sqrt_(_A);
//指数部分
N = (uint32_t)(_N/2+127);
//得到结果
n.u = (A&0x7fffff)|(N<<23);
return n.f;
}
同样,也写个测试用的程序,对inf/-inf/nan/0.0/-0.0以及负数不测了,这些很简单。
#include <stdio.h>
#include <stdint.h>
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <inttypes.h> int main(int argc, char **argv)
{
union {
float f;
uint32_t u;
} n;
uint32_t A,N;
float f,f2;
int i; srand((unsigned)time(NULL));
//随机10000个数据
for(i=0;i<10000;i++) {
N = rand()%256;
if(N==255)
N=254;
A = 0x0;
A |= rand()%256;
A |= (rand()%256)>>8;
A |= (rand()%256)>>16;
n.u = (A&0x7fffff)|(N<<23);
f = sqrtf(n.f);
f2 = mysqrtf(n.f);
printf("%.60f %.60f\n",f,f2);
} return 0;
}
结果发现,我们的程序和数学库里的sqrtf结果有细微差别。
于是,我们决定再加个小东西,就是四舍五入。之前我们用的是47位或者48位数开平方,为了四舍五入,我们需要多一位,于是就用49位或者50位数开平方。
修改一下mysqrtf,增加两位拿去开平方,_sqrt_也动一下。
#include <stdint.h>
static uint32_t _sqrt_(uint64_t a)
{
int i;
uint64_t res;
uint64_t remain; res = remain = 0ull; //之前整数平方根被直接优化,我们只需要求49位或者50位整数的平方根
for(i=48;i>=0;i-=2) {//这里之前是46,改成48
remain = (remain<<2)|((a&(3ull<<i))>>i);
if(((res<<2)|1ull) <= remain) {
remain = remain - ((res<<2)|1ull);
res = (res<<1)|1ull;
} else {
res <<= 1;
}
} return (uint32_t)res;
}
float mysqrtf(float f)
{
union {
float f;
uint32_t u;
} n;
uint32_t N,A;
int _N, i;
uint64_t _A; n.f = f;
if(n.u == 0x80000000 || n.u == 0x00000000) /* 0.0/-0.0 */
return n.f;
N = (n.u&(0xff<<23))>>23;
if(N==0xff||(n.u&0x80000000)) { /* inf/-inf/nan/ f < 0.0*/
n.u = 0x7fc00000; /* nan */
return n.f;
}
if(N!=0x0) { /* 用科学计数法表示的规格数 */
A = (n.u&0x7fffff)|0x800000;
_N = (int)N - 127;
if(N&0x1) {
_A = (uint64_t)A<<25;
} else {
_A = (uint64_t)A<<26;
_N--;
}
} else { //A*2^(-149)这种表示方式的浮点数
//还是需要找最高位
for(i=22;;i--)
if(n.u&((0x1)<<i))
break;
//然后需要移位,要区分奇数和偶数
if(i&0x1) {
_N = i-149;
_A = (uint64_t)n.u << (48-i);
} else {
_N = i-150;
_A = (uint64_t)n.u << (49-i);
}
}
//小数部分
A = _sqrt_(_A);
//四舍五入
A = (A+(A&0x1))>>1;
//指数部分
N = (uint32_t)(_N/2+127);
//得到结果
n.u = (A&0x7fffff)|(N<<23);
return n.f;
}
然后再测,准确无误。于是我们可以完工了。
平方根的C语言实现(三) ——最终程序实现的更多相关文章
- 深入研究C语言 第三篇
本篇研究TC2.0下其他几个工具.同时看看TC由源代码到exe程序的过程. 1. 用TCC将下面的程序编为.obj文件 我们知道,TCC在默认的编译连接一个C语言的源程序a.c的时候分为以下两步: ( ...
- 利用Scala语言开发Spark应用程序
Spark内核是由Scala语言开发的,因此使用Scala语言开发Spark应用程序是自然而然的事情.如果你对Scala语言还不太熟悉,可 以阅读网络教程A Scala Tutorial for Ja ...
- .NET DLL 保护措施详解(三)最终效果
针对.NET DLL 保护措施详解所述思路完成最终的实现,以下为程序包下载地址 下载 注意: 运行环境为.net4.0,需要安装VS2015 C++可发行组件包vc_redist.x86.exe.然后 ...
- ASP.NET MVC:多语言的三种技术处理策略
ASP.NET MVC:多语言的三种技术处理策略 背景 本文介绍了多语言的三种技术处理策略,每种策略对应一种场景,这三种场景是: 多语言资源信息只被.NET使用. 多语言资源信息只被Javascrip ...
- Swift5 语言指南(三) 快速之旅
传统表明,新语言中的第一个程序应在屏幕上打印“Hello,world!”字样.在Swift中,这可以在一行中完成: print("Hello, world!") // Prints ...
- UWP 多语言的三个概念
首先了解一下 RFC4646 和 BCP-47 是什么东西: RFC4646 The name is a combination of an ISO 639 two-letter lowercase ...
- SAS进阶《深入解析SAS》之开发多语言支持的SAS程序
SAS进阶<深入解析SAS>之开发多语言支持的SAS程序 1. 多语言支持的应用程序是指该程序在世界给第使用时,其能够处理的数据,以及处理数据的方式.信息展现的方式都符合当地的语言.文化习 ...
- [转载] 使用C/C++语言编写基于DSP程序的注意事项
原文地址:『转』使用C/C++语言编写基于DSP程序的注意事项作者:skysmile 1.不影响执行速度的情况下,可以使用c或c/c++语言提供的函数库,也可以自己设计函数,这样更易于使用“裁缝师 ...
- C语言之简易了解程序环境
C语言之简易了解程序环境 大纲: 程序的翻译环境 预编译 编译 汇编 链接 程序的运行环境 在ANSI C的任何一种实现中,存在两个不同的环境. 第1种是翻译环境,在这个环境中源代码被转换为可执行的机 ...
随机推荐
- Python3 下实现 Tencent AI 调用
1.背景 a.鹅厂近期发布了自己的AI api,包括身份证ocr.名片ocr.文本分析等一堆API,因为前期项目用到图形OCR,遂实现试用了一下,发现准确率还不错,放出来给大家共享一下. b.基于py ...
- python学习笔记 改变字符串中的某一位
a = ' a = list(a) a[2] = ' news = ''.join(a) print news,a 注意不能使用 news = '' news.join(a) 因为news.join只 ...
- C++ sqlite3解决中文排序问题
导言:sqlite3默认的编码方式为UTF8编码,而在UTF8编码下,中文不是按照拼音顺序编码的,所以想解决中文排序问题,必须自定义排序规则,将UTF8编码转换成GB2312编码(GB2312编码中文 ...
- C/S架构自动化测试入门
所谓C/S架构即Client/Server(客户端/服务器架构).虽然近年来C/S架构产品越来越少,大有被B/S(Browser/Server 浏览器/服务器)架构超越的趋势,但C/S还是有B/S不可 ...
- 2746:约瑟夫问题poj
2746:约瑟夫问题 总时间限制: 1000ms 内存限制: 65536kB 描述 约瑟夫问题:有n只猴子,按顺时针方向围成一圈选大王(编号从1到n),从第1号开始报数,一直数到m,数到m的猴子退 ...
- 去培训机构参加IT培训值不值
近几年,IT培训机构可谓是琳琅满目,稂莠不齐.培训Java的,培训PHP的,培训大数据的等等吧,不一而足. 自己也算是IT技术圈子待了好多年了,面试过一些机构培训出来的学生,也有几个好哥们在培训机构做 ...
- SSM框架开发web项目系列(二) MyBatis真正的力量
前言 上篇SSM框架环境搭建篇,演示了我们进行web开发必不可少的一些配置和准备工作,如果这方面还有疑问的地方,可以先参考上一篇“SSM框架开发web项目系列(一) 环境搭建篇”.本文主要介绍MyBa ...
- js代码大全(各种方法、属性)《转载》
事件源对象 event.srcElement.tagName event.srcElement.type 捕获释放 event.srcElement.setCapture(); event.sr ...
- 使用python
最近看视频学习,老师布置了个作业,关于如何使用python将多个excel进行合并,老师写的代码我感觉比较复杂,下面是我自己改良之后较简单的方式. 实现这个功能主要有两种方法,一种是用xlwd,xls ...
- Thinkphp高仿陌陌网页直播
此项目利用 TP+Redis+Nginx+nginx-rtmp-module+ffmpeg+HLS +Swoole 的架构方案 GitHub 地址:https://github.com/DOUBLE- ...