C 语言关于 %d 打印 double 值的问题
先贴上我在 v2ex 上问的这个问题:
让我疑惑的代码:
#include <stdio.h>
int main()
{
float a = 3.3f;
int b = 2;
printf("%d", a * b);
}
运行截图:
运行环境:
Microsoft Windows [版本 10.0.19043.1645]
gcc version 11.3.0 (MinGW-W64 x86_64-ucrt-posix-seh, built by Brecht Sanders)
在这个环境下每一次运行出来的结果都是一样的。
然后切换到 Linux 的环境:
运行截图:
运行环境:
cenos
gcc version 8.5.0 20210514 (Red Hat 8.5.0-4) (GCC)
在 Linux 这个环境下每一次的运行结果都是不一样的。
然后我想弄清楚的是,这个问题是不是就是将 double 类型的值在内存中截掉一半然后打印出来(截掉前半段)。
所以我们用一个程序来验证一下:
#include <stdio.h>
union HEXDOUBLE
{
double num;
unsigned char bnum[8];
};
int main()
{
float m = 3.3f;
int n = 2;
union HEXDOUBLE a;
a.num = (double)m * n;
printf("%d\n", a.num);
for (int i = 0; i < 8; ++i) //大端模式顺着来0-8,小端模式逆着来8-0
{
printf("%x ", a.bnum[8 - i - 1]);
}
printf("\n");
printf("%d\n", a.num);
printf("%lf", a.num);
return 0;
}
运行结果:
(double) a * b = 0x40 1a 66 66 60 00 00 00, 截断四个字节后 0x60 00 00 00 = 1610612736
我利用这个工具网将 16 进制的数转换了一下,使用 IEEE754 标准,如下:
发现在 Windows 下,C 语言 %d 打印 double 的结果确实是把 double 类型在内存中的值截掉一半然后打印。
对于 Linux 系统中的值,我在 v 站中得到了这样的解释:
在 x86_64 下,整数和指针参数通过通用寄存器传递,浮点数通过浮点数寄存器传递。
这里调用时往浮点数寄存器写入了参数,但函数里面却去通用寄存器里读取,所以结果是随机的。
在 x86 下,参数都通过栈传递,结果应该是 double 截断的结果。
但是,新的问题又来了,为什么第二次打印 a.num
的值一直是
10
呢?
也罢,暂时把这个问题先搁置在这里,汇编编译出来的代码如下:
.file "test_format_d.c"
.text
.section .rodata
.LC1:
.string "%d\n"
.LC2:
.string "%x "
.LC3:
.string "%lf"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movss .LC0(%rip), %xmm0
movss %xmm0, -8(%rbp)
movl $2, -12(%rbp)
cvtss2sd -8(%rbp), %xmm1
cvtsi2sd -12(%rbp), %xmm0
mulsd %xmm1, %xmm0
movsd %xmm0, -24(%rbp)
movsd -24(%rbp), %xmm0
movl $.LC1, %edi
movl $1, %eax
call printf
movsd -24(%rbp), %xmm0
movl $.LC1, %edi
movl $1, %eax
call printf
movl $0, -4(%rbp)
jmp .L2
.L3:
movl $7, %eax
subl -4(%rbp), %eax
cltq
movzbl -24(%rbp,%rax), %eax
movzbl %al, %eax
movl %eax, %esi
movl $.LC2, %edi
movl $0, %eax
call printf
addl $1, -4(%rbp)
.L2:
cmpl $7, -4(%rbp)
jle .L3
movl $10, %edi
call putchar
movsd -24(%rbp), %xmm0
movl $.LC1, %edi
movl $1, %eax
call printf
movsd -24(%rbp), %xmm0
movl $.LC3, %edi
movl $1, %eax
call printf
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.section .rodata
.align 4
.LC0:
.long 1079194419
.ident "GCC: (GNU) 8.5.0 20210514 (Red Hat 8.5.0-4)"
.section .note.GNU-stack,"",@progbits
C 语言关于 %d 打印 double 值的问题
http://fanyfull.github.io/2022/05/15/C-语言关于-d-打印-double-值的问题/