【问题标题】:float to double (IEEE754) conversion浮点到双 (IEEE754) 转换
【发布时间】:2017-12-30 18:58:17
【问题描述】:

我正在尝试在 x86 架构的 asm 中将 32 位 float 转换为 64 位 double。转换是由用 asm 编写的函数完成的,然后我想从 C 中调用它。我不知道我做错了什么,但dst 指向的内存似乎保持不变,并且在printf 程序崩溃之后。 我想在没有任何浮点指令的情况下这样做。这是代码:

.686 
.model flat 
public _conv

.data
mantissa_mask dd 00000000011111111111111111111111b
exponent_mask dd 01111111100000000000000000000000b

.code 

_conv PROC
pusha
mov ebp, esp

mov esi, dword ptr [ebp+8] ; src
mov edi, dword ptr [ebp+12]; dst

mov dword ptr [edi], 0
mov dword ptr [edi+4], 0

mov eax, dword ptr [esi]
and eax, dword ptr mantissa_mask
mov dword ptr [edi], eax
xor edx, edx ; zero edx
mov ecx, 1
shl ecx, 29 ;ecx == 2^29
mul ecx ;so it's like `shl edx:eax, 29`
mov dword ptr [edi], eax
mov dword ptr [edi+4], edx

mov eax, dword ptr [esi]
and eax, dword ptr exponent_mask
shr eax, 23 ;put exponent on lowest bits
sub eax, 127 ;exponent in float is coded enlarged by 127
add eax, 1023 ;in double it's enlarged by 1023
shl eax, 20 ;exponent in double starts on 20bit of 2nd byte
or dword ptr [edi], eax

;sign bit:
bt dword ptr [esi], 31
jc set_sign_bit
    btr dword ptr [edi+4], 31
    jmp endthis
set_sign_bit:
    bts dword ptr [edi+4], 31
endthis:

popa
ret
_conv ENDP

END

还有C代码:

void conv(float * src, double * dst);

int main()
{
    float src = 4.5f;
    double dst = 0.;
    conv(&src, &dst);
    printf("%f\n", dst);
    return 0;
}

【问题讨论】:

  • 确保不会意外编译为 64 位。此外,将dst 设置为非零,以便您可以检查它确实没有被触及。并且一如既往地使用调试器。
  • 你应该做一些调试。
  • @Jester 我刚刚发现了一些错误。抱歉,我在问题中编辑了代码。
  • 转换未正确处理亚法线。建议首先将conv(float * src, double * dst); 创建为 C 代码以确保功能正常 - 然后将其编写为 .asm。
  • shr eax, 23 sub eax, 127 add eax, 1023 shl eax, 20 可以在 2 条指令中完成。这是一个细节,但是如果你不能用两条指令来编写汇编,那么它会适得其反,因为如果你用高级语言编写了相同的四个操作,编译器就会知道将你写的内容转换成这些两条指令。而且它会更具可读性。而且它会更容易工作。

标签: c assembly floating-point x86


【解决方案1】:

您的主要问题是访问参数。由于您执行了pusha,因此参数不是在[ebp+8] 和[ebp+12],而是在[ebp+36] 和[ebp+40]。调试器会立即向您展示这一点。即使进行了这些更改,您的代码仍然会损坏。

【讨论】:

  • 哦,谢谢pusha。完全没有注意到这一点。通过一些错误修复(我已经对有问题的代码进行了编辑)并更改 args 访问它不会崩溃,但输出 0。请再次查看代码,因为有编辑。
【解决方案2】:

好的,终于成功了。 Jester 关于 args 访问的建议非常有帮助。愚蠢的事情,但很难注意到。这是最终代码:

.686 
.model flat 
public _conv

.data
mantissa_mask dd 00000000011111111111111111111111b
exponent_mask dd 01111111100000000000000000000000b

.code 

_conv PROC
pusha
mov ebp, esp

;+36 and +40 since pusha
mov esi, dword ptr [ebp+36]; src
mov edi, dword ptr [ebp+40]; dst

mov dword ptr [edi], 0
mov dword ptr [edi+4], 0

;mentissa:
mov eax, dword ptr [esi]
and eax, dword ptr mantissa_mask
mov dword ptr [edi], eax
xor edx, edx ; zero edx
mov ecx, 1
shl ecx, 29 ;ecx == 2^29
mul ecx ;so it's like `shl edx:eax, 29`
mov dword ptr [edi], eax
mov dword ptr [edi+4], edx

;exponent:
mov eax, dword ptr [esi]
and eax, dword ptr exponent_mask
shr eax, 23 ;put exponent on lowest bits
sub eax, 127 ;exponent in float is coded enlarged by 127
add eax, 1023 ;in double it's enlarged by 1023
shl eax, 20 ;exponent in double starts on 20bit of 2nd byte
or dword ptr [edi+4], eax

;sign bit:
bt dword ptr [esi], 31
jc set_sign_bit
    btr dword ptr [edi+4], 31
    jmp endthis
set_sign_bit:
    bts dword ptr [edi+4], 31
endthis:

popa
ret
_conv ENDP

END

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    • 2023-03-31
    • 2023-04-07
    • 1970-01-01
    • 2013-05-24
    • 1970-01-01
    相关资源
    最近更新 更多