C++ 为什么boost::lexical_cast即使转换了值也会抛出异常?
我手上有一个令人费解的错误。我确信这段代码在早期版本的boost中运行良好,现在(boost 1.72.0)它抛出了一个异常:C++ 为什么boost::lexical_cast即使转换了值也会抛出异常?,c++,boost,C++,Boost,我手上有一个令人费解的错误。我确信这段代码在早期版本的boost中运行良好,现在(boost 1.72.0)它抛出了一个异常: string problemStr = "1.03964e-312"; double problemChild = boost::lexical_cast<double>(problemStr); boost::wrapexcept<boost::bad_lexical_cast>: bad lexical cast:
string problemStr = "1.03964e-312";
double problemChild = boost::lexical_cast<double>(problemStr);
boost::wrapexcept<boost::bad_lexical_cast>: bad lexical cast: source type value could not be interpreted as target
我很困惑。它似乎进行了转换,但仍然抛出异常?我忽略了什么?或者这实际上是一个bug?这让人困惑
一开始无法重新编译:-您可以在那里更改编译器版本和增强版本
然而,将编译器更改为clang成功了:(即使使用boost 1.73)
事情变得更奇怪:在我的盒子上,即使使用asan/ubsan,叮当++-9也可以
所以我开始安装一些docker发行版
事实证明,当使用clagn++-stdlib=libc++
时,事情会破裂
结论
经过长时间的调试器和标准库实现,它并没有那么复杂。下面是一些细节:
#include <sstream>
#include <cassert>
#include <iostream>
int main() {
double v;
std::cout << std::numeric_limits<double>::min_exponent10 << std::endl;
std::cout << std::numeric_limits<double>::max_exponent10 << std::endl;
assert(std::istringstream("1e308") >> v);
assert(std::istringstream("1.03964e-312") >> v); // line 10
assert(std::istringstream("1e309") >> v); // line 11
}
关于libc++:
-307
308
sotest: /home/sehe/Projects/stackoverflow/test.cpp:10: int main(): Assertion `std::istringstream("1.03964e-312") >> v' failed.
总之,libstdc++在某些情况下允许:
指数的11位宽度允许表示10到10之间的数字−308和10308,具有完整的15–17位小数精度。通过降低精度,低于正常值的表示允许更小的值,最高可达约5×10−324
图书馆可能会进行一些检查,以确定精度损失是否可以接受,但也可能完全由您自己判断
建议
如果您需要这种范围,我建议您使用多精度库(GMP、MPFR或Boost)
对于十进制输入格式的完全保真度,考虑例如CPPPyDEXFLIFT:
#include <boost/multiprecision/cpp_dec_float.hpp>
using Decimal = boost::multiprecision::cpp_dec_float_50;
int main() {
Decimal v("1.03964e-312");
std::cout << v << std::endl;
}
我在macOS High Sierra(10.13.6)。非常感谢您提供的详尽答案。我发现wandbox工具非常有用,也谢谢你。我确实在使用Clang10.0.0(我没有提到这一点)。关于您使用十进制的建议:一个好主意。不幸的是,这并不是我的选择。我的代码大量使用模板,应该可以处理各种数字类型。现在,我正在通过将这些值设置为0来解决这个问题。毫无疑问,它们本身就是舍入误差的结果,我将追查其原因,并尝试在那里消除该问题。“各种数字类型”都有效!但是,我建议您这样做,因为它们确实倾向于破坏(“意外”)通用代码。事实上,听起来准确度并不是一个真正的目标,因为你的输入已经不准确了,所以使用双打可能对你有好处。如果你发现你得到了像这样的“边界条件输入”,并且希望能够更好地诊断它们,你可以混合/匹配。
-307
308
sotest: /home/sehe/Projects/stackoverflow/test.cpp:10: int main(): Assertion `std::istringstream("1.03964e-312") >> v' failed.
#include <boost/multiprecision/cpp_dec_float.hpp>
using Decimal = boost::multiprecision::cpp_dec_float_50;
int main() {
Decimal v("1.03964e-312");
std::cout << v << std::endl;
}
1.03964e-312