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&lt;T&gt;(-1) 是否足够好? (根据我猜的大小,我可以将其他类型视为无符号字符数组。)

【问题讨论】:

我认为语言标准并没有规定 2 的补码,所以也许使用 ~0 而不是 -1... 哦,已经有一个答案准确地说明(或多或少)什么我以为... 所以您也无权访问&lt;limits.h&gt;?因为否则,您可以很容易地自己实现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&lt;T&gt;(-1) 采用二进制补码,这是不可移植的。如果您只关心无符号类型,hvd's answer 是不错的选择。

工作示例:https://ideone.com/iV28u0

【讨论】:

二进制补码并没有到处使用是正确的,依赖于它的代码不可移植,但它在绝大多数系统中都有使用。 如果Tshort,那么~(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&lt;T&gt;(-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 的情况下生成全一位数据的正确方法吗?

使用 static_cast<> 对指针进行类型转换

C++ TBB concurrent_unordered_map find() at() return static_cast<size_t>( t ) * internal::hash_multip

右值引用、复制和移动

为啥使用 static_cast<int>(x) 而不是 (int)x?

哪个演员更快 static_cast<int> () 或 int()