gpt4 book ai didi

c++ - 如何在避免未定义行为的同时将任意 double 转换为整数?

转载 作者:IT老高 更新时间:2023-10-28 23:02:54 27 4
gpt4 key购买 nike

假设我有一个接受 64 位整数的函数,我想调用它与 double具有任意数值(即它可能非常大量级,甚至无限):

void DoSomething(int64_t x);

double d = [...];
DoSomething(d);

C++11 标准中 [conv.fpint] 的第 1 段是这样说的:

A prvalue of a floating point type can be converted to a prvalue of an integer type. The conversion trun- cates; that is, the fractional part is discarded. The behavior is undefined if the truncated value cannot be represented in the destination type.

因此d的值有很多以上将导致未定义行为。我想转换为饱和,使值大于 std::numeric_limits<int64_t>::max() (以下称为 kint64max),包括无穷大,成为那个值,同样用最小可表示值(value)。这似乎是自然的方法:

double clamped = std::min(d, static_cast<double>(kint64max));
clamped = std::max(clamped, static_cast<double>(kint64min));
DoSomething(clamped);

但是,标准中的下一段是这样说的:

A prvalue of an integer type or of an unscoped enumeration type can be converted to a prvalue of a floating point type. The result is exact if possible. If the value being converted is in the range of values that can be represented but the value cannot be represented exactly, it is an implementation-defined choice of either the next lower or higher representable value.

所以 clamped最终可能仍然是 kint64max + 1 ,并且行为可能仍然是未定义。

什么是最简单的便携方式来做我正在寻找的事情?积分如果它还能优雅地处理 NaN秒。

更新:更准确地说,我希望以下所有内容都适用于 int64_t SafeCast(double)解决这个问题的函数:

  1. 对于任何 double d , 调用SafeCast(d)不执行未定义的行为根据标准,也不会抛出异常或以其他方式中止。

  2. 对于任何 double d[-2^63, 2^63) 范围内, SafeCast(d) == static_cast<int64_t>(d) .也就是说,SafeCast同意 C++ 的定义后者的任何转换规则。

  3. 对于任何 double d >= 2^63 , SafeCast(d) == kint64max .

  4. 对于任何 double d < -2^63 , SafeCast(d) == kint64min .

我怀疑这里真正的困难在于弄清楚 d在里面范围 [-2^63, 2^63) .正如问题和其他评论中所讨论的答案,我认为使用 kint64maxdouble测试鞋面由于未定义的行为,bound 是非首发。可能更有希望使用 std::pow(2, 63) ,但我不知道这是否能保证完全正确2^63。

最佳答案

事实证明,这比我想象的要简单。感谢迈克尔·奥莱利了解此解决方案的基本思想。

问题的核心是弄清楚截断的 double 是否会可表示为 int64_t。您可以使用 std::frexp 轻松完成此操作:

#include <cmath>
#include <limits>

static constexpr int64_t kint64min = std::numeric_limits<int64_t>::min();
static constexpr int64_t kint64max = std::numeric_limits<int64_t>::max();

int64_t SafeCast(double d) {
// We must special-case NaN, for which the logic below doesn't work.
if (std::isnan(d)) {
return 0;
}

// Find that exponent exp such that
// d == x * 2^exp
// for some x with abs(x) in [0.5, 1.0). Note that this implies that the
// magnitude of d is strictly less than 2^exp.
//
// If d is infinite, the call to std::frexp is legal but the contents of exp
// are unspecified.
int exp;
std::frexp(d, &exp);

// If the magnitude of d is strictly less than 2^63, the truncated version
// of d is guaranteed to be representable. The only representable integer
// for which this is not the case is kint64min, but it is covered by the
// logic below.
if (std::isfinite(d) && exp <= 63) {
return d;
}

// Handle infinities and finite numbers with magnitude >= 2^63.
return std::signbit(d) ? kint64min : kint64max;
}

关于c++ - 如何在避免未定义行为的同时将任意 double 转换为整数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25857843/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com