Can't output coprocessor float from variable two times in a row

谁说胖子不能爱 提交于 2021-01-29 14:35:55

问题


Good afternoon! In this example, I simply add two numbers with a comma, save the variable in tbyte and display the same variable two times in a row on the screen, but the first time I get 11.1, as it should be, and the second time 4.667261E-062. Why is this happening?

And one more question, is it possible in tbyte to somehow save and access numbers by array type? for example, storing numbers in dd, I just could save and read them in increments of 4, for example, result [0], result [4], etc. Is it possible to use the same with tbyte and how? If I understand right - it should be a step of 10.

.386
.model flat,stdcall
option casemap:none

include \masm32\include\masm32rt.inc

.data
titletext db  'Title',0
frmt db 'Result1 = %.7G',10
     db 'Result2 = %.7G',0
buff db 1024 dup (?)
result tbyte ?
num1 qword 5.5
num2 qword 5.6

.code
start:
    finit
    fld qword ptr [num1]
    fld qword ptr [num2]
    fadd
    fstp qword ptr [result]

    invoke crt_sprintf,addr buff,addr frmt, result, result
    invoke MessageBox,0,addr buff,addr titletext,MB_OK
    invoke ExitProcess,0
end start

回答1:


Why are you doing a fstp qword (double) into a tbyte (long double)?

Oh, that's probably your bug. Presumably the invoke macro pushes 12 bytes for each of the result macro args. (Because a 10-byte tbyte padded to a multiple of 4-byte stack slots is 12).

But your format string only tells sprintf to look for double args, which are 8 bytes wide. Since you only stored a qword double to the low 8 bytes of result, crt_sprintf can correctly read the first variadic arg as a double. (x86 is little-endian so the low 8 bytes are at the stack address sprintf is looking at.)

But the 2nd %G conversion will be looking for another double right after the end of the previous arg. Which according to the format string should be 8 bytes later. But what your invoke actually pushed didn't match that. So the 2nd %G reads 8 bytes that overlap the two 12-byte pushes.

It's probably 0 in the upper 4 bytes (including exponent and sign bit), and non-zero only in the low 31 bits of the mantissa, giving you a very small subnormal number. You can use a debugger to examine memory as a double and see that it represents the value sprintf read.

If long double in that C library is the 10 byte x87 type, use %LG and use fstp tbyte.

If sizeof(long double) is only 8, then it's the same as double and you can't printf x87 tbyte values with that C library. (Unless it has some non-standard extension for it.) In that case You just change result to also be a qword, matching the store you're doing.


Also, you didn't balance the x87 stack; use faddp so it's empty after the fstp. (If your assembler requires an operand, use faddp st(1) or st1, however it likes to spell x87 register names.)

You're technically violating the calling convention by making a function call with the x87 stack non-empty, but apparently crt_sprintf doesn't use all 8 of st0..7 so it doesn't get a NaN from overflowing the x87 stack.



来源:https://stackoverflow.com/questions/60887371/cant-output-coprocessor-float-from-variable-two-times-in-a-row

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!