C printf 函数在 NASM 程序集中无法正常工作
C printf function does not work properly in NASM assembly
我正在尝试使用用 C 编写的 printTime() 函数来测试我的 32 位 NASM 程序的 运行 时间。
void printTime(float time){
printf("The cpu time is %e \n" , time);
}
这是 NASM 代码的一部分:
push edi <---------------- Here edi holds int 6580001
call printTime
pop edi
这里是GCC生成的汇编代码,我用GDB跟踪每一步:
push ebp
mov esp, ebp
sub 0x8, esp
flds 0x8(esp) <------- x/d ($esp+0x8) gives me 6580001, the correct number
sub 0x4,esp
lea -0x8(esp),esp
fstpl (esp)
push 0x80486f6
call 0x8048370 <printf@plt> <--- it prints 9.220545e-39 here, which is wrong
add 0x10, esp
leave
ret
任何人都可以告诉我为什么 printf 函数打印错误而不是 6.58001e+5 吗?我很感激。
您正在将一个整数加载到浮点寄存器中并期望它被视为相同的浮点值。这不是 IEEE754 浮点数的工作方式,数字使用不同的编码方案。
整数6580001
(0x00646701
)形成的位模式如下分析图所示。浮点数由符号、偏置指数和小数部分组成。
seee eeee efff ffff ffff ffff ffff ffff
0000-0000 0110-0100 0110-0111 0000-0001
首先,让我们来处理一下标志。为0
,即为正数。这是简单的一点:-)
指数位全为零,在 IEEE754 编码中,您通常会减去 127
的偏差,然后乘以 2 的次方以获得乘数。
但是,全零指数被特殊对待(这些是非规范化数字)。首先,向小数位加一的正常做法(见下文)没有完成。其次,乘数调整为2<sup>-126</sup>
而不是2<sup>-127</sup>
.
这使得乘数 1.1754943508222875079687365372222 x 10<sup>-38</sup>
.
对于小数位,您通常会用它们的位值加一(减少二的倒数)但是,因为它是非规范化的,所以您跳过加一:
110-0100 0110-0111 0000-0001
|| | || ||| |
|| | || ||| +- 1/8M = 0.00000011920928955078125
|| | || ||| (64K-4M)
|| | || ||+----------- 1/32K = 0.000030517578125
|| | || |+------------ 1/16K = 0.00006103515625
|| | || +------------- 1/8K = 0.0001220703125
|| | || (2K,4K)
|| | |+----------------- 1/1K = 0.0009765625
|| | +------------------ 1/512 = 0.001953125
|| | (64-256)
|| +----------------------- 1/32 = 0.03125
|| (8,16)
|+--------------------------- 1/4 = 0.25
+---------------------------- 1/2 = 0.5
0.78439342975616455078125
要获得 实际 数字,请将该结果与之前计算的乘数相乘,得到 9.2205004550049022573489420224302 x 10<sup>- 39</sup>
,这就是您看到的结果。
我正在尝试使用用 C 编写的 printTime() 函数来测试我的 32 位 NASM 程序的 运行 时间。
void printTime(float time){
printf("The cpu time is %e \n" , time);
}
这是 NASM 代码的一部分:
push edi <---------------- Here edi holds int 6580001
call printTime
pop edi
这里是GCC生成的汇编代码,我用GDB跟踪每一步:
push ebp
mov esp, ebp
sub 0x8, esp
flds 0x8(esp) <------- x/d ($esp+0x8) gives me 6580001, the correct number
sub 0x4,esp
lea -0x8(esp),esp
fstpl (esp)
push 0x80486f6
call 0x8048370 <printf@plt> <--- it prints 9.220545e-39 here, which is wrong
add 0x10, esp
leave
ret
任何人都可以告诉我为什么 printf 函数打印错误而不是 6.58001e+5 吗?我很感激。
您正在将一个整数加载到浮点寄存器中并期望它被视为相同的浮点值。这不是 IEEE754 浮点数的工作方式,数字使用不同的编码方案。
整数6580001
(0x00646701
)形成的位模式如下分析图所示。浮点数由符号、偏置指数和小数部分组成。
seee eeee efff ffff ffff ffff ffff ffff
0000-0000 0110-0100 0110-0111 0000-0001
首先,让我们来处理一下标志。为0
,即为正数。这是简单的一点:-)
指数位全为零,在 IEEE754 编码中,您通常会减去 127
的偏差,然后乘以 2 的次方以获得乘数。
但是,全零指数被特殊对待(这些是非规范化数字)。首先,向小数位加一的正常做法(见下文)没有完成。其次,乘数调整为2<sup>-126</sup>
而不是2<sup>-127</sup>
.
这使得乘数 1.1754943508222875079687365372222 x 10<sup>-38</sup>
.
对于小数位,您通常会用它们的位值加一(减少二的倒数)但是,因为它是非规范化的,所以您跳过加一:
110-0100 0110-0111 0000-0001
|| | || ||| |
|| | || ||| +- 1/8M = 0.00000011920928955078125
|| | || ||| (64K-4M)
|| | || ||+----------- 1/32K = 0.000030517578125
|| | || |+------------ 1/16K = 0.00006103515625
|| | || +------------- 1/8K = 0.0001220703125
|| | || (2K,4K)
|| | |+----------------- 1/1K = 0.0009765625
|| | +------------------ 1/512 = 0.001953125
|| | (64-256)
|| +----------------------- 1/32 = 0.03125
|| (8,16)
|+--------------------------- 1/4 = 0.25
+---------------------------- 1/2 = 0.5
0.78439342975616455078125
要获得 实际 数字,请将该结果与之前计算的乘数相乘,得到 9.2205004550049022573489420224302 x 10<sup>- 39</sup>
,这就是您看到的结果。