【发布时间】:2021-08-04 01:17:31
【问题描述】:
所以我正在编写一个 C 内核(在保护模式下运行)并且我正在涉足浮点数。首先,我编写了这个 C 程序以确保不会出现任何问题(在我的物理机器上运行,而不是作为内核的一部分),它使用 for 循环每次将 float 值递增 1.0f 并显示结果按预期工作:
#include <stdio.h>
int main() {
float numba;
for(int i = 0; i < 100; i++) {
numba = numba + 1.0f;
printf("%d\n", (unsigned int)numba);
}
}
1
2
3
4
...
99
100
考虑到我不能在我的内核中使用printf,我不得不以其他方式实现它:P。使用像素绘制例程:
void putPixel(MODEINFOBLOCK mib, unsigned int x, unsigned int y, unsigned int color);
...我可以画出完美的对角线。如何?通过增加 for 循环的计数器和 numba,我可以将它们用作例程的 X 和 Y 坐标:
float numba;
for(int i = 0; i < 100; i++) {
numba = numba + 1.0f;
putPixel(mib, i, (unsigned int)numba, 0xFFFFFF);
}
显然它每次都给我numba = 0,因此它不能按预期工作:
通过查看内核的反汇编,我没有发现任何可疑之处:
1ef: 83 c4 10 add esp,0x10
1f2: bb 00 00 00 00 mov ebx,0x0
1f7: d9 44 24 08 fld DWORD PTR [esp+0x8]
1fb: d8 05 f4 10 10 00 fadd DWORD PTR ds:0x1010f4
201: d9 54 24 08 fst DWORD PTR [esp+0x8]
205: 83 ec 0c sub esp,0xc
208: 68 ff ff ff 00 push 0xffffff
20d: d9 7c 24 1e fnstcw WORD PTR [esp+0x1e]
211: 0f b7 44 24 1e movzx eax,WORD PTR [esp+0x1e]
216: 80 cc 0c or ah,0xc
219: 66 89 44 24 1c mov WORD PTR [esp+0x1c],ax
21e: d9 6c 24 1c fldcw WORD PTR [esp+0x1c]
222: df 7c 24 10 fistp QWORD PTR [esp+0x10]
226: d9 6c 24 1e fldcw WORD PTR [esp+0x1e]
22a: ff 74 24 10 push DWORD PTR [esp+0x10]
22e: 53 push ebx
22f: 81 ec 18 01 00 00 sub esp,0x118
235: be 40 1c 10 00 mov esi,0x101c40
23a: b9 46 00 00 00 mov ecx,0x46
23f: 89 e7 mov edi,esp
241: f3 a5 rep movs DWORD PTR es:[edi],DWORD PTR ds:[esi]
243: e8 2c 01 00 00 call 0x374
248: 83 c3 01 add ebx,0x1
24b: 81 c4 30 01 00 00 add esp,0x130
251: 83 fb 64 cmp ebx,0x64
254: 75 a1 jne 0x1f7
256: e8 0c 00 00 00 call 0x267
25b: e8 0d 00 00 00 call 0x26d
260: 83 c4 10 add esp,0x10
263: 5b pop ebx
264: 5e pop esi
265: 5f pop edi
如您所见,内核正在使用 FPU 指令。但是,由于某种原因,该程序没有。
问题是……为什么会这样?保护模式下的 FPU 有什么问题吗?
注意:这是我第一次处理 FP 号码,所以我知道这个问题的答案对你们来说似乎很明显(我希望。)
【问题讨论】:
-
你永远不会初始化
numba。 -
@EricPostpischil 我如何初始化它 ;-)
-
@MARSHMALLOW 您正在编写内核但不知道如何初始化变量?
-
gcc -O1 -Wall甚至会警告你'numba' may be used uninitialized in this function [-Wmaybe-uninitialized]godbolt.org/z/35K5cY1nW。显然禁用优化后您不会收到警告,但编译器可以为您发现这样的错误。似乎几乎不值得取消删除这个问题,因为浮点实际上与问题无关。只是一个独立的非内核程序通常会从堆栈中为未初始化的本地变量读取 0 个字节,如果这是调用堆栈所获得的最深的话。 -
我敢打赌,在这种情况下,未初始化的内存恰好包含
0xff字节。那是NaN。所以numba将保持NaN无论你添加多少次1,它看起来像在x86 上,将NaN转换为unsigned int产生0。这样就可以解释numba如何神秘地无法增加。
标签: c x86 floating-point protected-mode