将浮动比率转换为int

时间:2019-06-29 21:59:31

标签: python math

我需要将浮动比率转换为等效的整数

0.5:1 ---应该转换为---> 1:2

0.5:0.6:1 ---应转换为---> 5:6:10(最小整数比)

我的谷歌搜索在这方面没有任何结果

2 个答案:

答案 0 :(得分:9)

float.as_integer_ratio

In [1064]: f = .5                                                                                                                                                                                           

In [1065]: f.as_integer_ratio()                                                                                                                                                                             
Out[1065]: (1, 2)

答案 1 :(得分:0)

对不起,不是python编码器,但这是一般方法(不受lib或语言的限制):

  1. 定义

    因此您得到了2个(或N)浮点数a,b,并希望具有2个整数aa,bb,这样:

    a/b == aa/bb
    
  2. 方法

    浮点数只是整数尾数,以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转换为整数而不更改其比率的变量(除非由于目标变量数据类型的位宽较小而减少了一些尾数位)。就像将数字乘以相同的常数一样。

  3. a,b提取指数

    可以简单地通过直接提取一个s整数的指数位并从中减去偏差使其有符号或使用log2()数学函数来完成。

  4. 计算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
    
  5. 位移或乘以a,b并构造结果

    所以只需将a,b转换为整数即可,如前面的项目符号所述。之后,您可以将结果整数除以它们的 GCD 或将它们右移,直到ab的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精度将以相同的方式完成,只需要更改常量,并且需要64bit2x32bit变量来存储整数尾数和结果...

精度取决于指数的相对距离。如果这些数字之间的差异太大,则结果数字将不适合目标整数,导致较小的数量级数字在以下情况下转换为零:

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案例算法

  1. 提取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);
     }
    
  2. 计算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;  
    
  3. 应用移位并构造结果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[]中。