回 帖 发 新 帖 刷新版面

主题:[讨论]关于整数转字符串

今天发现了以前编写代码中的一个错误。
代码的主要目的是把整数转化为字符串,但需要在不调用itoa之类标准函数的情况下完成。我原来的代码大致如下:

[code=c]#include <stdio.h>
#include <limits.h>

void convert(short n) {
    if (n < 0) {
        putchar('-');
        n = -n;
    }

    if (n == 0) {
        putchar('0');
    } else {
        char buf[256];
        char* p = buf;

        while (n != 0) {
            *p++ = '0' + (n % 10);
            n /= 10;
        }

        while (p != buf) {
            putchar(*--p);
        }
    }

    putchar('\n');
}

int main() {
    convert(0);
    convert(12345);
    convert(32767);
    convert(-32768);
    return 0;
}[/code]

现在问题出来了,如果是用short类型的话,-32768这个值的输出是错误的。
可以想象,如果把short改为int,则-2147483648这个值的输出也是错误的。
如果把short改为64位的整数,则-9223372036854775808这个值的输出也是错误的。

问题的根源在于n = -n;这一句,因为对-32768取反的时候无法得到对应的正整数。于是后面的n%10,因为n为负数,这里的结果也就说不清楚了。
如何解决这个问题呢?

我对标准的itoa函数进行了反汇编,发现它用的是无符号的整数除法,回避了用负数去取模的问题。但是,如何把一个值为-32768的short成功的转化为值为32768的unsigned short呢?

继续跟踪,我找了一份itoa的实现(就是VC6所使用的那份实现),发现它里面是这样写的:
        if (is_neg) {
            /* negative, so output '-' and negate */
            *p++ = '-';
            val = (unsigned long)(-(long)val);
        }

其中val本身是unsigned long的类型。
参数从itoa传入,是int类型,先转化为unsigned long类型,得到val,如果参数为负,再执行val = (unsigned long)(-(long)val);,怎么看都感觉,太乱来了。

我又看了sprintf的实现,它把所有的整数都转化为unsigned __int64来做,但是总的来说跟上面的itoa差不多,采用了特别乱的转换。

不知道还有没有更好一点的办法。

回复列表 (共6个回复)

沙发

扯淡法:
先移位一次,计算,然后再把字符串重新乘回来

板凳

问题的根源在于n = -n;这一句,因为对-32768取反的时候无法得到对应的正整数
不知道还有没有更好一点的办法
--- 将那唯一一个特例作特殊处理,行不行?

3 楼

对于bt同学的建议,我认为由于编译器内核不同会导致边界差别,这样的话要为每个内核定义不同的边界,不过始终觉得不爽啊
扯淡法2:如果是负数,先抽出最后一位再修正符号,这样就不会有转换不回来的问题了吧?

4 楼

我认为由于编译器内核不同会导致边界差别
------ 没差异吧,c用SHRT_MIN,c++用numeric_limit<short>::min()
怕麻烦还可以用 ((short)1) << (sizeof(short)*8)

5 楼

你好.我是全职网赚工作者.
如果你有时间有电脑.
想在网络上创业.请联系我..
项目绝对真实.详情QQ空间资料
加盟请联系 QQ908889846

6 楼

[code=c]
    unsigned int a;
    int b=-2147483648;
    *((int*)&a)=b;
[/code]
申请加精喔:)

我来回复

您尚未登录,请登录后再回复。点此登录或注册