C++ 将uint8_t转换为sint8_t

C++ 将uint8_t转换为sint8_t,c++,c,serialization,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); ) 假设类型sint8\u t和uint8\u t是分配兼容的,这是可

在便携式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\u t
uint8\u t
是分配兼容的,这是可行的

sint8_t DESER_SINT8(uint8_t x) { return x; }

1uUhm,。。。我想你是想返回x,如果x可以用sint8表示,或者abs(sint8_MAX-x),如果不能,对吗

在这种情况下,这里有一个可行的方法(我认为你的方法有一个小错误):


#定义高位(X)((X)和(1将
uint8_t
转换为
int8_t
基本上颠倒了两个半范围的顺序。“高”数字变为“低”。这可以通过异或实现

x ^ 0x80
然而,所有的数字仍然是正的。这不好。我们需要引入适当的符号并恢复适当的大小

return ( x ^ 0x80 ) - 0x80;

好了!

假设您的
sint8\u t
实际上是
int8\u t
来自
,那么它保证是2的补码形式,并且保证没有填充位

进一步假设您希望反向(隐式)转换工作并产生原始值

然后,给定一个
uint8\u t
类型的
v
,您所要做的就是

    int8_t( v )
就这样

C标准不保证这种转换,只保证相反的转换。但是,没有已知的系统或编译器不能工作(假设您有这些类型可用)

忘记所有手动的位篡改。或者,为了测试您是否做得对,只需将值赋给
uint8\u t
即可进行转换,并检查您是否在所有情况下都得到了原始值。特别是,您使用的公式产生-((2^n-1)-x)=1+x-2^n,而正确的值保留转换是x-2^n

干杯


–Alf

如果你想避开分支机构,你可以做一些疯狂的事情,比如:

int selector= 127 - x; // 0 or positive if x <=127, negative otherwise
int selector>>= 8; // arithmetic rotate to get -1 or 0
int wrapped_value= x - 256;

return (x&~selector)|(wrapped_value&selector); // if selector is 0, use x, otherwise, use the wrapped value.
int selector=127-x;//如果x>=8,则为0或正数;//通过算术旋转得到-1或0
int-u值=x-256;
return(x&~selector)|(wrapped_value&selector);//如果选择器为0,则使用x,否则使用wrapped值。

我不知道这是否有实际价值,但我想到了一种不同的方法:

uint8_t input;
int8_t output;
*(uint8_t *)&output = input;
请注意:

  • int8\u t
    必须是两个补码
  • 相应的有符号类型和无符号类型的范围重叠部分需要具有相同的表示形式,以便可以通过任一类型的指针访问在有符号类型和无符号类型范围内的值
  • 这只剩下一个位,它必须是两个补符号位
我能看出此推理可能无效的唯一方法是,如果
CHAR\u BIT>8
和8位整数类型是带有陷阱位的扩展整数类型,这些陷阱位以某种方式标记值是有符号的还是无符号的。但是,以下显式使用
CHAR
类型的类似代码永远不会失败:

unsigned char input;
signed char output;
*(unsigned char *)output = input;
因为
char
类型不能有填充/陷阱位

一种可能的变体是:

return ((union { uint8_t u; int8_t s; }){ input }).s;
或对于
char
类型:

return ((union { unsigned char u; signed char s; }){ input }).s;

Edit:正如Steve Jessop在另一个回答中指出的那样,
int8\u t
uint8\u t
如果存在,就不需要有填充位,因此它们的存在意味着
CHAR\u BIT==8
。因此我相信这种方法是有效的。尽管如此,我仍然不会使用
uint8\u t
,而且总是明确的请尽量使用
无符号字符
,以防实现将
uint8_t
实现为一个大小相等的扩展整数类型,因为
char
类型在别名规则和类型双关方面具有特权,这使得它们更为可取。

什么是
sint8_t
?我只知道
uint8_t
t8\u t
。自动转换有什么问题?@Donotalo:他实际上只是得到了自动转换,因为条件永远不会满足。@Donotalo:自动转换在大正值上是未定义的。这是OP试图避免的。@Donotalo:在这种情况下,自动转换有实现定义avior if
x>127
。看看他的代码,pmg,他正在做一些不同的事情。他不想要强制转换。这与标准中的规则
[conv.int]
相冲突,该规则规定“如果目标类型是有符号的,那么如果可以在目标类型(和位字段宽度)中表示,则值不变。”;否则,价值将被定义为实施。”@斯蒂芬:不,它不是未定义的,而是实现定义的或实现定义的信号。而且,我认为你确实颠倒了这两种情况,不是吗?@Jens:我的意思是说行为没有被标准定义,但你是对的,我有点马虎。你想知道哪两种情况?@Potatoswatter:the文字
256
具有类型
int
,因此从
uint8\u t
中减去它会得到
int
结果(§6.3.1.8“常用算术转换”)@Alf:假设不存在这样的实现是不相关的。如果出现一个缺少本机8位算法的平台,并且在该平台上钳位恰好比符号扩展快(一点也不荒谬,尤其是在某些向量架构上),那么很容易就会有一个具有这些属性的实现(如果一个已经存在,我不会感到惊讶)。更重要的是:在回答问题时,您不必更改问题。问题是,正如所问的那样,如何以标准保证的方式进行更改。@阿尔夫:在我的假设场景中,类型确实具有所需的属性。
uint8\u t
是一个无符号8位类型。
int8\u t
是一个2补8位类型然而,从
uint8_t
int8_t
的转换会发生饱和而不是换行。没有
x ^ 0x80
return ( x ^ 0x80 ) - 0x80;
    int8_t( v )
int selector= 127 - x; // 0 or positive if x <=127, negative otherwise
int selector>>= 8; // arithmetic rotate to get -1 or 0
int wrapped_value= x - 256;

return (x&~selector)|(wrapped_value&selector); // if selector is 0, use x, otherwise, use the wrapped value.
uint8_t input;
int8_t output;
*(uint8_t *)&output = input;
unsigned char input;
signed char output;
*(unsigned char *)output = input;
return ((union { uint8_t u; int8_t s; }){ input }).s;
return ((union { unsigned char u; signed char s; }){ input }).s;