【问题标题】:Two's complement and loss of information in CC中的二进制补码和信息丢失
【发布时间】:2014-10-26 11:13:37
【问题描述】:

我想做浮点数据的二进制补码。

    unsigned long Temperature ; 
    Temperature = (~(unsigned long)(564.48))+1;

但问题是演员丢失了信息,564 而不是 564.48。 我可以在不丢失信息的情况下进行二进制补码吗?

【问题讨论】:

  • 564.48 是一个double,转换为unsigned long 会将其截断为564。你期待什么?
  • memcpy 浮点值到 unsigned long 然后应用二的组件。或者将 union 与 float 和 unsigned long 一起使用,将 564.48f 分配给 float,并将 two 的组件应用于 unsigned long。
  • 您实际上需要做什么?对浮点数据执行二进制补码是没有意义的,它不会以任何方式有用。
  • 您知道-564,48 是有效值的温度标度不存在吗?
  • @bitmask:通过 memcpy 和联合的类型双关语是明确定义的(除非陷阱表示);在 C99 的(非规范性)附件中错误地将您未写信给工会成员的信息列为未指定行为;这已用 C11 更正

标签: c casting twos-complement


【解决方案1】:

将浮点值转换为整数值会更改该值的“位内容”。

为了对浮点值的“位内容”执行二进制补码

float f = 564.48f;
unsigned long Temperature = ~*(unsigned long*)&f+1;

确保sizeof(long) == sizeof(float),或使用double 而不是float

【讨论】:

  • 对浮点数的位内容执行二进制补码是完全没有意义的操作。
  • @interjay:这值得一提。我想了想,但认为 OP 可能有他/她的原因(可能需要将其写入寄存器,或者将其存储在一些二进制数据库中等)。
  • 在 C++ 中这是 UB,因为有严格的别名规则。我不太确定C,我认为C中可以,你能确认一下吗?
  • @bolov 它也可以在 C 上中断。最好改用memcpy
  • @bolov:我不确定如何确认这一点。我希望如果两种类型的大小相同,那么没有什么可以阻止它成功执行,即没有分段错误,并且具有确定性的结果(当然,在 LE 和 BE 上不同,因为我已经在答案底部提到)。
【解决方案2】:

这是一件很奇怪的事情;浮点数存储为 2s 补码,因此没有多大意义。

不管怎样,你也许可以使用古老的 union 技巧:

union {
  float real;
  unsigned long integer;
} tmp = { 564.48 };

tmp.integer = ~tmp.integer + 1;
printf("I got %f\n", tmp.real);

当我尝试它时(在 ideone 上)它打印出来了:

I got -0.007412

请注意,这依赖于 未指定 行为,因此如果您的编译器没有以最直接的方式实现访问,它可能会中断。这是不同形式的 undefined 行为(这会使代码无效),但仍然不是最佳的。有人确实告诉我,更新的标准使其更清晰,但我没有找到确切的参考,所以......请考虑自己警告。

【讨论】:

  • 不确定it's safe in modern compilers 来自哪里?
  • @tangrs 我也没有......我重写了它以使其更清晰。
  • 也许我错了,但似乎 OP 想要 (~tmp.integer) + 1 而不是 ~tmp.integer + 1
  • @AlterMann 这些表达式完全等价,~ has higher precedence+
  • @unwind,在这种情况下,tmp.integer = ~tmp.integer;tmp.integer = ~tmp.integer + 1;产生相同的结果:ideone.com/siXo94,如果在转换后加1,结果必须是0.992588
【解决方案3】:

你不能在浮点数上使用~(它必须是整数类型):

#include <stdio.h>

void print_binary(size_t const size, void const * const ptr)
{
    unsigned char *b = (unsigned char *) ptr;
    unsigned char byte;
    int i, j;

    for (i = size - 1; i >= 0; i--) {
        for (j = 7; j >= 0; j--) {
            byte = b[i] & (1 << j);
            byte >>= j;
            printf("%u", byte);
        }
    }
    printf("\n");
}

int main(void)
{
    float f = 564.48f;
    char *p = (char *)&f;
    size_t i;

    print_binary(sizeof(f), &f);
    for (i = 0; i < sizeof(float); i++) {
        p[i] = ~p[i];
    }
    print_binary(sizeof(f), &f);
    f += 1.f;
    return 0;
}

输出:

01000100000011010001111010111000
10111011111100101110000101000111

当然print_binary 用于测试结果,将其删除,并且(正如@barakmanos 所指出的)print_binary 假定为小端,其余代码不受端的影响:

#include <stdio.h>

int main(void)
{
    float f = 564.48f;
    char *p = (char *)&f;
    size_t i;

    for (i = 0; i < sizeof(float); i++) {
        p[i] = ~p[i];
    }
    f += 1.f;
    return 0;
}

【讨论】:

  • 在 big-endian 和 little-endian 上会产生不同的结果。
  • @barakmanos,你确定吗?字节序仅影响字节顺序。由于char 恰好是一个字节,因此在所有字节序格式上都是相同的。 Take a look
  • 是的,但是当您调用 print_binary 时,您会将 float* 转换为 char*
  • 顺便说一句,我没有说这是错误的,我暗示作为答案的一部分值得一提......
  • 谢谢...有一分钟我以为for 循环(您实际上更改浮点值的地方)也受到底层硬件架构的字节序的影响,但后来我意识到你是分别为每个字节做的。
猜你喜欢
  • 1970-01-01
  • 2016-10-01
  • 1970-01-01
  • 2012-11-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 1970-01-01
相关资源
最近更新 更多