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是否安全?
答案 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
在存储在任何浮点变量(类型为float
,double
,long 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);
或(明确说明从double
到int
的转化发生在哪里)
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)