将整数写入 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
格式说明符需要一个 int
或 unsigned int
参数。使用 l
修饰符,它需要一个 long
或 unsigned 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
打印 double
值 r
实际上有未定义的行为。
您很幸运能够将值 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。
我在大学里接到了一个任务,有一个数字,我需要按照计算机上显示的那样以十六进制显示它。我写了一个翻译有符号整数的程序。而且我还在 HEX 中发现了一个实数条目。但是和平时的不一样。
对于我使用的整数:printf("%#X", d);
我使用的实数:printf("%#lX", r);
如果我输入 12
,首先打印:0xC
如果我输入 12.0
,第二次打印:0x4028000000000000
你能解释一下有什么区别以及它是如何计算的吗?
X
格式说明符需要一个 int
或 unsigned int
参数。使用 l
修饰符,它需要一个 long
或 unsigned 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
打印 double
值 r
实际上有未定义的行为。
您很幸运能够将值 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。