static_cast<T>(-1) 是在没有 numeric_limits 的情况下生成全一位数据的正确方法吗?
Posted
技术标签:
【中文标题】static_cast<T>(-1) 是在没有 numeric_limits 的情况下生成全一位数据的正确方法吗?【英文标题】:Is static_cast<T>(-1) the right way to generate all-one-bits data without numeric_limits? 【发布时间】:2016-08-09 02:54:44 【问题描述】:我在无法访问 C++ 标准库的环境中编写 C++ 代码,特别是无法访问 std::numeric_limits
。假设我要实现
template <typename T> constexpr T all_ones( /* ... */ )
专注于无符号整数类型,我该放什么?具体来说,static_cast<T>(-1)
是否足够好? (根据我猜的大小,我可以将其他类型视为无符号字符数组。)
【问题讨论】:
我认为语言标准并没有规定 2 的补码,所以也许使用~0
而不是 -1
... 哦,已经有一个答案准确地说明(或多或少)什么我以为...
所以您也无权访问<limits.h>
?因为否则,您可以很容易地自己实现std::numeric_limits
之类的东西。
@ChristianHackl:对此不确定。但是,有了limits.h,我至少可以对所有数字类型进行明确的特化。
Is it safe to use -1 to set all bits to true?的可能重复
@DavidStone:在这个问题中,我们有:1. 模板多态性和 2. 非整数类型作为问题的次要部分,所以我认为这不是一个骗局。
【参考方案1】:
在0
上使用bitwise NOT
运算符~
。
T allOnes = ~(T)0;
static_cast<T>(-1)
采用二进制补码,这是不可移植的。如果您只关心无符号类型,hvd's answer 是不错的选择。
工作示例:https://ideone.com/iV28u0
【讨论】:
二进制补码并没有到处使用是正确的,依赖于它的代码不可移植,但它在绝大多数系统中都有使用。 如果T
是short
,那么~(short)0
首先将(short)0
提升回int。然后,您最终会得到一个可能超出short
范围的值,从而导致转换的实现定义的结果不必全是位。 (这对于二进制补码是不可能的,但您的答案专门试图解决对其他系统的可移植性。)
@hvd 为什么要宣传短片?全局 ~ 操作符是否不能保证有一个简短的实现?
@Andreas 好问题。这是因为继承自 C 的整体提升。C 由 dmr 设计为便于编译器编写者使用,因此所有可以表示为 int
的东西都被提升为 int,如果它不能表示为signed int
,它被提升为unsigned int
。
这种方法的另一个优点是它富有表现力。你想要全1,这与全零相反。阅读它的人知道它的作用。 “减一二补”方法的表达力不强,依赖于实现知识。【参考方案2】:
专注于无符号整数类型,我该放什么?具体来说,static_cast(-1) 是否足够好
如果您只关心无符号类型,是的,转换 -1
对于所有标准 C++ 实现都是正确的。对无符号类型的操作,包括有符号类型到无符号类型的转换,保证以模数 (max+1) 工作。
【讨论】:
“所有标准 C++ 实现” - 谈论一个令人困惑的标签。这是由“标准”指定的,还是像其他答案声称的那样“实现”定义? @underscore_d C++ 标准确实很好地定义了它。无符号类型需要完美溢出。它不适用于有符号类型,有符号溢出是未定义的。我的答案是实现定义或两者之一,但可以假设任何理智的实现都可以按照您的期望进行。 @underscore_d 我的方案不依赖它,hvd的方案也不依赖。 @Leandros 是的,nvm,模数是这里的关键。 @underscore_d 没错。 ;)【参考方案3】:这种令人放松的直接方式。
T allOnes;
memset(&allOnes, ~0, sizeof(T));
【讨论】:
@Leandros 和 Omni,如何/何时出现别名问题? @Andreas 考虑过,从来没有。 这仅对 8 位字节有效。 我想初始化一个 constexpr 表达式,而不是在运行时设置值。 @einpoklum, 要求至少 8。虽然8位字节在业界已经占主导地位,但其他尺寸还是可以满足的;至少,非 8 位字节更有可能满足非 2 的补码负数。不过,这个问题很容易解决,只需使用~0
而不是0xFF
。【参考方案4】:
专注于无符号整数类型,我该放什么? 具体来说,static_cast(-1) 是否足够好
是的,这已经足够了。
但我更喜欢十六进制值,因为我的背景是嵌入式系统,而且我一直必须知道 sizeof(T)。
即使在桌面系统中,我们也知道以下 T 的大小:
uint8_t allones8 = 0xff;
uint16_t allones16 = 0xffff;
uint32_t allones32 = 0xffffffff;
uint64_t allones64 = 0xffffffffffffffff;
【讨论】:
【参考方案5】:另一种方法是
static_cast<T>(-1ull)
无论 1 的补码、2 的补码或符号幅度如何,这会更正确,并且可以在任何有符号整数格式下工作。你也可以使用static_cast<T>(-UINTMAX_C(1))
因为unary minus of an unsigned value被定义为
无符号量的负数是通过从 2^n 中减去它的值来计算的,其中 n 是提升的操作数中的位数。"
因此-1u
将始终在unsigned int
中返回全一位数据。 ll
后缀是为了使它适用于比unsigned long long
更窄的任何类型。 C++ 中还没有extended integer types,所以应该没问题
然而,表达意图更清晰的解决方案是
static_cast<T>(~0ull)
【讨论】:
字符数相同的解决方案如何“更简洁”?以上是关于static_cast<T>(-1) 是在没有 numeric_limits 的情况下生成全一位数据的正确方法吗?的主要内容,如果未能解决你的问题,请参考以下文章
static_cast<T>(-1) 是在没有 numeric_limits 的情况下生成全一位数据的正确方法吗?
C++ TBB concurrent_unordered_map find() at() return static_cast<size_t>( t ) * internal::hash_multip