我有一个 size_t 类型的数据,现在我想将它转换成 double 或 int 类型。
如果我像这样做:
size_t data = 99999999;
int convertdata = data;
编译器会报警告,因为它可能会溢出。
你是否有像Boost或其他方法可以进行转换的方法?
一个演员阵容,就像Blaz Bratanic建议的那样:
size_t data = 99999999;
int convertdata = static_cast<int>(data);
虽然原则上编译器可以警告任何内容(即使有强制类型转换),但很可能会消除该警告。
但这并没有解决警告所提醒的问题,即从size_t
到int
的转换确实可能会溢出。
如果可能的话,请设计您的程序,使您不需要将size_t
值转换为int
。只需将其存储在size_t
变量中(如您已经完成的那样)并使用它。
转换为double
不会导致溢出,但对于非常大的size_t
值,可能会导致精度损失。同样,将size_t
转换为double
没有太多意义;最好仍将值保留在size_t
变量中。
(如果无法避免强制类型转换,则R Sahu的回答提供了一些建议,例如在溢出时抛出异常。)
data
过大时抛出异常。size_t data = 99999999;
if ( data > INT_MAX )
{
throw std::overflow_error("data is larger than INT_MAX");
}
int convertData = static_cast<int>(data);
静态转换:
static_cast<int>(data);
double
类型引起溢出? 不可能。 - πάντα ῥεῖdata
大于适合int
的值,这将导致溢出 - 因此可能会引起“有趣”的问题,例如在convertdata
中出现负值,当您认为大小为负时,您知道某些事情非常错误。此外,data
的选定值可能会导致convertdata
中的值为零,其他值可能表示大小为非常小的正数,尽管原始大小很大。 - Mats Petersson你可以使用Boost numeric_cast
。
如果源值超出目标类型的范围,这将抛出异常,但它不会检测转换为double
时的精度损失。
无论使用哪个函数,你都应该决定在size_t
中的值大于 INT_MAX
的情况下想要发生什么。如果你想要检测这种情况,则使用numeric_cast
或编写自己的代码进行检查。如果你某种方式知道它不可能发生,则可以使用static_cast
来禁止运行时检查而不会产生成本,但在大多数情况下成本并不重要。
假设程序无法重新设计以避免转换(参见Keith Thomson的答案):
要将size_t转换为int,您需要确保size_t不超过int的最大值。这可以使用std::numeric_limits来实现:
int SizeTToInt(size_t data)
{
if (data > std::numeric_limits<int>::max())
throw std::exception("Invalid cast.");
return std::static_cast<int>(data);
}
如果您需要从 size_t 转换为 double,并且需要确保不丢失精度,我认为您可以使用窄转换(参见 Stroustrup:C++程序设计语言第四版):
template<class Target, class Source>
Target NarrowCast(Source v)
{
auto r = static_cast<Target>(v);
if (static_cast<Source>(r) != v)
throw RuntimeError("Narrow cast failed.");
return r;
}
我通过检查浮点数可表示的最大整数的极限来测试使用窄转换进行 size_t 到 double 转换(代码使用 googletest):
EXPECT_EQ(static_cast<size_t>(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() - 2 })), size_t{ IntegerRepresentableBoundary() - 2 });
EXPECT_EQ(static_cast<size_t>(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() - 1 })), size_t{ IntegerRepresentableBoundary() - 1 });
EXPECT_EQ(static_cast<size_t>(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() })), size_t{ IntegerRepresentableBoundary() });
EXPECT_THROW(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() + 1 }), std::exception);
EXPECT_EQ(static_cast<size_t>(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() + 2 })), size_t{ IntegerRepresentableBoundary() + 2 });
EXPECT_THROW(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() + 3 }), std::exception);
EXPECT_EQ(static_cast<size_t>(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() + 4 })), size_t{ IntegerRepresentableBoundary() + 4 });
EXPECT_THROW(NarrowCast<double>(size_t{ IntegerRepresentableBoundary() + 5 }), std::exception);
where
constexpr size_t IntegerRepresentableBoundary()
{
static_assert(std::numeric_limits<double>::radix == 2, "Method only valid for binary floating point format.");
return size_t{2} << (std::numeric_limits<double>::digits - 1);
}
也就是说,如果N是尾数中的数字个数,那么对于小于或等于2^N的双精度数,整数可以被精确表示。对于在2^N和2^(N+1)之间的双精度数,每隔一个整数就可以被精确表示。对于在2^(N+1)和2^(N+2)之间的双精度数,每隔四个整数就可以被精确表示,以此类推。
double
和size_t
都是64位的。对于size_t
,它们都是值位。double
将一位用于符号,几位用于指数。double
可以轻松地容纳一个具有npos
(SIZE_MAX
)大小的数字,但它无法存储其完整精度。 - Keith Thompson