我需要将浮动比率转换为等效的整数
0.5:1 ---应该转换为---> 1:2
0.5:0.6:1 ---应转换为---> 5:6:10(最小整数比)
我的谷歌搜索在这方面没有任何结果
答案 0 :(得分:9)
float.as_integer_ratio
:
In [1064]: f = .5
In [1065]: f.as_integer_ratio()
Out[1065]: (1, 2)
答案 1 :(得分:0)
对不起,不是python编码器,但这是一般方法(不受lib或语言的限制):
定义
因此您得到了2个(或N
)浮点数a,b
,并希望具有2个整数aa,bb
,这样:
a/b == aa/bb
方法
浮点数只是整数尾数,以2为底的整数指数向左移动(如果为负指数则向右移动),因此:
a = sign(a)*mantisa(a)*2^exponent(a) = sign(a)*(mantisa(a)<<exponent(a))
b = sign(b)*mantisa(b)*2^exponent(b) = sign(b)*(mantisa(b)<<exponent(b))
因此,如果我们同时移动两个a,b
号,则较大数值的尾数的 msb (最高有效位)将变为某个整数的 msb 您将a,b
转换为整数而不更改其比率的变量(除非由于目标变量数据类型的位宽较小而减少了一些尾数位)。就像将数字乘以相同的常数一样。
从a,b
提取指数
可以简单地通过直接提取一个s整数的指数位并从中减去偏差使其有符号或使用log2()
数学函数来完成。
计算shift
我们需要将a,b
的尾数位移shift
位或将a,b
乘以2^shift
,以便较大的数值最大,仍然适合整数变量。因此,如果我假设32
位有符号整数,我们希望较大量级的 msb 为30
(位从0
开始编号,我们希望保留最后一点,这样我们仍然可以应用符号)。
计算很简单:
shift=max( exponent(a), exponent(b) );
shift=30-shift;
// shift-=_f32_man_bits; // this is just in case of bit-shifting
位移或乘以a,b
并构造结果
所以只需将a,b
转换为整数即可,如前面的项目符号所述。之后,您可以将结果整数除以它们的 GCD 或将它们右移,直到a
或b
的lsb为非零(删除尾随的零)。
以下是二进制示例:
exponent(b)=2 exponent(a)=-3
| |
| 0.0010101110b <- a
100.01101b <- b
--------------------------------------------------------------------------
_f32_man_bits = 23 // 32 bit float has 24 bit mantisa but first one is implicit
shift = 30 - max(exponent(b),exponent(a)) = 30 - 2 = 28
--------------------------------------------------------------------------
????????????????????????????????.0000000000b <- 32 bit integer variable
00000010101110000000000000000000.0000000000b <- a * (1 << shift) = mantissa(a)|(1<<_f32_man_bits) << (shift + exponent(a) - _f32_man_bits)
01000110100000000000000000000000.0000000000b <- b * (1 << shift) = mantissa(b)|(1<<_f32_man_bits) << (shift + exponent(b) - _f32_man_bits)
|
msb is zero so sign can still be applied ...
删除尾随零可以这样做:
// remove trailing zeros
for (;((aa|bb)&1)==0;)
{
aa>>=1;
bb>>=1;
}
以上示例将更改为:
0000001010111b
0100011010000b
通过 GCD 除法可以这样进行(删除尾随零后):
// divide by GCD
for (int d=3;(d<=aa)&&(d<=bb);d+=2)
while ((aa%d)+(bb%d)==0)
{ aa/=d; bb/=d; }
最后施加符号。
以下是C ++浮动示例(成倍增加):
void f32_ratio0(int &aa,int &bb,float a,float b) // aa/bb = a/b
{
// IEEE 754 constants
const DWORD _f32_man_bits=23; // mantisa bits (without implicit one)
// variables
int shift,d;
int expa,siga;
int expb,sigb;
// extract parts of a,b
siga=(a<0.0); a=fabs(a); sigb=(b<0.0); b=fabs(b);
expa=floor(log(a)/log(2.0)); expb=floor(log(b)/log(2.0));
// compute shift
shift=expa; if (shift<expb) shift=expb; // max(expa,expb)
shift=30-shift; // shift msb of bigger mantisa to 30th bit of integer
// construct result
aa=float(a*pow(2.0,shift));
bb=float(b*pow(2.0,shift));
// remove trailing zeros
for (;((aa|bb)&1)==0;)
{
aa>>=1;
bb>>=1;
}
// divide by GCD
for (d=3;(d<=aa)&&(d<=bb);d+=2)
while ((aa%d)+(bb%d)==0)
{ aa/=d; bb/=d; }
// sign
if (siga) aa=-aa;
if (sigb) bb=-bb;
}
以下是C ++整数示例(移位):
void f32_ratio1(int &aa,int &bb,float a,float b) // aa/bb = a/b
{
// IEEE 754 constants
const DWORD _f32_sig =0x80000000; // sign
const DWORD _f32_exp =0x7F800000; // exponent
const DWORD _f32_exp_sig=0x40000000; // exponent sign
const DWORD _f32_exp_bia=0x3F800000; // exponent bias
const DWORD _f32_exp_lsb=0x00800000; // exponent LSB
const DWORD _f32_man =0x007FFFFF; // mantisa
const DWORD _f32_man_msb=0x00400000; // mantisa MSB
const DWORD _f32_man_bits=23; // mantisa bits (without implicit one)
const DWORD _f32_exp_bias=127; // exponent bias
// float bits access
union
{
float f; // 32bit floating point
DWORD u; // 32 bit uint
} y;
// variables
int shift,d;
int mana,expa,siga;
int manb,expb,sigb;
// extract parts of a
y.f=a;
mana=(y.u&_f32_man)|_f32_exp_lsb;
expa=((y.u&_f32_exp)>>_f32_man_bits)-_f32_exp_bias;
siga=(y.u&_f32_sig);
// extract parts of b
y.f=b;
manb=(y.u&_f32_man)|_f32_exp_lsb;
expb=((y.u&_f32_exp)>>_f32_man_bits)-_f32_exp_bias;
sigb=(y.u&_f32_sig);
// compute shift
shift=expa; if (shift<expb) shift=expb; // max(expa,expb)
shift=(30-_f32_man_bits)-shift; // shift msb of bigger mantisa to 30th bit of integer
// construct result
d=shift+expa; aa=mana; if (d<0) aa>>=-d; else if (d>0) aa<<=d;
d=shift+expb; bb=manb; if (d<0) bb>>=-d; else if (d>0) bb<<=d;
// remove trailing zeros
for (;((aa|bb)&1)==0;)
{
aa>>=1;
bb>>=1;
}
// divide by GCD
for (d=3;(d<=aa)&&(d<=bb);d+=2)
while ((aa%d)+(bb%d)==0)
{ aa/=d; bb/=d; }
// sign
if (siga) aa=-aa;
if (sigb) bb=-bb;
}
其中DWORD
是任何无符号的32位数据类型,例如:
typedef unsigned __int32 DWORD;
double
精度将以相同的方式完成,只需要更改常量,并且需要64bit
或2x32bit
变量来存储整数尾数和结果...
精度取决于指数的相对距离。如果这些数字之间的差异太大,则结果数字将不适合目标整数,导致较小的数量级数字在以下情况下转换为零:
abs( exponent(a) - exponent(b) ) >= 31
如果将较大的位宽用于整数,则31也会相应改变...
现在是您的示例:
// a b a/b
0.50000 / 1.00000 = 0.500000 // floats
// aa bb aa/bb
1 / 2 = 0.500000 // ratio0
1 / 2 = 0.500000 // ratio1
// a b a/b
0.50000 / 0.60000 = 0.833333 // floats
// aa bb aa/bb
4194304 / 5033165 = 0.833333 // ratio0
4194304 / 5033165 = 0.833333 // ratio1
请注意,0.6
不能完全用浮点数表示,因此aa,bb
的值很大!要解决此问题,您需要添加舍入,但为此您需要知道告诉您要舍入的整数部分的阈值...在不知道目标浮点数范围或准确性的情况下,我无法安全地实现此目标...
如果您要保留更多浮点数之间的比率,而不仅仅是将其添加到函数中。
此处为3个变量的浮动C ++示例:
void f32_ratio0(int &aa,int &bb,int &cc,float a,float b,float c) // aa/bb/cc = a/b/c
{
// IEEE 754 constants
const DWORD _f32_man_bits=23; // mantisa bits (without implicit one)
// variables
int shift,d;
int expa,siga;
int expb,sigb;
int expc,sigc;
// extract parts of a,b
siga=(a<0.0); a=fabs(a); sigb=(b<0.0); b=fabs(b); sigc=(c<0.0); c=fabs(c);
expa=floor(log(a)/log(2.0)); expb=floor(log(b)/log(2.0)); expc=floor(log(c)/log(2.0));
// compute shift
shift=expa; // max(expa,expb)
if (shift<expb) shift=expb;
if (shift<expc) shift=expc;
shift=30-shift; // shift msb of bigger mantisa to 30th bit of integer
// construct result
aa=float(a*pow(2.0,shift));
bb=float(b*pow(2.0,shift));
cc=float(c*pow(2.0,shift));
// remove trailing zeros
for (;((aa|bb|cc)&1)==0;)
{
aa>>=1;
bb>>=1;
cc>>=1;
}
// divide by GCD
for (d=3;(d<=aa)&&(d<=bb)&&(d<=cc);d+=2)
while ((aa%d)+(bb%d)+(cc%d)==0)
{ aa/=d; bb/=d; cc/=d; }
// sign
if (siga) aa=-aa;
if (sigb) bb=-bb;
if (sigc) cc=-cc;
}
和您的示例结果:
// a b c
0.50000 / 0.60000 / 1.00000
// aa bb cc
4194304 / 5033165 / 8388608
[Edit1] N
案例算法
提取N
浮点数O(N)
因此我们有浮点数a0,a1,a2,...,a(N-1)
,并且需要整数指数e0,e1,...
和尾数m0,m1,...
和符号s0,s1,...
。对于32位浮点数,将是(使用上面示例中的// IEEE 754常量):
int i,m[N],e[N],s[N];
float a[N]={ ... your numbers here ... };
unsigned __int32 *u=(unsigned __int32*)a,i;
for (i=0;i<N;i++)
{
m[i]=(u[i]&_f32_man)|_f32_exp_lsb;
a[i]=((u[i]&_f32_exp)>>_f32_man_bits)-_f32_exp_bias;
s[i]=(u[i]&_f32_sig);
}
计算shift
及其O(N)
因此,首先计算最大e[i]
O(N)
,然后计算shift
本身O(1)
// shift = max(e[0...N-1])
int shift;
for (shift=e[0],i=1;i<N;i++)
if (shift<e[i])
shift=e[i];
// shift
shift=30-shift;
应用移位并构造结果O(N)
for (i=0;i<N;i++)
{
int d=shift+e[i]-_f32_man_bits;
if (d<0) m[i]>>=-d;
else if (d>0) m[i]<<= d;
if (s[i]) m[i]=-m[i];
}
结果在m[]
中。