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-值的问题/
作者
Fany Full
发布于
2022年5月15日
许可协议