【问题标题】:C kernel is going crazy with floating point numbers [closed]C内核因浮点数而疯狂[关闭]
【发布时间】: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


【解决方案1】:

numba 未初始化。

// float numba;
float numba = 0.0f;

for(int i = 0; i < 100; i++) {
    numba = numba + 1.0f;
    putPixel(mib, i, (unsigned int)numba, 0xFFFFFF);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-12
    • 1970-01-01
    • 2013-03-04
    • 2015-07-12
    • 2014-07-31
    相关资源
    最近更新 更多