Vol*_*maz 0 c++ floating-point
我有一个介于0和1之间的浮点值.我需要将其转换为-120到80.为此,首先我在120减去后乘以200.当减去时,我有舍入错误.让我们看看我的榜样.
float val = 0.6050f;
val *= 200.f;
Run Code Online (Sandbox Code Playgroud)
现在val是我预期的121.0.
val -= 120.0f;
Run Code Online (Sandbox Code Playgroud)
现在val是0.99999992
我想也许我可以通过乘法和除法来避免这个问题.
float val = 0.6050f;
val *= 200.f;
val *= 100.f;
val -= 12000.0f;
val /= 100.f;
Run Code Online (Sandbox Code Playgroud)
但它没有帮助.我手上还有0.99.
有解决方案吗?
编辑:经过详细的日志记录,我知道这部分代码没有问题.在我的日志显示"0.605"之前,我有详细的日志后,我看到"0.60499995946884155273437500000000000000000000000000"问题出在不同的地方.
编辑2:我想我发现了内疚.初始值为0.5750.
std::string floatToStr(double d)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(15) << d;
return ss.str();
}
int main()
{
float val88 = 0.57500000000f;
std::cout << floatToStr(val88) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
结果是0.574999988079071
实际上我每次都需要从这个值中加上和减去0.0025.通常我预计0.575,0.57575,0.5800,0.52525 ....
编辑3:其实我用双倍尝试了所有这些.这是我的榜样.
std::string doubleToStr(double d)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(15) << d;
return ss.str();
}
int main()
{
double val88 = 0.575;
std::cout << doubleToStr(val88) << std::endl;
val88 += 0.0025;
std::cout << doubleToStr(val88) << std::endl;
val88 += 0.0025;
std::cout << doubleToStr(val88) << std::endl;
val88 += 0.0025;
std::cout << doubleToStr(val88) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
结果是:
0.575000000000000
0.577500000000000
0.580000000000000
0.582500000000000
Run Code Online (Sandbox Code Playgroud)
但不幸的是,我一定要漂浮.我需要改变很多东西.
感谢大家的帮助.
Edit4:我找到了带字符串的解决方案.我使用ostringstream的舍入并在之后转换为double.我可以有4个精确的正确数字.
std::string doubleToStr(double d, int precision)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(precision) << d;
return ss.str();
}
double val945 = (double)0.575f;
std::cout << doubleToStr(val945, 4) << std::endl;
std::cout << doubleToStr(val945, 15) << std::endl;
std::cout << atof(doubleToStr(val945, 4).c_str()) << std::endl;
Run Code Online (Sandbox Code Playgroud)
结果是:
0.5750
0.574999988079071
0.575
Run Code Online (Sandbox Code Playgroud)
让我们假设您的编译器完全实现IEEE 754 binary32和binary64 float
以及double
值和操作.
首先,你必须明白,0.6050f
这并不代表数学量6050/10000.确切地说0.605000019073486328125
,它是最接近的float
.即使你从那里写出完美的计算,你必须记住这些计算从0.605000019073486328125开始,而不是从0.6050开始.
其次,通过计算double
和转换到float
最后,您几乎可以解决所有累积的舍入问题:
$ cat t.c
#include <stdio.h>
int main(){
printf("0.6050f is %.53f\n", 0.6050f);
printf("%.53f\n", (float)((double)0.605f * 200. - 120.));
}
$ gcc t.c && ./a.out
0.6050f is 0.60500001907348632812500000000000000000000000000000000
1.00000381469726562500000000000000000000000000000000000
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,所有计算和中间值都是双精度的.
1.0000038…
如果你记得你从0.605000019073486328125而不是0.6050(它不存在float
)开始,这是一个非常好的答案.