关于浮点一点想法和使用

 突然想起浮点的一些东西,无聊写个求浮点的小数部分函数
说到浮点又想到前阵一件郁闷的事
前阵项目中用到分解浮点的小数和整数部分用于显示
不知道为什么ads中编译的代码在显示 "."的时候居然是乱码
很是郁闷没办法使用库函数只能自己写
当时也没当回事,觉得很简单
简单的分析了一下流程也很简单
1.判断是否为负数,是则保存标志
2.化成正小数依次求整数部分和小数部分
3.将整数部分按1步骤中的正负转成相应的正负数返回
于是粗粗的将函数设计成
void f(int* fi, int* ff);  将整数和小数部分分别返回
用了很长一段时间才发现这个函数居然有问题
我当时匆忙没有-0.XXX的格式是没有办法通过返回整数和小数用于显示
如果返回变成0, -XXX显示时就不对了,最后没有办法修改接口将符号也返回
代码通通改变,设计的时候功能分解的不彻底,就导致这种问题.如果彻底返回显示的字符串
或者是符号 整数 小数三部分都没有问题,就是想偷懒做半吊子事搞的自己郁闷.
废话说多了谈谈浮点
浮点根据IEEE浮点格式标准
符号1位 指数8位 位数23位  ----单精度格式
符号 sign: 表示正负.负数为1正书为0
指数 exponent: 表示数据以二为底的幂,单精度的指数偏移基数为127.
有效数字 significand:表示数据的有效数字.
基本上可以使用当前这个公式来求解浮点数据的值:
  (-1)^s ×(1+x) ×2^(e-127)
s是符号位 x有效数字 e指数

再看下实数转化为浮点的过程这个有助我们推导获得小数的过程
随便抄了例子
比如100.75 = 0110 0100.11b = 1.10010011×2^6
保留1指数是按照小数点的当前位置左移到第一个1后的位置偏移
符号为0
指数部分  6 = e-127 - > e=133=10000101b
有效数字 10010011 00000000 0000000
所以组成值为  0 10000101 10010011 0000 0000 0000 000

这样我们需要的小数0.75的计算过程需要 0.11->1*2^(-1)+1*2^(-2) = 0.5+0.25
这也是精度问题的原因浮点是计算出来的所以位数超过存储位后就会产生误差
求解小数部分我们从浮点结构入手就是要获得到小数的二进制表达式才能用于计算
1.获得指数e
2.获得有效数字
3.有效数字左移掉e-127得到小数位数
4.是计算的最重要部分
  此刻得到的是分布在整型中的和小数部分位分布相同的值
  我们观察小数中计算从最高位开始以1/2为底的幂进行计算累加
  而整型中计算是以最低位开始以2为底的幂进行累加
  此刻只要对整型中的数据*10 ^ 需要小数位数 除以2^小数的总位数就得到了小数值
理论上分析是这样的实际求解时抒写函数验证可以获得

#define F_SIGNED_NUM  1  //符号位
#define F_EXPONET_NUM  8  //指数位
#define F_SIGNIFICAND_NUM 23  //有效数字位
#define F_E_SIZE   127 //指数基数大小

unsigned int getff( float in_f, int n)
{
 unsigned int fi = 0;
 unsigned int i = 0, fe = 0, fs = 0;
 memcpy( (unsigned char*)&fi, (unsigned char*)&in_f, 4);
 fe = fi<<1>>(1+23);     //获得指数移码
 fs = fi<<(1+8)>>(1+8)<<(fe-127+1+8)>>(fe-127+1+8); //获得小数部分二进制整型数据
 while(n--)
  fs *= 10;     //乘以需要位的10的指数
 return (fs>>23-(fe-127));    //除以2^小数位数 得到小数值
}
这样算法只是理论上验证获得小数部分
对于位数获取很大的情况下导致fs*10 ^指数 造成溢出未做处理
以后我想找出一种更加简单安全的转化方法.不过此种方法实用64位的整型可以获得相当的精度
在实际使用中不要求非常多的小数位的话够用了.

可以考虑使用大数组存放进行乘法运算防止溢出
#include "stdio.h"
#include "memory.h"
#include "stdlib.h"
#include "string.h"
#define F_SIGNED_NUM  1  //符号位
#define F_EXPONET_NUM  8  //指数位
#define F_SIGNIFICAND_NUM 23  //有效数字位
#define F_E_SIZE   127  //指数基数大小

unsigned char bin2bcd(unsigned char bin)
{
 return (bin/10)<<4|(bin%10);
}

unsigned char bcd2bin(unsigned char bcd)
{
 return (bcd>>4)*10+bcd&0x0F;
}

void bins2bcds(unsigned char* src, unsigned char* des, unsigned int n)
{
 while (n--)
  *des++ = bin2bcd(*src++);
}
void bcds2bins(unsigned char* src, unsigned char* des, unsigned int n)
{
 while (n--)
  *des++ = bcd2bin(*src++);
}

void bcdsls4bins(char* src, int n )
{
 int i = 0;
 src[i=strlen(src)] = '0';
 *(src+i+1) = 0;
 
}
void bcdsrs4bins( char* src, int n )
{
 int i = 0;
 i = strlen(src);
 src[i-1] = 0;
}

void binsmul10(unsigned char* src, int n)
{
 unsigned char pt[200] = {0};
 int i = 0, l = 0;
 unsigned char tc = 0;

 l = strlen((char*)src);
 for( i = 0; i < l; i++ )
 { 
  if(src[i]<='9'&&src[i]>='0') 
   src[i] -= '0';
  else
   src[i] -= 'a'-10;
 }
 for( i = l-1; i >= 0; i-- )
 {
  src[i] *= 10;
 }
 for( i = l-1; i > 0; i-- )
 {
  src[i-1] += src[i]>>4;
  src[i] &= 0x0F;
 }
 if(src[i]&0xF0)
 {
  src[l+1] = 0;
  for( i = l-1; i > 0; i-- )
   src[i+1] = src[i];
  src[i+1] = src[i]&0x0F;
  src[i] >>= 4;
  l++;
 }
 for( i = 0; i < l; i++ )
 {
  if(src[i]<=9&&src[i]>=0) 
   src[i] += '0';
  else
   src[i] += 'a' - 10;
 }
 
}
int strtohex(unsigned char* src)
{
 int i = 0, tr = 0,l = 0;;

 l = strlen((char*)src);
 for( i = 0; i < l; i++ )
 { 
  if(src[i]<='9'&&src[i]>='0') 
   src[i] -= '0';
  else
   src[i] -= 'a'-10;
 }
 for( i = 0; i < l; i++)
 {
  tr <<= 4;
  tr |= src[i];
 }
 return tr;
}

unsigned int getff( float in_f, int n)
{
 unsigned int fi = 0;
 unsigned int i = 0, fe = 0, fs = 0;
 char ia[100]={0};
 unsigned int points = 0;

 memcpy( (unsigned char*)&fi, (unsigned char*)&in_f, 4);
 fe = fi<<1>>(1+23);    //获得指数移码
 //带整数和纯小数的小数部分
 if(fe>=127)
  fs = fi<<(fe-127+1+8)>>(fe-127+1+8);
 else
  fs = ((fi<<(1+8)>>(1+8))|0x00800000);
 points = 150-fe;
 //这个函数功能可由参考_itoa实现,不需要库
 sprintf((char*)ia, "%x", fs);
 while(n--)
  binsmul10((unsigned char*)ia, 10);  //乘以需要位的10的指数
 for( i = 0; i < points/4; i++ )
  bcdsrs4bins(ia,10);
 fs = strtohex((unsigned char*)ia);
 return (fs>>points%4);  //除以2^小数位数 得到小数值
}

void main()
{
 unsigned char farray[4], *fp = 0;
 float tf = 1.0375f;
 unsigned int fi = 0, i = 0, fe = 0;
 char test[100] = {0};

 fi = getff(tf, 6);
 sprintf(test, "%f", tf);
 tf = 0.7513458933;
 fi = getff(tf, 9);
 sprintf(test, "%0.10f", tf);
}
这样可以做到任意位抽取

其实这些实现应该是库都有的,但是没有源代码,并且我想后面做一些关于大数的运算

需要对浮点有些细致的了解.

发布了53 篇原创文章 · 获赞 1 · 访问量 20万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章