2016-02-03 13 views
5

Chương trình Tôi đang đảo ngược làm phép nhân đơn giản giữa số float và 8 byte integer:Làm thế nào để dịch ngược tính toán lắp ráp x87 này?

section .data 

va: dt 1.4426950408889634074 
vb: dd 0x42424242 
    dd 0x41414141 

section .text 
global main 

main: 
    fld tword[va] 
    fmul qword[vb] 
    ret 

quả dưới gdb:

Breakpoint 1, 0x08048360 in main() 
(gdb) x/i $eip 
0x8048360 <main>:  fld TBYTE PTR ds:0x804953c 
0x8048366 <main+6>:  fmul QWORD PTR ds:0x8049546 
0x804836c <main+12>: ret 
(gdb) x/gx 0x8049546 
0x8049546 <vb>: 0x4141414142424242 
(gdb) si 
0x08048366 in main() 
0x0804836c in main() 
(gdb) info float 
=>R7: Valid 0x4014c726039c95268dc4 +3262848.902912714389 

Tôi đang cố gắng để tái tạo chương trình này trong C (cùng 32bit môi trường):

#include <stdio.h> 

int main() { 

    unsigned long long vb = 0x4141414142424242LL; 
    float r, va = 1.4426950408889634074F; 

    r = va * vb; 
    printf("%f\n", r); 
} 

... nhưng tôi nhận được kết quả rất khác nhau:

$ ./test 
6783712964982603776.000000 

Tôi đang làm gì sai trong chương trình C của mình?

+0

Bạn bè x87 là gì? – m0skit0

+7

x87 là tập hợp con liên quan đến điểm nổi của tập lệnh kiến ​​trúc x86. => https://en.wikipedia.org/wiki/X87 – MCan

+6

@ m0skit0 Tốt hơn cả x86, tất nhiên. – Neil

Trả lời

8

Trong mã asm, bạn thực sự nhân hai số double với hướng dẫn fmul, không phải là floatint. Để thực hiện điều gì đó tương tự trong C:

#include <stdio.h> 
#include <stdint.h> 
#include <string.h> 

int main() 
{ 
    uint64_t vbi = 0x4141414142424242ULL; // hex representation of double 
    double r, vb, va = 1.4426950408889634074; 

    memcpy(&vb, &vbi, sizeof(vb));  // copy hex to double 
    r = va * vb; 
    printf("va = %f, vb = %f, r = %f\n", va, vb, r); 
    return 0; 
} 

Kết quả = va = 1.442695, vb = 2261634.517647, r = 3262848.902913.

LIVE DEMO

+0

Giả sử 'dài int' và' double' có cùng chiều rộng. Điều đó có thể được thực hiện một cách an toàn không? –

+0

OK - nó chỉ dành cho mục đích minh họa, nhưng để giữ cho mọi người vui vẻ, tôi sẽ đổi thành 'uint64_t'. –

+1

Đó là những gì tôi đang tìm kiếm, cảm ơn! – MCan

3

mã lắp ráp này không làm những gì bạn nghĩ rằng đó là thực hiện:

main: 
    fld tword[va] 
    fmul qword[vb] 
    ret 

Bạn đề nghị nhân đơn giản giữa số float và 8 byte integer. Đây thực sự là phép nhân của một giá trị dấu phẩy động kép được mở rộng 10 byte bằng số byte 8 byte (không phải số nguyên 8 byte) được biểu thị bằng 0x4141414142424242. 0x4141414142424242 được coi là các bit của một giá trị dấu phẩy động kép 8 byte theo mã của bạn, không phải là một số nguyên 8 byte được chuyển đổi thành một giá trị dấu phẩy động kép.

Mã cho những gì bạn tin rằng đã xảy ra có thể đã nhìn cái gì đó như:

main: 
    fild qword[vb]  ; Convert 64-bit integer to an extended precision double in st0 
    fld tword[va]  ; st(0)=>st(1) st(0) = 10-byte float(va) 
    fmulp    ; Multiply st(0) and st(1). Result in st(0). 

này chỉ xóa lên hiểu sai bạn của mã lắp ráp.

Các vấn đề liên quan