为什么存储在变量中的值比原始答案少一个?

时间:2018-04-18 04:43:07

标签: c++ int double cout equation

d = 92.345; //it is of double type
scnt = 92; //it is of int type
//cout<<scnt;
int d1 =  (d-scnt)*10;
int d2 =  ((d-scnt)*100)-(d1*10);
int d3 =  ((d-scnt)*1000) - ((d1*100)+d2*10);//same equation if we print in cout gives actual answer
bool f1, f2;
cout<<"original value : "<<((d-scnt)*1000) - ((d1*100)+d2*10)<<endl;
cout<<"d3 : "<<d3<<endl;

如果您尝试运行上面的代码片段,您会注意到存储在d3变量中的值比实际答案少一个。 我期望存储在d3中的值应该是5,但它是4。 如果我把d3的数据类型设为double,那么它运行正常。但是进一步的代码要求d3的数据类型为int,所以我不能改变它。如果我在第6行计算它后简单地将d3增加1是否安全?

2 个答案:

答案 0 :(得分:0)

我猜你是想从双数中获取int变量中的每个小数。 如果您看到此代码的结果一步一步显示:

double d = 92.345;
int scnt = 92;
int d1 =  (d-scnt)*10;
cout << d1 << endl;
cout << (d-scnt)*10 << endl;
int d2 =  ((d-scnt)*100)-(d1*10);
cout << d2 << endl;
cout << ((d-scnt)*100)-(d1*10) << endl;
int d3 =  ((d-scnt)*1000) - ((d1*100)+d2*10);
cout << d3 << endl;
cout << ((d-scnt)*1000) - ((d1*100)+d2*10) << endl;

所以印刷的结果是:

d1 = 3
calculated d1 = 3.45
d2 = 4
calculated d2 = 4.5
d3 = 4
calculated d3 = 5

所以,你在int和cout中得到4的事实是因为,92.345不是双重有效可表示的数字,实际上是92.344999999999999所以,通过每个整数乘法你松散精度,但cout将这些数字视为双数,因此cout对这些数字进行了反复处理。

在低级别,所有操作都完全相同,除了存储或显示部分,这是唯一的区别。

  mov DWORD PTR [rbp-24], eax //Int saving

  mov edi, OFFSET FLAT:std::cout //Cout displaying
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(double)
  mov esi, OFFSET FLAT:std::basic_ostream<char, std::char_traits<char> >& std::endl<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&)
  mov rdi, rax
  call std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))

更多信息,请执行以下操作:

if (((d - scnt) * 1000) - ((d1 * 100) + d2 * 10) == ((d - scnt) * 100) - (d1 * 10)) {
        cout << "lol" << endl;
    }

不会打印“lol”。希望它有所帮助。

答案 1 :(得分:0)

问题的原因是浮点类型的基本属性。有关详细信息,请查看here

在您的特定情况下,92.345的值无法在浮点类型中精确表示。分数1/3不能在有限数量的十进制(基数10)位置(无限重复项)中表示。浮点数会产生相同的效果,除了浮点值是二进制(基数2)。试图以二进制表示0.345(即作为2的负幂之和)需要无限数量的二进制数字。

这样做的结果是,文字92.345在存储在任何浮点变量(类型为floatdoublelong double)时会有一个值不完全等于92.345。

实际值取决于系统上浮点变量的表示方式,但可能在您的系统上实际值略小于92.345。

这会影响您的计算(假设d属于浮点类型)。

int d1 =  (d-scnt)*10;        
int d2 =  ((d-scnt)*100)-(d1*10);
int d3 =  ((d-scnt)*1000) - ((d1*100)+d2*10);
对于浮点计算,

将受到舍入误差的影响。计算(d-scnt)*10将产生略小于3.45的值,这意味着d1将具有值3。同样,d2的值为4。问题(在此示例中)来自计算d3,因为(d-scnt)*1000将给出略小于345的浮点值,并且减去((d1*100)+d2*10)将得到稍微少于的浮点值比5。转换为int轮向零,因此如您所述,d3的最终值为4

增加d3以“修复”问题是一个非常糟糕的主意,因为浮点舍入可以采用任何一种方式。您将找到d的其他值,其中舍入方式相反(值double略大于您的预期)。还有一些值,您的代码将生成您期望的行为,而无需修改。

有多种方法可以解决这个问题。

一种方法是将值打印到具有所需位数的字符串。这可以使用std::ostringstream(来自标准标题<sstream>)轻松完成。这会将92.345转换为字符串"92.345"。从那里,您可以从字符串中提取数字。

另一种方式 - 我更喜欢 - 是编写计算以正确考虑浮点舍入。在C ++ 11及更高版本中,您可以使用round()中的<cmath>,如下所示。

int d1 =  round((d-scnt)*10);
int d2 =  round((d-scnt)*100)-(d1*10);
int d3 =  round((d-scnt)*1000) - ((d1*100)+d2*10);

或(明确说明从doubleint的转化发生在哪里)

int d1 =  static_cast<int>(round((d-scnt)*10));
int d2 =  static_cast<int>(round((d-scnt)*100)) -(d1*10);
int d3 =  static_cast<int>(round((d-scnt)*1000)) - ((d1*100)+d2*10);

在C ++ 11之前(没有提供round()),round(x)的效果大致可以模拟为floor(x + 0.5)