将整数写入 HEX 与实际整数之间的区别

Difference between writing an integer in a HEX from a real

我在大学里接到了一个任务,有一个数字,我需要按照计算机上显示的那样以十六进制显示它。我写了一个翻译有符号整数的程序。而且我还在 HEX 中发现了一个实数条目。但是和平时的不一样。

对于我使用的整数:printf("%#X", d);

我使用的实数:printf("%#lX", r);

如果我输入 12,首先打印:0xC

如果我输入 12.0,第二次打印:0x4028000000000000

你能解释一下有什么区别以及它是如何计算的吗?

X 格式说明符需要一个 intunsigned int 参数。使用 l 修饰符,它需要一个 longunsigned long int 参数。如果你用其他任何东西(比如 double)调用它,你会得到未定义的行为。

如果要打印十六进制浮点数(带大写字母),请使用 %A 格式,对于 12.0 将打印 0X1.8P+3 -- 1½×23

生成十六进制数字的编码是一个简单的内存转储。

不同类型的流程差别不大。


下面通过对象的地址和大小组成一个字符串打印

#include <stdio.h>
#include <assert.h>
#include <limits.h>

//                                    .... compound literal ....................
#define VAR_TO_STR_HEX(x) obj_to_hex((char [(sizeof(x)*CHAR_BIT + 3)/4 + 1]){""}, &(x), sizeof (x))

char *obj_to_hex(char *dest, void *object, size_t osize) {
  const unsigned char *p = (const unsigned char *) object;
  p += osize;
  char *s = dest;
  while (osize-- > 0) {
    p--;
    unsigned i = (CHAR_BIT + 3)/4;
    while (i-- > 0) {
      unsigned digit = (*p >> (i*4)) & 15;
      *s++ = "0123456789ABCDEF"[digit];
    }
  }
  *s = '[=10=]';
  return dest;
}

int main(void) {
  double d = 12.0;
  int i = 12;
  printf("double %s\tint %s\n", VAR_TO_STR_HEX(d), VAR_TO_STR_HEX(i) );
  d = -d;
  i = -i;
  printf("double %s\tint %s\n", VAR_TO_STR_HEX(d), VAR_TO_STR_HEX(i) );
  return 0;
}

输出

double 4028000000000000 int 0000000C
double C028000000000000 int FFFFFFF4

对于更复杂的对象,输出可能包含填充 bits/bytes 并且输出对字节序敏感。

使用格式 %#lX 打印 doubler 实际上有未定义的行为。

您很幸运能够将值 12.0 表示为 double 的 64 位,除非 r 的类型为 unsigned long 并且是从值 12.0 这样:

unsigned long r;
double d = 12.0;
memcpy(&r, &d, sizeof r);
printf("%#lX", r);

但类型 unsigned long 并非在所有平台上都支持 64 位,实际上它在 32 位英特尔 ABI 上也没有。您应该使用来自 <stdint.h> 的类型 uint64_t 和来自 <inttypes.h>:

的转换格式
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>

int main() {
    int x = 12;
    printf("int: %#X  [", x);
    for (size_t i = 0; i < sizeof x; i++) {
        printf(" %02X", ((unsigned char *)&x)[i]);
    }
    printf(" ]\n");

    double d = 12.0;
    uint64_t r;
    memcpy(&r, &d, sizeof r);
    printf("double: %#"PRIX64" [", r);
    for (size_t i = 0; i < sizeof d; i++) {
        printf(" %02X", ((unsigned char *)&d)[i]);
    }
    printf(" ]\n");
    printf("sign bit: %d\n", (int)(r >> 63));
    printf("exponent: %d\n", (int)((r >> 52) & 2047));
    unsigned long long mantissa = r & ((1ULL << 52) - 1);
    printf("mantissa: %#llX, %.17f\n",
           mantissa, 1 + (double)mantissa / (1ULL << 52));
    return 0;
}

输出:

int: 0XC  [ 0C 00 00 00 ]
double: 0X4028000000000000 [ 00 00 00 00 00 00 28 40 ]
sign bit: 0
exponent: 1026
mantissa: 0X8000000000000, 1.50000000000000000

Double-precision floating-point format一文所述,该表示对应一个值为1.5*21026-1023的正数,即:1.5*8 = 12.0。