最优基10只itoa()函数? [英] Optimal Base-10 only itoa() function?

查看:118
本文介绍了最优基10只itoa()函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在超过20年的C编程我用比其他10个基地一次,所以当我发现我信赖的MSVC的_itoa()在另一个环境中失踪,我开始写一个只做基地10,并提出目标缓冲区的说法,指向由该函数返回的存储,在左边,而不是右边,像所有的C标准库中的字符串函数。我相信这code也是线程安全的。

In 20+ years programming in C I've used a base other than 10 once, so when I found my trusty MSVC's _itoa() missing in another environment, I set out to write one that only does base 10, and puts the destination buffer argument, pointing to the storage returned by the function, on the left, instead of on the right, like all of the string functions in the C Standard Library. I believe this code is also thread-safe.

有一个更快的方法来做到这一点?

我也要去问正确性,但我相信包括测试code证明了它的工作原理,甚至LONG_MIN的特定情况下,这是(-1 * LONG_MAX)-1,这引起故障在code,直到我改变了战术,注意到了号,然后复制符号int为unsigned int类型。然后,我做了所有的核心工作的无符号整数功能 - 这在75%的时间高兴地跑以及

I was also going to ask about correctness, but I believe the included test code proves it works, even for the particular case of LONG_MIN, which is (-1 * LONG_MAX) -1, which caused a failure in the code until I changed tactics, noted the sign, and then copied the signed int to an unsigned int. I then did all of the core work in the function in unsigned ints - which happily ran in 75% of the time as well.

char * _i32toa(char *const rtn, int32_t i)    {
    if (NULL == rtn) return NULL;

    // declare local buffer, and write to it back-to-front
    char buff[12];
    uint32_t  ut, ui;
    char minus_sign=0;
    char *p = buff + sizeof(buff)-1;
    *p-- = 0;    // nul-terminate buffer

    // deal with negative numbers while using an unsigned integer
    if (i < 0)    {
        minus_sign = '-';
        ui = (uint32_t)((int)-1 * (int)i);
    }    else    {
        ui = i;
    }

    // core code here...
    while (ui > 9) {
        ut = ui;
        ui /= 10;
        *p-- = (ut - (ui * 10)) + '0';
    }
    *p = ui + '0';

    if ('-' == minus_sign) *--p = minus_sign;

    // knowing how much storage we needed, copy chars from buff to rtn...
    memcpy(rtn, p, sizeof(buff)-(p - buff));

    return rtn;
}

// ------------------------------------------------------------------------------------------------
#define LOOP_KNT (SHRT_MAX * 1024)
// ------------------------------------------------------------------------------------------------
int main(void)    {
    time_t start = clock();

    int32_t t = 123456, i;
    char *buff = (char *)malloc(256);

    for (i = (SHRT_MIN *1024); i < LOOP_KNT; i++)    {
        _i32toa(buff, i);
    }
    printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));

    start = clock();
    for (i = (SHRT_MIN * 1024); i < LOOP_KNT; i++)    {
        _itoa(i, buff, 10);
    }
    printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));

    start = clock();
    for (i = (SHRT_MIN * 1024); i < LOOP_KNT; i++)    {
        ___itoa(i, buff, 10);
    }
    printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));

    printf("\nString from integer %i is %s\n", t, _i32toa(buff, t));
    printf("\nString from integer %i is %s\n", -0, _i32toa(buff, -0));
    printf("\nString from integer %i is %s\n", -1, _i32toa(buff, -1));
    printf("\nString from integer %i is %s\n", LONG_MIN, _i32toa(buff, LONG_MIN));

    start = clock();
    for (int i = LONG_MIN; i < LONG_MAX; i++) {
        if (i != atoi(_i32toa(buff, (int32_t)i))) {
            printf("\nError for %i", i);
        }
        if (!i) printf("\nAt zero");
    }
    printf("\nElapsed time was %f milliseconds", (double)clock() - (double)(start));

    getchar();
    return 0;
}

性能是2-4X不是部分的最-C标准的_itoa的()在Visual Studio 2013,并10-15X那的sprintf()。

Performance is 2-4X that of the not-part-of-the-C-standard _itoa() in Visual Studio 2013, and 10-15X that of sprintf().

该做法有些新颖,并且依赖于了解所需的缓冲区大小为完成字符串的 - 问题的函数分配它自己的字符串缓冲区,BUFF []解决了,使得它线程安全的在同一时间。

The approach is somewhat novel, and depends on knowing the required buffer size for the completed string - a problem the function allocating it's own string buffer, buff[] solves, making it thread-safe at the same time.

了解,其中缓冲区的末尾是允许从后面到前面写入的字符串的字符,解决了相反的顺序问题。调用函数不需要prepare * RTN以任何方式,因为这样得到的memcpy()的工作字符串* PTR已经是空值终止。

Knowing where the end of the buffer is allows the characters of the string to be written from the back to the front, solving the reverse order problem. The calling function doesn't need to prepare *rtn in any way, as the working string that gets memcpy()ed to *ptr is already null-terminated.

TVMIA您的反馈。缺乏一个良好的_atoi()函数是一种持久性不够的问题应有一个很好的解决方案。让我们做一个。

TVMIA for your feedback. The lack of a good _atoi() function is a persistent enough problem it deserves a good solution. Let's make one.

PS:在运行MSVS C ++ 64位全优化我的i7 Hazwell中,从LONG_MIN全循环LONG_MAX平均每次转换116时钟,用于往返,只有28个时钟对于_itoa()。这是每串的第二个超过725兆字节 - 如果比较本福格特的code。我觉得我赢了奔!

PS: On my i7 Hazwell box running MSVS C++ 64-bit with full optimizations, the full loop from LONG_MIN to LONG_MAX averages 116 clocks per conversion, for the round-trip, and only 28 clocks for _itoa(). That's over 725 megabytes per second of string - if comparing to Ben Voigt's code. I think I won Ben!

推荐答案

您可以消除的memcpy 通过直接写入到调用的存储区。结果
你应该有呼叫者传递的缓冲区的大小。

You can eliminate the memcpy by writing directly into the caller's memory area.
You should have the caller pass the size of the buffer.

另一个瓶颈是分裂的,但我不明白怎么去解决这一问题。

The other bottleneck is division, but I don't see how to get around that.

修改1:缓冲区指针的正确的初始化

char * _i32toa(char *const rtn, unsigned int buff_size, int32_t i)  
{
    if (NULL == rtn) return NULL;

    uint32_t  ut, ui;
    char minus_sign=0;
    char *p = rtn + buff_size - 1;
    // As before, without memcpy.
    return rtn;
}

这篇关于最优基10只itoa()函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆