如何将size_t转换为double或int C++

76
我的问题是:
我有一个 size_t 类型的数据,现在我想将它转换成 double 或 int 类型。
如果我像这样做:
 size_t data = 99999999;
 int convertdata = data;

编译器会报警告,因为它可能会溢出。

你是否有像Boost或其他方法可以进行转换的方法?

5个回答

100

一个演员阵容,就像Blaz Bratanic建议的那样:

size_t data = 99999999;
int convertdata = static_cast<int>(data);

虽然原则上编译器可以警告任何内容(即使有强制类型转换),但很可能会消除该警告。

但这并没有解决警告所提醒的问题,即从size_tint的转换确实可能会溢出。

如果可能的话,请设计您的程序,使您不需要将size_t值转换为int。只需将其存储在size_t变量中(如您已经完成的那样)并使用它。

转换为double不会导致溢出,但对于非常大的size_t值,可能会导致精度损失。同样,将size_t转换为double没有太多意义;最好仍将值保留在size_t变量中。

(如果无法避免强制类型转换,则R Sahu的回答提供了一些建议,例如在溢出时抛出异常。)


最近我遇到了一个问题,一个返回double类型的函数在失败时应该返回npos,但是当我将函数的结果与npos进行(==)比较时,它没有在应该返回true的情况下返回。 在控制台上检查后,我发现将npos强制转换为double类型后,其值比未转换的npos(size_t类型)高1个整数。 - Coyoteazul
@Coyoteazul:这是一个设计不良的函数。更糟糕的是,它很可能在32位系统上“工作”。 - Keith Thompson
是的。我实际上认为double比size_t有更大的容量,所以我没有想到将npos强制转换会有问题。我进行了注释,以防有人遇到相同的问题。 - Coyoteazul
@Coyoteazul:在许多现代系统中,doublesize_t都是64位的。对于size_t,它们都是值位。double将一位用于符号,几位用于指数。double可以轻松地容纳一个具有nposSIZE_MAX)大小的数字,但它无法存储其完整精度。 - Keith Thompson

27
如果您的代码已准备好处理溢出错误,可以在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);

1
有用的回复。我提交了一个编辑,将这个内容添加到已接受的答案的末尾,以便未来的读者可以在一个地方看到完整的响应。 - srm

20

静态转换:

static_cast<int>(data);

1
由于size_t范围比int范围大,是否可能导致溢出? - user2701639
1
@user2701639 嗯,你希望在那种情况下发生什么? - David Heffernan
@user2701639 能否通过double类型引起溢出? 不可能。 - πάντα ῥεῖ
2
是的,如果data大于适合int的值,这将导致溢出 - 因此可能会引起“有趣”的问题,例如在convertdata中出现负值,当您认为大小为负时,您知道某些事情非常错误。此外,data的选定值可能会导致convertdata中的值为零,其他值可能表示大小为非常小的正数,尽管原始大小很大。 - Mats Petersson

13

你可以使用Boost numeric_cast

如果源值超出目标类型的范围,这将抛出异常,但它不会检测转换为double时的精度损失。

无论使用哪个函数,你都应该决定在size_t中的值大于 INT_MAX的情况下想要发生什么。如果你想要检测这种情况,则使用numeric_cast或编写自己的代码进行检查。如果你某种方式知道它不可能发生,则可以使用static_cast来禁止运行时检查而不会产生成本,但在大多数情况下成本并不重要。


1
如果您无法避免这种类型转换,但希望程序在发生out_of_range时通知您,则需要这样做。 - Arkady

4

假设程序无法重新设计以避免转换(参见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)之间的双精度数,每隔四个整数就可以被精确表示,以此类推。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接