我的计划的不合逻辑的结果

时间:2013-09-21 14:27:16

标签: c++

我正在尝试解决这个问题:

将浮点数转换为十进制数。 例如,如果输入为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不起作用并且给出无限循环!!

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

5 个答案:

答案 0 :(得分: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。

答案 1 :(得分:2)

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

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

答案 2 :(得分:2)

以下解决方案的灵感来自Millimoose的评论 - 非常感谢!

将输入作为字符串读取。删除小数点右侧的任何尾随零。删除小数。转换为int。

注意 - 这特别有效,因为它避免了十进制表示中有限精度的问题 - 这是一个你总会遇到的问题,因为你从一个人类可读的浮点数开始。但是,如果您需要一个传递浮点数的不精确表示的函数,您可以期望的最好的方法是使用您的方法,并在有限次数的迭代后停止循环。 / p>

更好的方法是减去,并将余数乘以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转换向下舍入,此条件始终为真。

答案 3 :(得分:1)

您正在处理浮点数,所以请记住您必须小心精确度问题!

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

答案 4 :(得分:-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;