#include <iostream>
#include <cmath>
using namespace std;
string number = "159";
float valueFloat = 0;
int valueInt = 0;
int main()
{
for(int i = number.length()-1; i >=0; i--)
{
valueInt += (number[i]-48) * pow(10,i);
valueFloat += (number[i]-48) * pow(10,i);
}
cout<<"Int value: "<<valueInt<<endl;
cout<<"Float value: "<<valueFloat<<endl;
return 0;
}
输出:
Int value: 950
Float value: 951
为什么此代码返回不同的值?为什么Int类型有错?
答案 0 :(得分:2)
这不是一致的行为。即使在int
某些平台上,它也可能返回正确的数字。问题出在pow(10,i)
此函数返回float
个数字,当将其添加到其他int
(或char
)时,它会返回另一个float
个数字。然后,您将整个数字添加到int
变量,这意味着必须从float
到int
进行投射。您在float
中获得的号码可能是951
或950.99999
(或类似内容)。如果将950.99999
投放到int
,那么它将是950
。这解释了结果。
如果你想出于某种原因(似乎不存在)坚持int
变量,你应该做一些类似的事情:
for(int i = number.length()-1; i >=0; i--)
{
float p_float=pow(10,i);
int p_int=static_cast<int>(p_float);
p_int=(p_int-p_float)>.5f?p_int+1:p_int;
valueInt += (number[i]-48) * p_int;
valueFloat += (number[i]-48) * p_float;
}
或者 @Mats Petersson 提到你可以使用std::lround
:
for(int i = number.length()-1; i >=0; i--)
{
valueInt += (number[i]-48) * std::lround(pow(10,i));
valueFloat += (number[i]-48) * pow(10,i);
}
答案 1 :(得分:1)
即使我无法使用gcc 4.9.2重新生成您所看到的内容,我在循环中添加了一些printfs,如下所示:
printf("%d\n", pow(10, i));
printf("%f\n", pow(10, i));
得到了:
gsamaras@pythagoras:~$ g++ -Wall pc.cpp
pc.cpp: In function ‘int main()’:
pc.cpp:14:27: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘__gnu_cxx::__promote_2<int, int, double, double>::__type {aka double}’ [-Wformat=]
printf("%d\n", pow(10, i));
^
gsamaras@pythagoras:~$ ./a.out
1076101120
100.000000
1076101120
10.000000
1076101120
1.000000
Int value: 951
Float value: 951
我怀疑您看到此行为是因为pow()具有此签名(最接近int
):
float pow(float base,float exponent);
结果,pow()
返回float
,然后转换为int
,这可能会导致某些准确性丢失。
答案 2 :(得分:1)
问题在于pow
计算的值不是整数值(可能pow(x, y)
计算exp(ln(x)*y)
- 但它不一定要那样),但是一个浮点值,它将有一个小的舍入误差。这可能是正确值的“低于”或“高于”,并且由于从float
到int
的转换是截断,950.999996
会转换为950
而不是{{} 1}}。在这种情况下,您可以通过将951
的结果四舍五入到最近而不是截断[假设结果与无限精确值相差小于+/- 0.5]来解决这个问题。
计算浮点值的方式始终具有一定的精度。整数计算总是精确到计算本身的最小整数值,但是对于courwse,C和C ++的规则是如果一边是浮点,则计算是以浮点执行的。
我会完全消除代码中使用(通常很麻烦)pow
的问题。用于计算正确值和速度的更好方法是使用每次循环迭代更新的乘数值。像这样:
pow
[我显示的是整数和浮点乘数,不是严格要求的,你在第二个循环中使用int imult = 1;
float fmult = 1.0f;
for(int i = number.length()-1; i >=0; i--)
{
valueInt += (number[i]-48) * imult;
imult *= 10;
valueFloat += (number[i]-48) * fmult;
fmult *= 10.0f;
}
得到的答案一样好 - 但无论哪种方式都变成浮点值,所以我以为我会显示两种变体]
对于imult
的有效范围和int
的有效范围,这应该会给出更准确的结果。当然,float
中的整数值在开始被切断之前被限制为23位,因为浮点格式缺乏可用的精度。因此,任何超过830万的价值都将缺少最重要的一点。