初看起来似乎很简单,但我找不到任何有关此案例的好描述。
我有一个返回64位值的方法。该值使用long double
值在内部计算。在方法结束时,我想检查long double
是否在long long
值的范围内,否则只需指定最大long long
值。
我使用以下代码,它只检查正范围,因为没有负面结果:
long long calculateSomething()
{
long double calculatedValue = ...;
long long result;
if (calculatedValue > static_cast<long double>(std::numeric_limits<long long>::max())) {
result = std::numeric_limits<long long>::max();
} else {
result = static_cast<long long>(std::floor(calculatedValue));
}
return result;
}
现在我想知道,long double
可以等于double
。转换static_cast<long double>(std::numeric_limits<long long>::max())
始终可以正常运行吗?
还是有另一种更好的检查范围的方法吗?
答案 0 :(得分:0)
浮点和整数类型之间的转换在C ++标准的§4.9[conv.fpint]中指定:
1浮点类型的prvalue可以转换为prvalue 整数类型。转换截断;也就是说,分数 部分被丢弃。如果截断值,则行为未定义 无法在目标类型中表示。 [注意:如果是 目的地类型为
bool
,见4.12。 - 结束记录]2整数类型或无范围枚举类型的prvalue可以 转换为浮点类型的prvalue。结果是 如果可能的话确切如果转换的值在范围内 可以表示的值,但无法表示该值 确切地说,它是下一个实现定义的选择 更低或更高的可表示价值。 [注意:精度损失 如果积分值不能完全表示为值,则会发生 浮动类型。 - 结束注释]如果要转换的值是 在可以表示的值范围之外,行为是 未定义。如果源类型为
bool
,则值为false
转换为零,值true
转换为一。
典型的long long
是64位,因此std::numeric_limits<long long>::max()
是2 63 -1。这远小于LDBL_MAX
的最小可能值,即1E+37
。因此,我们安全地处于long double
的可表示范围内。但是,如果long double
是64位,则2 63 -1极不可能完全表示,并且您遇到了麻烦,因为标准说结果是“实现定义的”选择下一个较低或较高的可表示值“。换句话说,它可以是任何一种方式,并且你有问题。
如果编译器为转换选择了下一个较低的可表示值,那么一切都很好。即使calculatedValue == static_cast<long double>(std::numeric_limits<long long>::max())
,它仍然在long long
的可表示范围内,并且转换定义明确。
如果编译器为转换选择了下一个更高的可表示值(并且舍入到最近,使用的典型舍入,可能会这样,因为2 63 是完全可表示的),并且{ {1}},然后calculatedValue == static_cast<long double>(std::numeric_limits<long long>::max())
实际上超出calculatedValue
的可表示范围,但在您的代码中,您仍尝试将其转换为long long
。因此,根据上面的第一段,您有未定义的行为。哎哟。
最简单的解决方法是测试long long
而不是calculatedValue >= static_cast<long double>(std::numeric_limits<long long>::max())
。万一编译器向下舍入,你会错过一个案例。另一个可能的解决方法是测试calculatedValue > static_cast<long double>(std::numeric_limits<long long>::max())
,利用合理calculatedValue >= static_cast<long double>(std::numeric_limits<long long>::max() + 1ULL)
的2 n 在浮点中可以准确表示的事实。