在便携式C中将“uint8_t”转换为“sint8_t”的最佳方法是什么?
那是我想出的代码...。
#include <stdint.h>
sint8_t DESER_SINT8(uint8_t x)
(
return
(sint8_t)((x >= (1u << 8u))
? -(UINT8_MAX - x)
: x);
)
有没有更好/更简单的方法来做到这一点?也许没有使用条件的方法?
编辑:谢谢大家。综上所述,我已经学到了...
sint8_t
实际上称为int8_t
128
由1 << 7
而不是1 << 8
表示:)
所以这是我原始代码的更新版本:
#include <stdint.h>
int8_t DESER_INT8(uint8_t x)
(
return ((x >= (1 << 7))
? -(UINT8_MAX - x + 1)
: x);
)
最佳答案
1u << 8u
是0x100u
,它大于每个uint8_t
值,因此永远不会满足条件。您的“转换”例程实际上是:
return x;
这实际上是有道理的。
您需要更清楚地定义要转换的内容。 C99定义了从无符号到有符号整数类型的转换,如下所示(§6.3.1.3“有符号和无符号整数” )
因此,保留了
uint8_t
和0
之间的127
值,并且未定义大于127
的值的行为。许多(但不是全部)实现将简单地将无符号值解释为有符号整数的二进制补码表示。也许您真正要问的是如何确保跨平台的这种行为?如果是这样,您可以使用:
return x < 128 ? x : x - 256;
值
x - 256
是int
,保证将x
的值解释为二进制补码的8位整数。然后,隐式转换为int8_t
会保留此值。所有这些都假定
sint8_t
是int8_t
,因为sint8_t
不是标准类型。如果不是,则所有赌注都无效,因为我建议的转换的正确性取决于对int8_t
具有二进制补码表示形式的保证(§7.18.1.1“精确宽度整数类型” )。如果
sint8_t
相反是某种怪异的平台特定类型,则它可能会使用其他表示形式(例如ones-complement),该表示形式具有一组不同的可表示值,从而为某些输入呈现了上述实现定义的转换(因此不可移植)。编辑
Alf认为这是“愚蠢的”,并且在任何生产系统上都没有必要。我不同意,但可以肯定的是,这是一个极端案例。他的论点并非完全没有道理。
他声称这是“无效的”,因此应避免,但这是毫无根据的。一个合理的优化编译器将在不需要它的平台上优化它。例如,在x86_64上使用GCC:
#include <stdint.h>
int8_t alf(uint8_t x) {
return x;
}
int8_t steve(uint8_t x) {
return x < 128 ? x : x - 256;
}
int8_t david(uint8_t x) {
return (x ^ 0x80) - 0x80;
}
用-Os -fomit-frame-pointer编译会产生以下结果:
_alf:
0000000000000000 movsbl %dil,%eax
0000000000000004 ret
_steve:
0000000000000005 movsbl %dil,%eax
0000000000000009 ret
_david:
000000000000000a movsbl %dil,%eax
000000000000000e ret
请注意,优化后所有这三种实现都是相同的。 Clang/LLVM给出完全相同的结果。同样,如果我们为ARM而不是x86进行构建:
_alf:
00000000 b240 sxtb r0, r0
00000002 4770 bx lr
_steve:
00000004 b240 sxtb r0, r0
00000006 4770 bx lr
_david:
00000008 b240 sxtb r0, r0
0000000a 4770 bx lr
在“正常”情况下不花钱的情况下,保护实现免受极端情况的侵害永远不会“愚蠢”。
关于这会增加不必要的复杂性的说法,我说:这更难-写一条注释来解释转换及其原因,或者您的继任者实习生将在10年后尝试调试问题,因为新的编译器打破了幸运之门。您一直默默依赖于这种情况的偶然性?以下内容真的很难维护吗?
// The C99 standard does not guarantee the behavior of conversion
// from uint8_t to int8_t when the value to be converted is larger
// than 127. This function implements a conversion that is
// guaranteed to wrap as though the unsigned value were simply
// reinterpreted as a twos-complement value. With most compilers
// on most systems, it will be optimized away entirely.
int8_t safeConvert(uint8_t x) {
return x < 128 ? x : x - 256;
}
说完所有的话,我同意这是模糊的,但我也认为我们应该从表面上回答这个问题。当然,更好的解决方案是,对于C标准,当有符号类型是二进制补码整数而无需填充时,可以限制从无符号到有符号的转换行为(从而指定所有
intN_t
类型的行为)。关于c++ - uint8_t到sint8_t的转换,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3892924/