在便携式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
  • 1281 << 7而不是1 << 8表示
  • 2s补语“被一个人否定”

  • :)

    所以这是我原始代码的更新版本:
    #include <stdint.h>
    
    int8_t DESER_INT8(uint8_t x)
    (
      return ((x >= (1 << 7))
              ? -(UINT8_MAX - x + 1)
              : x);
    )
    

    最佳答案

    1u << 8u0x100u,它大于每个uint8_t值,因此永远不会满足条件。您的“转换”例程实际上是:

    return x;
    

    这实际上是有道理的。

    您需要更清楚地定义要转换的内容。 C99定义了从无符号到有符号整数类型的转换,如下所示(§6.3.1.3“有符号和无符号整数” )



    因此,保留了uint8_t0之间的127值,并且未定义大于127的值的行为。许多(但不是全部)实现将简单地将无符号值解释为有符号整数的二进制补码表示。也许您真正要问的是如何确保跨平台的这种行为?

    如果是这样,您可以使用:
    return x < 128 ? x : x - 256;
    

    x - 256int,保证将x的值解释为二进制补码的8位整数。然后,隐式转换为int8_t会保留此值。

    所有这些都假定sint8_tint8_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/

    10-14 17:40
    查看更多