3

我正在尝试优化以下内容。下面的代码执行此操作:

如果 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;
}
4

9 回答 9

2

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

于 2009-07-16T18:37:31.810 回答
2

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

我要说的是,如果您要接听数十亿次电话,即使是今天的技术也会窒息。

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

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

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

您将不得不依靠计算机硬件来完成其余的工作。

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

于 2009-07-16T18:44:03.700 回答
2

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

于 2009-07-16T18:51:39.520 回答
2

您可以通过将双 t[] 设为静态来节省您发布的代码中的一些主要周期,这样它就不会一遍又一遍地分配它。

于 2009-07-16T19:06:01.800 回答
1

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

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

于 2009-07-16T19:40:51.853 回答
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,它会溢出并向下舍入而不是向上舍入,即 234.00000000000001 在(我的)32 位环境中的双精度中被截断为 234。

于 2009-07-16T19:58:46.013 回答
0

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

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();
}

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

于 2009-07-16T22:42:34.857 回答
0

据我所知,您正在检查 p 点上的四舍五入是否等于 b。

将 a 更改为字符串,采用其他方式并将字符串更改为双精度 - (仅使用小表进行乘法和加法或仅加法) - 然后减去两个数字并检查减法是否在适当的范围内(如果 p==1 => abs (pa) < 0.05)

于 2009-07-20T01:16:26.993 回答
0

旧时代的开发商从旧国家的英镑、先令和便士的黑暗时代欺骗。

诀窍是将值存储为半便士的整数。(或者无论你的最小单位是什么)。然后你所有的后续算术都是简单的整数算术和舍入等将自行处理。

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

我相信 Boost 这些天来做了一个“BigDecimal”库,但是,您对运行时速度的要求可能会排除这个原本出色的解决方案。

于 2009-07-20T01:36:03.037 回答