类型结果之间的意外差异
unexpected difference between types results
#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 类型在那里是错误的?
这不是一致的行为。即使在某些平台上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彼得森提到的,你可以使用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);
}
尽管我无法重新生成您在 gcc 4.9.2 中看到的内容,但我在循环中添加了一些 printfs,如下所示:
printf("%dn", pow(10, i));
printf("%fn", 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("%dn", pow(10, i));
^
gsamaras@pythagoras:~$ ./a.out
1076101120
100.000000
1076101120
10.000000
1076101120
1.000000
Int value: 951
Float value: 951
我怀疑您看到此行为是因为 pow(),它具有此签名(最接近 int
):
浮点数 POW(浮点基数,浮点指数);
因此,pow()
返回一个float
,然后被转换为int
,这可能会导致一些精度的损失。
问题是pow
不是将值计算为整数值(也许pow(x, y)
exp(ln(x)*y)
计算 - 但它不必完全是这样),而是作为浮点值,并且它会有一个小的舍入误差。这可能是"低于"或"高于"正确的值,并且由于从 float
到 int
的转换是通过截断进行的,因此950.999996
被转换为 950
而不是 951
。在这种情况下,您可以通过将 pow
的结果舍入到最接近的值而不是截断 [假设结果与无穷精确值相差小于 +/-0.5] 来解决这个问题。
浮点值的计算方式始终具有一定程度的精度。整数计算总是精确到计算本身的最小整数值,但 C 和 C++ 的规则是,如果一侧是浮点数,则计算在浮点中执行。
我将完全消除代码中使用(通常很麻烦的)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
的有效范围,这应该给出更准确的结果。当然,float
中的整数值在开始被斩波之前被限制为 23 位,因为缺乏浮点格式的精度。因此,任何高于 830 万左右的值都将缺少最低有效位。
- 尝试将字符串/字符转换为整数会产生意外结果
- RapidXML - 代码创建意外结果
- 类中静态函数C++意外结果
- 指针数组中的意外结果
- 使用指针访问数组元素时出现意外结果
- 使用 sprintf 和 %g 将双精度转换为字符串的意外结果
- C++:比较运算符>和字符串文本的意外结果
- 具有意外结果的 C++ 闭包
- yaml-cpp 比较的意外结果
- 每次都出现意外结果
- 在 Qt 中解析嵌套的 JSON 时出现意外结果(数组不存在)
- 如何避免 std::abs 的意外结果?
- 使用嵌套 if 语句的意外结果
- A ^= B ^= A ^= B;C# Visual Studio 中的意外结果
- 逻辑错误,我将获得意外结果
- 在 c++ 中使用异步的意外结果
- 从函数的返回值将元素C++存储到 std::vector 中时出现意外结果
- 执行递增和递减时"cout"链接会产生意外结果
- OpenCL - 内核方法返回意外结果
- C++使用 std::get_time 解析 YYMMDD ISO 8601 日期字符串会得到意外结果