c/c++语言开发共享C如何计算没有浮点精度的百分比(perthousands)

如何计算从2个int值到表示百分比的int值的百分比(为了更准确度,数百个)?

背景/目的:使用没有FPU的处理器,浮点计算需要花费100倍的时间。

int x = 25; int y = 75; int resultPercentage; // desire is 250 which would mean 25.0 percent resultPercentage = (x/(x+y))*1000; // used 1000 instead of 100 for accuracy printf("Result= "); printf(resultPercentage); 

输出:

结果= 0

当我真正需要的是250.我不能使用任何浮点计算。

正常fpu计算的示例:

 int x = 25; int y = 75; int resultPercentage; // desire is 250 which would mean 25.0 percent resultPercentage = (int)( ( ((double)x)/(double(x+y)) ) *1000); //Uses FPU slow printf("Result= "); printf(resultPercentage); 

输出:

结果= 250

但是输出是以使用浮点计算为代价的。

    resultPercentage = (x/(x+y))*1000; 不起作用,因为(x/(x+y))在乘法*1000发生之前可能为01 。 代替:

    对于x/(x+y)的舍入无符号整数计算,设a = xb = x+y然后找到a/b使用:

     result = (a + b/2)/b; 

    对于a a/b使用的舍入无符号整数百分比%计算

     result = (100*a + b/2)/b; 

    对于圆形无符号整数permil‰计算a/b使用

     result = (1000*a + b/2)/b; 

    对于圆形无符号整数permyriad‱计算a/b使用

     result = (10000*a + b/2)/b; 

    @ H2CO3井指出了吃掉整数范围的担忧,因此乘法需要使用更宽的整数( longlong long )并且可能需要x+y

     result = (100L*a + b/2)/b; 

    当然,更换

     // printf(resultPercentage); printf("%dn", resultPercentage); 

    你可以写

     (x * 1000 + 5) / (10 * (x + y)) 

    如果你关心正确的舍入,

     (x * 100) / (x + y) 

    如果你不这样做。


    你在谈论“百分比”,但我注意到你乘以1000,这导致了数百人。 如果这就是你的意思,那么当然你需要将乘法因子分别改为10000和1000。

    此外,使用整数会显着降低执行计算的值的有效范围。 如果强制中间结果为更长的类型,特别是(有符号或无符号) long long ,则可以稍微加宽:

     (x * 1000ll + 5) / (10ll * (x + y)) 

    应该做的伎俩(由于整数提升)。

    使用长除法的解决方案

    现在,对于铅笔和纸的答案……不确定这是否比处理器的内置浮点运算更快,但是看起来很有趣(也许可以改进)。 这是一个长除法的实现(请记住?) – 原则上它是“无限精确”,有点像BigDecimal数学 – 在实践中它是有限的,因为为字符串分配了有限的空间(你可以改变它一个malloc/free )。 如果您的处理器(代码)空间有问题(以及缺少专用的浮点单元),那么这绝对不是可行的方法。 我也假设所有除法(偶数整数) 都很慢,并且只使用乘法,加法和减法。

    最终奖金 – 结果以字符串forms出现,以后需要单独的printf样式转换。 有许多方法可以加快速度; 现在看看你如何用有限的精度整数来解决这个问题很有趣,但是得到一个“非常好”的答案。 顺便提一下,根据我的行人定时代码,结果比分频和sprintf例程更快(这是非常令人满意的)。 几乎可以肯定,由于转换为一串数字“几乎是免费的”(如果您考虑通常如何进行,它需要大量的除法/模数学……)。

    以当前forms编辑 ,此代码甚至考虑了舍入(计算一个额外的数字,然后进行必要的调整)。 一个警告:它只适用于正整数。

    玩它并告诉我你是否喜欢它!

     #include  #include  void doRound(char *s, int n) { // round the number string in s // from the nth digit backwards. // first digit = #0 int ii; int N = n; if(s[n] - '0' < 5) { s[N] = ''; return; // remove last digit } while (n-- > 0) { if(s[n] == '.') { n--; } if(s[n] - '0' < 9) { s[n]++; s[N] = ''; return; } s[n] = '0'; } if (n == -1) { for (ii = N-1; ii >=0; ii--) { s[ii+1] = s[ii]; } s[0] = '1'; } else s[N] = ''; } void longDivision(unsigned int a, unsigned int b, char* r, int n) { // implement b / a using only integer add/subtract/multiply char temp[20]; // temporary location for result without decimal point char c = '0'; // current character (digit) of result int ci = 0; // character index - location in temp where we write c int t = n + 1; // precision - no more than n digits int dMult = 0; // scale factor (log10) to be applied at the end int di = 0; // first get numbers to correct relative scaling: while( a > b) { dMult++; b *=10; } while (10 * a < b) { dMult --; a*=10; } // now a >= b: find first digit with addition and subtraction only while( b > a ) { c++; b -= a; } t--; temp[ci++] = c; // copy first digit c = '0'; // now keep going; stop when we hit required number of digits while( b > 0 && t > 0) { b *= 10; t--; while( b > a ) { c++; b -= a; } temp[ ci++ ] = c; c = '0'; } // copy the result to the output string: temp[ ci ] = ''; // terminate temp string if (dMult > 0) { r[ di++ ] = '0'; r[ di++ ] = '.'; while( --dMult > 0 ) { r[ di++ ] = '0'; } ci = 0; while( temp[ ci ] != '' ) { r[ di++ ] = temp[ ci++ ]; } } else { ci = 0; while( temp[ ci ] != '') { r[ di++ ] = temp[ ci++ ]; if( dMult++ == 0 ) r[ di++ ] = '.'; } } r[ di ] = ''; // finally, do rounding: doRound(r, n+1); } int main(void) { int a, b; time_t startT, endT; char result[20]; int ii; a = 123; b = 700; longDivision(a, b, result, 5); printf("the result of %d / %d is %sn", b, a, result); printf("the actual result with floating point is %.5fn", (float) b / (float) a ); a = 7; b = 7000; longDivision(a, b, result, 5); printf("the result of %d / %d is %sn", b, a, result); a = 3000; b = 29999999; longDivision(a, b, result, 5); printf("the result of %d / %d is %sn", b, a, result); startT = clock(); for(ii = 1; ii < 100000; ii++) longDivision(a, ii, result, 5); endT = clock(); printf("time taken: %.2f msn", (endT - startT) * 1000.0 / CLOCKS_PER_SEC); // and using floating point: startT = clock(); for(ii = 1; ii < 100000; ii++) sprintf(result, "%.4f", (float) ii / (float) a); endT = clock(); printf("with floating point, time taken: %.2f msn", (endT - startT) * 1000.0 / CLOCKS_PER_SEC); return 0; } 

    结果(未启用优化):

     the result of 700 / 123 is 5.6911 the actual result with floating point is 5.69106 the result of 7000 / 7 is 1000.00 the result of 29999999 / 3000 is 10000.0 time taken: 16.95 ms with floating point, time taken: 35.97 ms 

    你为什么不用

     resultPercentage = (x*1000)/(x+y); 

    表达式的轻微修改可以解决问题。 就像在这种情况下:

    resultPercentage =(x * 1000)/(x + y); 应该做的工作。

    试试这个:

     int x = 25; int y = 75; int resultPercentage; // desire is 250 which would mean 25.0 percent resultPercentage = (x*1000)/(x+y); printf("Result= "); printf(resultPercentage); 

    如果您的要求只是找到一个给出两个数字之间关系的值,那么您可以使用1024而不是1000

    乘以1000将需要几个周期,但您可以使用

    resultRelation =(x << 10)/(x + y);

    乘法只需10个周期。 即使结果也不会有很大的变化1000.在找到百分比后,你可能会用你得到的百分比进行一些阈值处理,只需更改比较值即可。

    假设您正在使用

     if(resultPercentage > 500) do something 

    而不是那种用途

     if(resultRelation > 512) do something 

    这样,您可以找到相应的映射值并将其替换为它们。 如果你对周期和准确性都非常严格,那就去找一个保存的查找表,它将0-1024转换成0-1000但它使用了大量的RAM

      以上就是c/c++开发分享C如何计算没有浮点精度的百分比(perthousands)相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。

      本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

      ctvol管理联系方式QQ:251552304

      本文章地址:https://www.ctvol.com/c-cdevelopment/544784.html

      (0)
      上一篇 2021年1月9日
      下一篇 2021年1月9日

      精彩推荐