主题:[讨论]关于整数转字符串
今天发现了以前编写代码中的一个错误。
代码的主要目的是把整数转化为字符串,但需要在不调用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差不多,采用了特别乱的转换。
不知道还有没有更好一点的办法。
代码的主要目的是把整数转化为字符串,但需要在不调用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差不多,采用了特别乱的转换。
不知道还有没有更好一点的办法。