C / C ++以一定的精度有效地舍入小数

时间:2009-07-16 18:29:35

标签: c++ c optimization double decimal

我正在尝试优化以下内容。下面的代码是这样的:

如果a = 0.775并且我需要精度2 dp,那么a => 0.78

基本上,如果最后一个数字是5,它会向下舍入下一个数字,否则它不会。

我的问题是0.45没有舍入到0.5,带有1个小数点,因为该值保存为0.44999999343 ....并且setprecision将其舍入为0.4。

这就是为什么setprecision被迫更高setprecision(p+10)然后如果它真的以5结尾,添加少量以便正确地向上舍入。

完成后,它会将a与字符串b进行比较并返回结果。问题是,这个函数被称为几十亿次,使程序崩溃。关于如何重写/优化这个以及代码中的哪些函数在机器上如此重要的任何更好的想法?

bool match(double a,string b,int p) { //p = precision no greater than 7dp

    double t[] = {0.2, 0.02, 0.002, 0.0002, 0.00002, 0.000002, 0.0000002, 0.00000002};

    stringstream buff;
    string temp;

    buff << setprecision(p+10) << setiosflags(ios_base::fixed) << a; // 10 decimal precision
    buff >> temp;

    if(temp[temp.size()-10] == '5')  a += t[p]; // help to round upwards

    ostringstream test;
    test << setprecision(p) << setiosflags(ios_base::fixed) << a;
    temp = test.str();

    if(b.compare(temp) == 0) return true;

    return false;
}

10 个答案:

答案 0 :(得分:2)

根据您想要的数字,您可能希望使用定点数而不是浮点数。快速搜索会出现this

答案 1 :(得分:2)

我写了一个整数平方根子程序,只有几十行ASM,没有任何API调用 - 它仍然只能做大约5000万SqRoots /秒(这大约是五年前......) 。

我要说的是,如果你要拨打数十亿电话,即使今天的技术也会窒息。

但是,如果您真的想加快速度,请尽可能多地删除API用法。这可能需要您手动执行API任务,而不是让库为您执行。具体来说,删除任何类型的流操作。在这种情况下,这些比污垢慢。你可能真的需要即兴发挥。

在此之后剩下的唯一事情就是用自定义ASM替换尽可能多的C ++行 - 但你必须成为一个完美主义者。确保您充分利用每个CPU周期和寄存器 - 以及CPU缓存和堆栈空间的每个字节。

您可以考虑使用整数值而不是浮点值,因为它们对ASM更友好且效率更高。您必须将数字乘以10 ^ 7(或10 ^ p,具体取决于您决定形成逻辑的方式)将小数一直向右移动。然后你可以安全地将浮点数转换为基本整数。

你必须依靠计算机硬件来完成剩下的工作。

<--Microsoft Specific-->
我还要补充说,C ++标识符(包括静态的标识符,如Donnie DeBoer所提到的)可以直接从嵌套到C ++代码中的ASM块访问。这使得内联ASM变得轻而易举 <--End Microsoft Specific-->

答案 2 :(得分:2)

我认为你可以将精度加到0.005为百分之一,0.0005加千,等等。使用类似“%1.2f”(百分之一,1.3千分之一秒等)的结果snprintf并比较字符串。您应该能够对此逻辑进行表格化或参数化。

答案 3 :(得分:2)

你可以通过将那个双t []静态保存在你发布的代码中来保存一些主要周期,这样它就不会一遍又一遍地分配它。

答案 4 :(得分:1)

使用浮点(不精确的表示)意味着您丢失了有关真实数字的一些信息。您不能通过添加软糖值来“修复”存储在double中的值。这可能会解决某些情况(如.45),但它会破坏其他情况。你最终会将应该向下舍入的数字四舍五入。

这是一篇相关的文章: http://www.theregister.co.uk/2006/08/12/floating_point_approximation/

答案 5 :(得分:1)

请改为尝试:

#include <cmath>

double setprecision(double x, int prec) {
    return 
        ceil( x * pow(10,(double)prec) - .4999999999999)
        / pow(10,(double)prec);
}

它可能更快。也许也尝试将其内联,但如果它没有用,可能会受到伤害。

它的工作原理示例:

2.345* 100 (10 to the 2nd power) = 234.5
234.5 - .4999999999999 = 234.0000000000001
ceil( 234.0000000000001 ) = 235
235 / 100 (10 to the 2nd power) = 2.35

选择.4999999999999是因为32位系统上c ++ double的精度。如果你在64位平台上,你可能需要更多的9。如果你在32位系统上进一步增加9,它会溢出并向下舍入而不是向上,i。即在(我的)32位环境中,234.00000000000001被截断为234。

答案 6 :(得分:0)

我正在猜测你真正想做什么。我怀疑你正试图看一个字符串是否包含一个精度的double的十进制表示。也许这是一个算术测验程序,你试图看看用户的回答是否与真实答案“足够接近”。如果是这种情况,那么将字符串转换为double可能会更简单,看看两个双精度之间差异的绝对值是否在一定容差范围内。

double string_to_double(const std::string &s)
{
    std::stringstream buffer(s);
    double d = 0.0;
    buffer >> d;
    return d;
}

bool match(const std::string &guess, double answer, int precision)
{
    const static double thresh[] = { 0.5, 0.05, 0.005, 0.0005, /* etc. */ };
    const double g = string_to_double(guess);
    const double delta = g - answer;
    return -thresh[precision] < delta && delta <= thresh[precision];
}

另一种可能性是在将答案转换为字符串之前先将答案四舍五入(虽然它仍为数字)。

bool match2(const std::string &guess, double answer, int precision)
{
    const static double thresh[] = {0.5, 0.05, 0.005, 0.0005, /* etc. */ };
    const double rounded = answer + thresh[precision];
    std::stringstream buffer;
    buffer << std::setprecision(precision) << rounded;
    return guess == buffer.str();
}

这两种解决方案都应该比您的示例代码更快,但我不确定它们是否能够满足您的需求。

答案 7 :(得分:0)

据我所知,你正在检查圆点的p点是否相等b。

将ins更改为字符串,进行其他操作并将字符串更改为double - (只是乘法和加法或只使用小表additoins) - 然后减去两个数字并检查减法是否在适当的范围内(如果p == 1 =&gt; abs(p-a)<0.05)

答案 8 :(得分:0)

旧时代的开发者从旧国家的Pounds,Shilling和Pence的黑暗时代中捣蛋。

诀窍是将值存储为半个整数。 (或者无论你的最小单位是什么)。然后你所有随后的算术都是直截了当的整数算术和舍入等将自己处理。

因此,在您的情况下,您将数据存储在您所计算的任何数量的200的单位中, 对这些值进行简单的整数计算,并在想要显示结果时将其除以200变量浮点数。

我相信Boost最近做了一个“BigDecimal”库,但是,你对运行时速度的要求可能会排除这个优秀的解决方案。

答案 9 :(得分:-1)

看起来你要做的事情并不是真正的四舍五入。 0.45的二进制表示法确实是0.45,而0.44999999343不是一回事。

可能需要进行多次舍入 - 首先说3位小数,然后是2位,然后是1位。

问题是,你想要完成什么?你的匹配标准应该是

吗?
abs(a-b) < 10 ** -p

代替?