标签:
题目描述:输入一个由数字组成的字符串,请把它转换成整数输出
分析:int型整数的范围为:-2147483648~+2147483647,这意味着字符串输入太长会没办法正常显示数字,故当转换后的数大于最大正数时,我们显示2147483647;当转换后的数小于最小负数时,我们显示-2147483648。
同时需要考虑字符串中含有非数字字符,以及字符串前有空格等情况,如" 123t444",这时我们规定输出123。此外,还应该考虑到字符串中含有正负号的问题。这些都是小问题,主要需要考虑溢出的情况:
如下,先写一个小程序分析unsigned与int的转换关系:
#include <iostream> using namespace std; int main(){ cout << "-1 = " << -1 << ", unsigned(-1) = " << unsigned(-1) << ", (int)((unsigned)(-1)) = " << (int)((unsigned)(-1)) << endl; cout << "-(unsigned)1 = " << -(unsigned)1 << " 或 " << (unsigned)(-(unsigned)1) << endl;//输出 4294967295 或 4294967295 unsigned tmp = -(unsigned)1; int nums[32], cnt = 0; do{ nums[cnt] = tmp % 2; tmp /= 2; ++cnt; }while(cnt < 32); cout << "-(unsigned)1的unsigned值为: "; for(cnt = 31; cnt >= 0; --cnt) cout << nums[cnt];//输出11111111111111111111111111111111 cout << endl << "-(unsigned)1的int值为: " << (int)tmp << endl;//输出-1 cout << "(unsigned)~0 >> 1 = " << ((unsigned)~0 >> 1) << endl; //输出2147483647 cout << "(int)(unsigned)~0 >> 1 = " << (int)((unsigned)~0 >> 1) << endl;//输出2147483647 cout << "-((unsigned)~0 >> 1) = " << -((unsigned)~0 >> 1) << endl;//输出2147483649 cout << "-(int)((unsigned)~0 >> 1) = " << -(int)((unsigned)~0 >> 1) << endl;//输出-2147483647 int MAX_INT = (int)((unsigned)~0 >> 1); int MIN_INT = -(int)((unsigned)~0 >> 1) - 1; }
分析:
第一句:由第一条语句的输出也表明对于代码中的常量,编译器默认它为int型,即有符号型,当加上unsigned做变换后,此后该常量变为无符号型,并按照它所存储的补码形式计算输出,若将其再加上int强制变换,则它此后便又是int型。
第二句到第十三句做了验证和int,unsigned转换分析:对1而言,(unsigned)1的在计算机中的补码存储形式为:00000000000000000000000000000001。对于-(unsigned)1, 加上负号后,就是大学里学过的知识(我记忆的口诀是:若为负数的二,十进制转换,按照"二对加;对十减"):令最高位的符号位变为1,其他位不变,即为它的原码;各位取反(符号位除外),即变为它的反码;最后将反码加1,即变为补码,故最后-(unsigned)1在计算机的补码存储形式为:11111111111111111111111111111111。
-(unsigned)1的值应该是将-(unsigned)1的补码存储形式按照无符号常量的补码存储形式来看,故大小为2^31 + 2^30 + ... + 2^0 = 4294967295。根据第一句分析,(unsigned)(-(unsigned)1) = -(unsigned)1。而(int)(-(unsigned)1)是将它的补码存储形式按照int来看,即需“对十减”,先将11111111111111111111111111111111反过来,为全0,再变为十进制为0,再减1,即(unsigned)(-(unsigned)1)=-1。
因此,我们若想求int的最大正数,而int的最大正数的补码形式为01111111111111111111111111111111,而((unsigned)~0 >> 1)对应的无符号型补码存储形式正好与int型的补码存储形式一致。故可直接int强制转换。
若想求int的最小负数,由于(int)((unsigned)~0 >> 1)已经是int类型,故可直接加负号变为-2147483647,再减去1就为int类型中最小的负数,至于补码的变换就不细述了。
-((unsigned)~0 >> 1):之所以输出2147483649,是因为加上负号后,其补码存储形式01111111111111111111111111111111变为10000000000000000000000000000001,而它又为无符号型,故输出2147483649。
总结:对于程序中使用的常量,编译器默认该常量为int型,当对其使用过unsigned后,该常量就变为unsigned型,此后也可对其再次强制转换为int型。对一个常量取int或unsigned值时,要根据该常量在计算机中的补码存储形式,按照int和unsigned的各自规则得出正确求值。当int转unsigned型时,若int为正或0,则两者补码存储形式一致,大小不会变;若int为负,此时根据int型的补码存储形式按照无符号的求值方式来计算无符号数的大小,大小肯定会改变;当unsigned转int时,若unsigned的二进制最高位为0,很显然,大小不会改变;若unsigned的二进制最高位为1,按照int负数的二进制转十进制的方法求int值,大小肯定也会改变;当对int值加负号时,int值变为其相反数;当对unsigned加负号时,其补码存储形式最高位置1,之后除最高位外按位取反,最后减1,获得其最终补码形式。
如下,先考虑以下有问题的程序片段:
while(isdigit(*str)){ int c = *str - '0'; if(sign == '+' && (c > (MAX_INT-n*10))){ n = MAX_INT; break; } else if(sign == '-' && (c - 1 < (MAX_INT-n*10)){ n = MIN_INT; break; } n = n*10 + c; ++str; }如输入字符串10522545459,当遍历第十次时,n为1052254545,并进入下次循环中执行c > (MAX_INT-n*10),根据上面的分析,首先MAX_INT肯定小于n*10,且两者为无符号数,相减得到一个负数的补码存储形式,但是得到的相减结果默认为一个无符号常量,而负数的补码存储形式对应的无符号值是很大的,因此c > (MAX_INT-n*10)肯定不成立,即使溢出,也无法进入该处理语句内,无法令最终结果为MAX_INT。
解决方式有两种,a,
既然得到一个负数,就强制转换为int型,即条件语句变为if(sign == ‘+‘ && (c > (int)(MAX_INT-n*10))),这样最后相减为负数也能判断出来,并且相减得到的正数的二进制补码最高位也不可能为1;
b,之所以有上述问题的存在,主要原因在于有减法的存在,故将条件语句改为if(sign == ‘+‘ && (n == MAX_INT/10 && c > MAX_INT%10) || (n > MAX_INT/10),这样就能考虑到溢出的问题,前者可应对8900000000,1052254545等这种情况,后者可应对2147483649这种情况。
当然n为负数时下面的代码也需要相应的改变,下面是代码:
#include <iostream> #include <string> using namespace std; int StrToInt(string &str){ int MAX_INT = (int)((unsigned)~0 >> 1); int MIN_INT = -(int)((unsigned)~0 >> 1) - 1; if(str.size() == 0) return 0; //去掉空格 int i = 0; while(str[i] == ' ') ++i;//测试过string会自动过滤字符串前面的空格 //处理正负 int sign = 1; if(str[i] == '-' || str[i] == '+'){ if(str[i] == '-') sign = -1; ++i; } //确定是数字后进行循环 unsigned n = 0; while(i < str.size() && isdigit(str[i])){ int c = str[i] - '0'; /**********************处理溢出***********************/ if(sign > 0 && ((n > (unsigned)MAX_INT/10) || (n == (unsigned)MAX_INT/10 && c > (unsigned)MAX_INT%10))){ n = (unsigned)MAX_INT; break; } if(sign < 0 && ((n > (unsigned)MIN_INT/10) || (n == (unsigned)MIN_INT/10 && c > (unsigned)MIN_INT%10))){//书中为(unsigned)(MIN_INT/10)),这是因为MIN_INT的补码为100...00,unsigned强制转换后,也正好为2147483648 n = (unsigned)MIN_INT; break; } /**********************处理溢出***********************/ //把之前得到的数字乘以10,再加上当前字符表示的数字 n = n*10 + c; ++i; } //前面是对无符号进行分析,最后将无符号转换为int输出 if(sign < 0 && n != (unsigned)MIN_INT) return -(int)n; else return (int)n; } int main(){ string str; while(cin >> str) cout << StrToInt(str) << endl; return 0; }
#include <iostream> #include <string> using namespace std; int StrToInt(string &str){ int MAX_INT = (int)((unsigned)~0 >> 1); int MIN_INT = -(int)((unsigned)~0 >> 1) - 1; if(str.size() == 0) return 0; //去掉空格 int i = 0; while(str[i] == ' ') ++i; //处理正负 int sign = 1; if(str[i] == '-' || str[i] == '+'){ if(str[i] == '-') sign = -1; ++i; } //确定是数字后进行循环 unsigned n = 0; while(i < str.size() && isdigit(str[i])){ int c = str[i] - '0'; /**********************处理溢出***********************/ if(sign > 0 && (c > (int)(MAX_INT - n*10))){ n = (unsigned)MAX_INT; break; } if(sign < 0 && ((c > (int)((unsigned)MIN_INT - n*10)))){ n = (unsigned)MIN_INT; break; } /**********************处理溢出***********************/ //把之前得到的数字乘以10,再加上当前字符表示的数字 n = n*10 + c; ++i; } //考虑无符号向int数据的转换 if(sign < 0 && n != (unsigned)MIN_INT) return -(int)n; else return (int)n; } int main(){ string str; while(cin >> str) cout << StrToInt(str) << endl; return 0; }
标签:
原文地址:http://blog.csdn.net/qibofang/article/details/51923997