0

我正在尝试解决这个练习:

将浮点数转换为十进制。例如,如果输入是 12.345,那么输出应该是 12345

...所以这是我的解决方案:

double d=0;
cout<<"Enter a double : ";
cin>>d;

while(d-(int)d > 0.)
    d*=10;

cout<<"Result : "<<d<<endl;

我认为该算法在理论上是正确的,但实际上有些值对我不起作用!

例如 :

1.123正常工作,程序给出1123,

但 1.12 不起作用并给出无限循环!

问题出在哪里,使我的程序正常工作的解决方案是什么?

4

5 回答 5

3

问题是浮点运算中涉及的舍入:您不能用二进制表示十进制数中可以表示的所有数字。

例如,假设您在 1/3 上以 3 的因子运行您的算法。十进制的 1/3 由 0.3333 表示,因此您的算法将计算以下内容:

 0.3333 * 3 =  0.9999
 0.9999 * 3 =  2.9997
 2.9997 * 3 =  8.9991
 8.9991 * 3 = 26.9973
26.9993 * 3 = 80.9919

如您所见,小数部分并没有像您预期的那样消失。

就像 1/3 不能用十进制表示,1/10 不能用二进制表示,所以你不能指望 10*(1/10) 以二进制计算为 1,就像你不能指望 3*(1/3)以十进制计算为 1。

于 2013-09-21T14:37:17.917 回答
2

浮点舍入错误——因为二进制浮点变量不能准确地表示所有十进制值。一些在打印为十进制数时准确的值存储在一个稍微小一点的二进制数中,因此while条件中的计算会给您留下一个负数。

调试:打印值 - 可能使用精确控制。printf()使用et al 比使用 et al更容易(更简洁)cout

于 2013-09-21T14:31:38.033 回答
2

以下解决方案的灵感来自 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转换向下舍入,此条件始终为真。

于 2013-09-21T14:38:05.177 回答
1

您正在处理浮点数,因此请始终记住您必须小心精度问题!

一般来说,如果您将该值读取到变量中,这是不可能double的,因为当您输入 3.33 时,它表示为类似3.32999978434的符号以及更多符号。你真的想以这种方式将其转换为整数吗?

于 2013-09-21T14:32:20.667 回答
-3

您为什么不打印出这些值并自己弄清楚呢?

double d=0;
cout<<"Enter a double : ";
cin>>d;

while(d-(int)d > 0.){
    cout << "d - (int)d is: " << d << " - " << (int)d <<endl;
    d*=10;
    cout << "Now d is: " << d << endl;
}

cout<<"Result : "<<d<<endl;
于 2013-09-21T14:30:52.873 回答