C++自定义字符串转数值函数结果自动舍入问题求助
问题概述
编写了一个C++程序实现字符串转数值,先后使用double和float类型,处理带小数的输入(如1234.6789)时,结果总会自动舍入最后一位,添加调试输出仍未定位原因,需技术分析。
程序代码
#include <iostream> using namespace std; //variables initialised in a set of curly brackets are called local variables //variables initialised outside a set of curly brackets are called global variables // local variables can only be used in that set of curly brackets // global variables can be used anywhere after it has been declared // global variables are automatically set to 0 when it has not been assigned a value //a void function is a function that returns nothing //StringName.length() returns the length of a string /*----------------------------------------FUNCTION-------------------------------------*/ int pow(int a, int b){ int c = 1; for(int i=0;i<b;i++){ c*=a; } return c; } float StringNoToNo(string a){ float b=0.0; int y = 1; int s = 0; //cout<<"a.length() is: "<<a.length()<<endl; for(int i = (a.length()); i>0;i--){ // cout<<"the loop: "<<(a.length()-i)<<endl; int z = a[i-1]; //cout<<"z = "<<z<<endl; switch(z){ case 48 ... 57: // cout<<"(a[i]>=48)&&(a[i]<=57) is true"<<endl; //cout<<"pow(10.0,(a.length()-i)) = "<<pow(10.0,(a.length()-i))<<endl; //cout<<"a.length() - i = "<<(a.length()- i)<<endl; b += ((a[i-1]-48)* pow(10.0,(a.length()-i-s))); // cout<<"b= "<<b<<endl; break; case 46: y=pow(10,(a.length()-i)); // cout<<"y = "<<y<<endl; if(s==0){ s++;}else{ goto v; } break; default: v: // cout<<"(a[i]<48)||(a[i]>57) is true"<<endl; cout<< "the number was not written properly"<<endl; return 0; break; } } //cout<<" b = "<<b<<endl; //cout<<"b/y = "<<b/y<<endl; return (b/y); } /*----------------------------------------FUNCTION-------------------------------------*/ // overloading functions - you can create multiple functions with the same name so as long // as they have different parameters. // as long as the function is declared at the beginning, even if the function meant to // overload it is written at the end of the code, it can still be used. main() { string no; cout << "write a number: "; cin >> no; cout << "the number is: "<<StringNoToNo(no)<<endl; }
输入输出示例
write a number: 1234.6789 the number is: 1234.68 Process returned 0 (0x0) execution time : 4.713 s Press any key to continue.
调试输出
write a number: 1234.6789 the number is: a.length() is: 9 the loop: 0 z = 57 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 1 a.length() - i = 0 b= 9 the loop: 1 z = 56 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 10 a.length() - i = 1 b= 89 the loop: 2 z = 55 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 100 a.length() - i = 2 b= 789 the loop: 3 z = 54 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 1000 a.length() - i = 3 b= 6789 the loop: 4 z = 46 the loop: 5 z = 52 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 100000 a.length() - i = 5 b= 46789 the loop: 6 z = 51 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 1000000 a.length() - i = 6 b= 346789 the loop: 7 z = 50 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 10000000 a.length() - i = 7 b= 2.34679e+06 the loop: 8 z = 49 (a[i]>=48)&&(a[i]<=57) is true pow(10.0,(a.length()-i)) = 100000000 a.length() - i = 8 b= 1.23468e+07 1234.68 Process returned 0 (0x0) execution time : 24.055 s Press any key to continue.
原因分析
float类型的精度限制float仅能提供约6-7位有效数字,输入值1234.6789包含8位有效数字,超出float的精确存储范围。从调试输出可见,当累加至2.34679e+06时,数值已出现舍入,最终结果自然无法保留全部小数位。即使改用double,若输出时未指定精度,默认输出也可能因格式问题显示舍入后的结果。自定义
pow函数的隐患
自定义的pow函数返回int类型,当计算高次幂时可能触发整数溢出(例如pow(10, 10)会超出32位int的最大值),且调用时传入10.0会被隐式转换为int类型,虽未在当前案例中直接引发问题,但会导致浮点运算精度损失。输出格式的默认行为
cout输出浮点值时,默认仅显示6位有效数字,因此1234.6789会被格式化为1234.68(四舍五入到6位有效数字)。
解决建议
改用
double类型存储
将StringNoToNo函数的返回值和变量b改为double,double支持15-17位有效数字,足以精确存储当前输入的数值。替换自定义
pow为标准库函数
移除自定义的pow函数,包含<cmath>头文件后使用标准库的std::pow函数,或改用逐步累加的方式构建数值(例如:b = b * 10 + (a[i-1]-48)),避免整数溢出和精度损失。控制输出精度
在输出结果前添加格式控制,例如:cout << fixed << setprecision(4) << "the number is: "<<StringNoToNo(no)<<endl;需包含
<iomanip>头文件,确保输出保留指定的小数位数。
内容的提问来源于stack exchange,提问作者Shuayeb Ahmed

