突然想起浮點的一些東西,無聊寫個求浮點的小數部分函數
說到浮點又想到前陣一件鬱悶的事
前陣項目中用到分解浮點的小數和整數部分用於顯示
不知道爲什麼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);
}
這樣可以做到任意位抽取
其實這些實現應該是庫都有的,但是沒有源代碼,並且我想後面做一些關於大數的運算
需要對浮點有些細緻的瞭解.