最佳base-10仅ITOA()函数

Optimal Base-10 only itoa() function?

本文关键字:函数 ITOA base-10 最佳      更新时间:2023-10-16

在20多年的编程中,我使用了一次以外的10个以外的基础,所以当我在另一个环境中找到我值得信赖的msvc的_itoa()在另一个环境中缺少了一个,我开始写一个这只能做10,并放置目标缓冲区参数,指向函数返回的存储,左侧,而不是在右侧,就像C标准库中的所有字符串函数一样。我相信此代码也是线程安全。

是否有更快的方法来执行此操作?

我也要询问正确性,但我相信随附的测试代码证明了它有效,即使对于long_min的特定情况,也就是(-1 * long_max)-1,这会导致代码中的故障直到我改变了战术,指出了标志,然后将签名的INT复制到未签名的INT中。然后,我在未签名的INT中完成了该功能的所有核心工作 - 在75%的时间内也很高兴地运行。

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 %sn", t, _i32toa(buff, t));
    printf("nString from integer %i is %sn", -0, _i32toa(buff, -0));
    printf("nString from integer %i is %sn", -1, _i32toa(buff, -1));
    printf("nString from integer %i is %sn", 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;
}

的性能是Visual Studio 2013中的不合时宜的_itoa()和10-15x的2-4x。

该方法有些新颖,取决于知道完整的字符串所需的缓冲区大小 - 问题分配了其自己的字符串缓冲区的功能,buff []求解,使其同时使其线程安全。

知道缓冲区的末端在哪里允许从背面到正面写下字符串的字符,从而解决了反向顺序问题。调用函数不需要以任何方式准备 *rtn,因为将memcpy()ed的工作字符串已经终止。

TVMIA的反馈。缺乏良好的_atoi()函数是一个足够持久的问题,值得一个好的解决方案。让我们做一个。

ps:在我的i7 Hazwell盒子上运行MSVS C 64位,具有完整的优化,从long_min到long_max平均每次转换为116个时钟,用于往返行程,只有28个时钟对于_itoa()。如果与Ben Voigt的代码进行比较,则每秒超过725兆字节。我想我赢了本!

您可以通过直接写入呼叫者的内存区域来消除memcpy
您应该让呼叫者通过缓冲区的大小。

另一个瓶颈是分裂,但我看不出如何解决。

编辑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;
}

摆脱自动char阵列并使它们的尺寸使它们可以检查溢出。

#define I32TOA( buff, val ) _i32toa( (buff), sizeof(buff), (val) )
char * _i32toa(char *const rtn, size_t size, int32_t i)    {
    if (NULL == rtn) return NULL;
    uint32_t  ut, ui;
    char minus_sign=0;
    char *p = rtn + size-1;
    *p-- = 0;    // nul-terminate buffer
    assert( p >= rtn );
    if (i < 0)    {
        minus_sign = '-';
        ui = (uint32_t)((int)-1 * (int)i);
    }    else    {
        ui = i;
    }
    while (ui > 9) {
        ut = ui;
        ui /= 10;
        *p-- = (ut - (ui * 10)) + 48;
        assert( p >= rtn );
    }
    *p = ui + 48;
    if ('-' == minus_sign) {
        *--p = minus_sign;
        assert( p >= rtn );
    }
    return p;
}