以下解决方案的灵感来自 Millimoose 的评论 - 非常感谢!
将输入读取为字符串。去除小数点右侧的所有尾随零。去掉小数。转换为整数。
注意 - 这特别好用,因为它绕过了小数表示中的有限精度问题 - 你总是会遇到这个问题,因为你从一个人类可读的浮点数开始。但是,如果您需要一个传递浮点数的不精确表示的函数,那么您最好的希望是使用您的方法,并在有限次数的迭代后停止循环。
更好的是减去,然后将余数乘以 10。这样,您的循环可以始终终止(这就是数字到字符串表示函数的工作方式)。如果您打算在最后转换为整数,则需要确保不会溢出。如果您将数字收集到另一个字符串中,则不必担心溢出。
简而言之 - 如前所述,问题没有确切的解决方案,但以上内容应该让您对采取的方法有所了解。
这里的编辑是一些执行我描述的代码(不同的方法 - 并演示了溢出问题):
#include <iostream>
#include <math.h>
int main(void){
double d=0;
char buffer[100];
char buf2[100];
long int i,j;
std::cout<<"Enter a double : ";
fgets(buffer, 100, stdin);
strncpy(buf2, buffer, 100);
// method 1: use string manipulation to get the answer
i = (int)(strstr(buffer, ".") - buffer);
if (i >= 0)
{
j = strlen(buffer)-1;
while(buffer[j-1]=='0')
{
j--;
}
for(; i<j; i++)
{
buffer[i]=buffer[i+1];
}
buffer[j-1]='\0'; // trim it
}
std::cout << "The integer representation of that string is " << buffer << "\n";
// method 2: use a finite length conversion
sscanf(buf2, "%lf", &d);
printf("The string converted to double is %lf\n", d);
j = (long int)d;
sprintf(buffer, "%ld", j);
int k;
k = strlen(buffer);
d -= j;
for(i=0; i<20; i++)
{
d *= 10;
if (fabs(d) > 0)
{
sprintf(buffer + k, "%01d", (int)d);
k++;
printf("i = %ld; j is now %ld; d is %lf\n", i, j, d);
j = 10 * j + (int) d;
d -= (int) d;
}
else break;
}
printf("after conversion, the number is %ld\n", j);
printf("using the string method, it is %s\n", buffer);
}
这是示例输出:
Enter a double : 123.456001
The integer representation of that string is 123456001
The string converted to double is 123.456001
i = 0; j is now 123; d is 4.560010
i = 1; j is now 1234; d is 5.600100
i = 2; j is now 12345; d is 6.001000
i = 3; j is now 123456; d is 0.010000
i = 4; j is now 1234560; d is 0.100000
i = 5; j is now 12345600; d is 1.000000
i = 6; j is now 123456001; d is 0.000000
i = 7; j is now 1234560010; d is 0.000000
i = 8; j is now 12345600100; d is 0.000001
i = 9; j is now 123456001000; d is 0.000005
i = 10; j is now 1234560010000; d is 0.000054
i = 11; j is now 12345600100000; d is 0.000545
i = 12; j is now 123456001000000; d is 0.005448
i = 13; j is now 1234560010000000; d is 0.054479
i = 14; j is now 12345600100000000; d is 0.544787
i = 15; j is now 123456001000000000; d is 5.447873
i = 16; j is now 1234560010000000005; d is 4.478733
i = 17; j is now -6101143973709551562; d is 4.787326
i = 18; j is now -5671207515966860768; d is 7.873264
i = 19; j is now -1371842938539952825; d is 8.732636
after conversion, the number is 4728314688310023374
using the string method, it is 12345600100000000054478
从这里可以看出,有一些“舍入错误位”卡在右边;当我进行乘法和减法时,这些位最终变得可见。这个循环可以永远持续下去。我在 20 点停了它,实际上太多了——j
此时已经溢出了。不过,我认为它向您展示了到底发生了什么。
您必须定义您想要得到答案的精度,否则您无法笼统地解决这个问题。如果您确实需要超过 10 个左右的数字,我建议您使用基于字符串的方法,或者 BigDecimal 之类的方法。
PS 我为混合 C 和 C++ 道歉。我从来都不是一个 C++ 人,所以这在星期六早上更自然。
PS2:稍微修改一下上面的代码,我可以找到你提到的两个数字的完整十进制表示。事实证明
1.12 --> 112000000000000010658141036401502788066864013671875
换句话说,实际的表示比 略大1.12
。另一方面,
1.123 --> 11229999999999999982236431605997495353221893310546875
如您所见,它稍微小一些。这就是为什么您的循环永远不会终止的原因,因为您正在检查d - (int) d > 0.0
. 随着int
转换向下舍入,此条件始终为真。