hec*_*rot 4 c c++ serialization
在便携式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);
)
Run Code Online (Sandbox Code Playgroud)
有更好/更简单的方法吗?也许没有使用条件的方式?
编辑:谢谢你们.总而言之,我已经学到了什么......
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);
)
Run Code Online (Sandbox Code Playgroud)
Ste*_*non 12
1u << 8u
是0x100u
,它大于每个uint8_t
值,因此从不满足条件.您的"转换"例程实际上只是:
return x;
Run Code Online (Sandbox Code Playgroud)
这实际上是有道理的.
您需要更清楚地定义转换所需的内容.C99定义了从无符号到有符号整数类型的转换,如下所示(§6.3.1.3"有符号和无符号整数")
当具有整数类型的值转换为除了之外的另一个整数类型时
_Bool
,如果该值可以由新类型表示,则它将保持不变....
否则,新类型将被签名,并且值无法在其中表示; 结果是实现定义的,或者引发实现定义的信号.
因此,uint8_t
之间的值0
和127
被保留,而对于大于值的行为127
是未定义的. 许多(但不是全部)实现将简单地将无符号值解释为有符号整数的二进制补码表示.也许您真正要问的是如何保证跨平台的这种行为?
如果是这样,您可以使用:
return x < 128 ? x : x - 256;
Run Code Online (Sandbox Code Playgroud)
值为x - 256
a int
,保证将值x
解释为二进制补码8位整数.隐式转换int8_t
然后保留此值.
这一切都假设sint8_t
是int8_t
,而sint8_t
不是标准类型.如果不是,那么所有的赌注都是关闭的,因为我建议的转换的正确性取决于int8_t
具有二进制补码表示的保证(§7.18.1.1"精确宽度整数类型").
如果sint8_t
是一些古怪的平台特定类型,它可能使用一些其他表示,例如一个补码,它具有一组不同的可表示值,从而使某些输入的上述转换实现定义(因此是非可移植的).
编辑
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;
}
Run Code Online (Sandbox Code Playgroud)
使用-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
Run Code Online (Sandbox Code Playgroud)
请注意,优化后所有三种实现都是相同的.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
Run Code Online (Sandbox Code Playgroud)
当"通常"案件没有成本时,保护您的实施免受极端情况从来都不是"愚蠢".
对于这增加了不必要的复杂性的论点,我说:哪个更难 - 写一个注释来解释转换以及它为什么存在,或者你的继任者的实习生试图调试问题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;
}
Run Code Online (Sandbox Code Playgroud)
当一切都说完了,我同意这是模糊的,但我也认为我们应该尝试以面值回答这个问题.当然,更好的解决方案是C标准将有符号类型为二进制补码整数而没有填充(因此指定所有intN_t
类型的行为)时,将无符号转换为有符号的行为.
转换uint8_t
为int8_t
基本上颠倒了两个半范围的顺序."高"数字变得"低".这可以通过XOR完成.
x ^ 0x80
Run Code Online (Sandbox Code Playgroud)
但是,所有数字仍然是正数.那不好.我们需要引入正确的符号并恢复正确的幅度.
return ( x ^ 0x80 ) - 0x80;
Run Code Online (Sandbox Code Playgroud)
你去!
假设类型sint8_t
和uint8_t
赋值兼容,这是可行的
sint8_t DESER_SINT8(uint8_t x) { return x; }
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
4486 次 |
最近记录: |