Невозможно вывести сопроцессор с плавающей точкой из переменной два раза подряд - PullRequest
0 голосов
/ 27 марта 2020

Добрый день! В этом примере я просто добавляю два числа с запятой, сохраняю переменную в тбайт и отображаю одну и ту же переменную два раза подряд на экране, но в первый раз получаю 11.1 как положено, и во второй раз 4.667261E-062 . Почему это происходит?

И еще один вопрос, возможно ли в тбайт каким-либо образом сохранять и получать доступ к номерам по типу массива? например, сохраняя числа в dd , я просто мог сохранить и прочитать их с шагом 4, например, result [0], result [4], et c. Можно ли использовать то же самое с тбайт и как? Если я правильно понял - это должен быть шаг 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 Ответ

1 голос
/ 27 марта 2020

Почему вы делаете fstp qword (double) в тбайт (long double)?

О, это, вероятно, ваша ошибка. Предположительно, макрос invoke добавляет 12 байтов для каждого из аргументов макроса result. (Потому что 10-байтовый tbyte, добавленный к кратным 4-байтовым слотам стека, равен 12).

Но ваша строка формата только указывает sprintf искать double аргументы, которые имеют ширину 8 байтов. Поскольку вы сохранили только qword double в младших 8 байтах result, crt_sprintf может правильно прочитать первый аргумент c arg как double. (x86 имеет младший порядок байтов, поэтому младшие 8 байтов находятся по адресу стека, который просматривает sprintf.)

Но второе преобразование %G будет искать другое double сразу после окончания предыдущего аргумента Который в соответствии со строкой формата должен быть через 8 байт. Но то, что на самом деле нажал invoke, не соответствовало этому. Таким образом, 2-й %G читает 8 байтов, которые перекрывают два 12-байтовых нажатия.

Это, вероятно, 0 в старших 4 байтах (включая биты экспоненты и знака) и ненулевое значение только в младшем 31 бит мантиссы, давая вам очень маленькое субнормальное число. Вы можете использовать отладчик для проверки памяти как double и увидеть, что она представляет значение sprintf read.

Если long double в этой библиотеке C является 10-байтовый тип x87, используйте %LG и используйте fstp tbyte.

Если sizeof(long double) только 8, то это то же самое, что double, и вы не можете печатать значения x87 тбайт с эта C библиотека. (Если у него нет нестандартного расширения для него.) В этом случае вы просто меняете result на qword, соответствующий магазину, который вы делаете.


Кроме того, вы не уравновесили стек x87; используйте faddp, чтобы он пуст после fstp. (Если вашему ассемблеру требуется операнд, используйте faddp st(1) или st1, однако ему нравится записывать имена регистров x87.)

Технически вы нарушаете соглашение о вызовах, делая вызов функции со стеком x87 не пустой, но, очевидно, crt_sprintf не использует все 8 из st0..7, поэтому не получает NaN при переполнении стека x87.

...