На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Перед отправкой сообщения внимательно прочтите правила раздела!!!
1. Запрещается обсуждать написание вирусов, троянов и других вредоносных программ!
2. Помните, что у нас есть FAQ раздела Assembler и Полезные ссылки. Посмотрите, возможно, там уже имеется решение вашего вопроса.

3. Настоятельно рекомендуем обратить особое внимание на правила форума, которые нарушаются чаще всего:
  3.1. Заголовок темы должен кратко отражать её суть. Темы с заголовками типа "Срочно помогите!" или "Ассемблер" будут отправляться в Корзину для мусора.
  3.2. Исходники программ обязательно выделяйте тегами [code]...[/code] (одиночные инструкции можно не выделять).
  3.3. Нежелательно поднимать старые темы (не обновлявшиеся более года) без веской на то причины.

Не забывайте также про главные Правила форума!

Добро пожаловать и приятного вам общения!!! ;)
 
Модераторы: Jin X, Qraizer
  
> floating-point invalid в dll
    Здравствуйте. Использую dll в VS C++ написанную в Borlande. Эта dll писалась для одного приложения, в котором она отлично работает.
    Используя эту .dll в VS, вылетает ошибка floating-point invalid при использовании одной из функций.
    Ковыряю dll в IDA pro, обнаружил, что ошибка вылетает при выполнении следующей конструкции:

    ExpandedWrap disabled
      do
      {
      ...
      var = (unsigned __int64) (pow(2.0, (double) k) - 1.0);
      ...
      k++;
      }
      while(k < 64)


    Попробовал выполнить этот код в консольном приложение VS, вылетает точно такая же ошибка: floating-point invalid.
    Как быть? Почему в приложении собранном в Borland эта .dll успешно работает, при использовании же в VS вылетает ошибка.
    Кстати под Win x64 .dll так же работает, по понятным причинам. С чем это связано? VS при сборке проекта изменяет эту .dll, может нужны какие нибудь параметры компиляции?
    Было бы здорово заменить конструкцию приведённую выше на:

    ExpandedWrap disabled
      do
      {
      ...
      var = ((unsigned __int64) 1 << k) - 1;
      ...
      k++;
      }
      while(k < 64)
      Соглашение о вызове совпадает?
        100%
        В Win x64 же работает.
        Сообщение отредактировано: Abraziv -
          А код этой DLL есть? Это функция pow?
            Нету. Только псевдокод в IDA.

            Добавлено
            Нет, это не функция pow(). То что я привёл, это кусок псевдокода из IDA. Несколько итераций проходит, потом падает.

            Добавлено
            Как вообще происходит использование .dll. В нете не могу найти инфу. Просто мне кажется, что компилятор что то делает с этой .dll.

            Добавлено
            Хотя нет. Exe собранная на Win64 под x86 запускается только на Win64, если запустить на Win32, то падает. Компилятор здесь не причём. Это винда значит, что-то делает.
            Сообщение отредактировано: Abraziv -
              А что делает эта функция-то вообще?
              Прикрепите эту DLL-ку и вызов функции (нерабочий и рабочий код - VS, Borland).
              Сложно что-то сказать, ничего не видя...

              Вызов функции из DLL происходит как обычный вызов функции. Небольшие отличия есть только в вызове через указатель (GetProcAddress) и когда DLL грузится системой (когда функция в импорте), но на работе это никак не должно отражаться.
                Цитата
                100%
                В Win x64 же работает.


                В 64-битном режиме соглашение о вызове единственное, так что это не аргумент.

                Покажи объявление функции в DLL, если оно есть и в использующем её приложении Билдера и VS
                  Не в 64 битном режиме, а на 64 битной винде 32 битное приложение.

                  Добавлено
                  Ок. Скоро скину сюда.
                    Еще вариант - посмотреть, совпадает ли кодовое слово сопроцессора 8087CW
                      А можно по подробнее ? Где в VS глянуть?

                      Добавлено
                      Сразу же не вкурил что вы имели ввиду. Вы имеете ввиду слово состояния сопроцессора. Хм. Как узнать, какой используется в том приложении. В IDA pro? Но как?
                        IDA не пользовался. В отладчике Borland его состояние можно видеть.

                        В Borland-овских продуктах его получают функцией Get8087CW и устанавливают Set8087CW. Если аналоги есть в VS, то можно сравнить состояние перед вызовом
                        Сообщение отредактировано: MBo -
                          У меня только .dll. Нет возможности запустить в отладке.
                            В отладке запускается приложение, использующее DLL.
                            Приложение же своё, самописное? Тогда и отладка не нужна - вывести слово состояния перед вызовом.
                            Сообщение отредактировано: MBo -
                              Да самописное. Народ подскажите как в плюсах (WinAPI) установить слово состояния арифметического сопроцессора. Или без ассемблера ни как не обойтись? И какой слово состояния используется в делфях по дефолту???
                              Сообщение отредактировано: Abraziv -
                                $1332 или $1372
                                  Попробую сделать так:
                                  __asm {
                                  MOV EAX, 0x1332;
                                  PUSH EAX;
                                  FLDCW [ESP];
                                  POP EAX;
                                  }
                                  Сейчас нет возможности проверить, т.к. винда 64 бита.

                                  Добавлено
                                  Спасибо всем заранее.
                                    Цитата Abraziv @
                                    Попробую сделать так
                                    Попробуйте 0x133F, т.к. это замаскирует все ошибки, и исключения при ошибках вызываться не будут. Но вообще, если причина в этом, значит либо в коде DLL ошибка, либо ей передаются неверные данные, которые не проверяются.

                                    Цитата Abraziv @
                                    Не в 64 битном режиме, а на 64 битной винде 32 битное приложение.
                                    В 32-битных программах соглашений куча. И они могут быть другими в Borland'е (и Borland - это что: Builder, Delphi...?) А в какой винде запускается 32-битная прога (x86 или x64) разницы быть не должно. Если возникает ошибка только в x86, то это странно.

                                    Почему не хотите прислать DLL и код, который его вызывает (рабочий - борландовский и MSVSCP)? Мы бы могли посмотреть в отладчике что происходит, а не гадать на кофейной гуще.
                                    И что вообще эта функция должна делать?
                                    Может, там вообще глюк из разряда: нужно передать Double через 2 даблворда в стеке, а он передаётся через ссылку...

                                    p.s. CW - управляющее слово только, а не кодовое ;)

                                    Добавлено
                                    Только сейчас увидел сообщение в личке... :)
                                      Вангую переполнение стека FPU.
                                        В общем, ситуация следующая...
                                        DLL-функция вызывает функцию Trunc(X), которая удаляет дробную часть числа. Результат возвращается в виде 64-битного целого.
                                        Код этой функции таков:
                                        ExpandedWrap disabled
                                          add esp,FFFFFFF4
                                          wait
                                          fnstcw word ptr ss:[esp]
                                          wait
                                          mov al,byte ptr ss:[esp+1]
                                          or byte ptr ss:[esp+1],C
                                          fldcw word ptr ss:[esp]
                                          fistp qword ptr ss:[esp+4]
                                          mov byte ptr ss:[esp+1],al
                                          fldcw word ptr ss:[esp]
                                          mov eax,dword ptr ss:[esp+4]
                                          mov edx,dword ptr ss:[esp+8]
                                          add esp,C
                                          ret
                                        Т.е. читается CW, устанавливаются биты, отвечающие за округление в режим "обрезания дробной части", выполняется fistp, затем CW восстанавливается.

                                        Так вот, каждый раз туда передаётся число, вдвое больше предыдущего (с вычетом единицы). В определённый момент (на последнем цикле) значение этого числа выходит за рамки 64 битного знакового числа, fistp помечает соответствующие биты в SW (status word) о том, что произошла исключительная ситуация, однако исключения сразу не происходит (оно происходит при вызове fwait и т.п. инструкций, в т.ч. после fldcw). И тут попадается fldcw, и возникает исключение.
                                        Почему только в 32-битной Windows? Потому что в 64-битной Windows CW=027F, в 32-битной CW=1272 (по крайней мере, в 7-ке, все вопросы к мелкомягким и их VSC++). Т.е. в 32-битной не замаскировано исключение #IA (invalid arithmetic), которое возникает при записи слишком большого значения как целого.

                                        Что делать?
                                        Вызывать finit из основной проги перед вызовом функции armInit :)
                                        Или выполнять такой код:
                                        ExpandedWrap disabled
                                              short CW, CWnew;
                                              __asm {
                                                  fnstcw CW
                                                  fnstcw CWnew
                                                  or CWnew, 1  // можно сделать or CWnew,0x3F для надёжности :)
                                                  fldcw CWnew
                                              }
                                              amrInit(0);
                                              __asm fldcw CW


                                        Добавлено
                                        Ну или так:
                                        ExpandedWrap disabled
                                              __asm {
                                                  push eax
                                                  fnstcw [esp]
                                                  push eax
                                                  fnstcw [esp]
                                                  or [esp], 1
                                                  fldcw [esp]
                                                  pop eax
                                              }
                                              amrInit(0);
                                              __asm {
                                                  fldcw [esp]
                                                  pop eax
                                              }
                                          В таком случае это не проблема ОСи. Trunc() не должна сообщать о проблеме, т.к. от проебразования к unsigned __int64 требуется беззнаковый результат, значит коли это происходит, Trunc() реализована неверно.
                                            А это и не проблема оси. Это проблема MSVCPP, потому что на момент старта программы CW=027F как в 32 битах, так и в 64-х. И прога его меняет его ещё до вызова mainCRTStartup в зависимости от битности винды...
                                              Цитата Jin X @
                                              Это проблема MSVCPP, потому что на момент старта программы CW=027F как в 32 битах, так и в 64-х. И прога его меняет его ещё до вызова mainCRTStartup в зависимости от битности винды...
                                              Хм.
                                              Visual Studio 2015:
                                              Цитата _control87, _controlfp, __control87_2
                                              По умолчанию библиотеки времени выполнения маскируют все исключения для операций с плавающей запятой .
                                              И насколько я помню, так было всегда. Кто-то однозначно неправ.

                                              Добавлено
                                              Попробую ещё раз вангануть: dll имеет и другие проблемы, по крайней мере с совместимостью.
                                              Сообщение отредактировано: Qraizer -
                                                Цитата Qraizer @
                                                Попробую ещё раз вангануть: dll имеет и другие проблемы, по крайней мере с совместимостью.
                                                Ну, скорее всего, да.
                                                Пустой проект показывает нормальный CW (027F) к моменту mainCRTStartup...
                                                  Переписать код. Видишь ли, var = (unsigned __int64) (pow(2.0, (double) k) - 1.0); работает – если работает – по чистой случайности. FPU не имеет беззнаковых типов, даже целочисленных. При сохранении в квадрословное целое FPU всегда сохраняет его как знаковое. Если подать ему 263, то будет фиксироваться переполнение, о котором сообщается через IA. Если оно замаскировано, то маскированной реакцией является возврат целочисленной неопределённости, которая имеет вид минимально возможного (знакового) целого, т.е. 0x8000000000000000. И оно по чистой случайности совпадает с 263 в беззнаковом целом формате.
                                                    NaN – это нечисло. Думаю, этого достаточно, чтобы понять абсурдность желания. NaN появляется в вычислениях, которые невозможно вычислить. Они не имеют результата. Даже если поделить 123 на 0, будет не NaN, а всего лишь INF, с которым в принципе можно продолжать вычисления. При делении 0 на 0 будет NaN.
                                                    Если твоему алгоритму подходят выражения без результата, то мне сложно представить, что полезного такой алгоритм в состоянии предложить.
                                                      Цитата Qraizer @
                                                      Видишь ли, var = (unsigned __int64) (pow(2.0, (double) k) - 1.0); работает – если работает – по чистой случайности.
                                                      ... Если подать ему 263, то будет фиксироваться переполнение

                                                      С чего это? В соответствии с выражением к целому должно приводиться не 263, а 263-1.0 = _I64_MAX. Может просто ms-оптимизатор тупит, заменяя вещественное вычитание целочисленным?
                                                        leo, Qraizer, вы оба правы, только есть нюанс: точность.
                                                        Оптимизатор не тупит, всё нормально. В Trunc передаётся уже 263-1, но! Мантисса FPU (при 80-битной точности) содержит 63 бита (без знака). 263-1 – это ещё 63 бита, а вот 263 – уже 64. После pow результат содержит число 263, после него идёт вычитание единицы (fsub), вот только вычесть эту единицу он не может, т.к. точности не хватает, и там остаётся 263. И при преобразовании в unsigned __int64:
                                                        Цитата Qraizer @
                                                        оно по чистой случайности совпадает с 263 в беззнаковом целом формате
                                                        :)

                                                        Добавлено
                                                        Хотя результат по задумке должен быть 263-1... так что, уж не знаю в какой момент эта DLL-ка глюканёт при дальнейшей работе...

                                                        Добавлено
                                                        Вспоминается статья про Закон дырявых абстракций...
                                                        Как раз в тему... :)
                                                          Хотя, нет, немного не так... FPU нормально вычитает единицу из 263 при точности FPU-операций в 80 бит, результат же умещается в 63 бита. Проблема в другом: CW=1272 в 32 битах и CW=027F в 64 битах. А это вычисления с точностью double, т.е. 64 бита (где составляет 52 бита без учёта знака), т.е. результат будет неверным (без вычета единицы) значительно раньше, чем на последнем цикле. Так что, решить первоначальную проблему лучше изменением в CW не младшего бита, а установкой значения 3 в битах 8-9:
                                                          ExpandedWrap disabled
                                                                __asm {
                                                                    push eax
                                                                    fnstcw [esp]
                                                                    or word ptr [esp],0x300  // если ошибки всё же будут возникать (уже в другом месте), можно заменить 0x300 на 0x301, но тогда будет потеря точности вычислений (местами весьма серьёзная)... но такого быть уже не должно
                                                                    fldcw [esp]
                                                                    pop eax
                                                                }
                                                                amrInit(0);

                                                          Тогда и результаты вычислений будут верными...
                                                            Спасибо за ответ. Огромное спасибо. Сейчас буду тестировать. После отпишусь. Скажу сразу же, прога не падает после асм вставки.
                                                              Цитата Jin X @
                                                              Мантисса FPU (при 80-битной точности) содержит 63 бита (без знака)...

                                                              Учти, что вычисляется степень двойки, а двоичная мантисса любого числа 2N строго равна 1 и представляется абсолютно точно в любом вещественном формате. Не знаю, как в билдере и мсвс, а в дельфях math.Power(..) для целых степеней считается через IntPower методом умножения, поэтому число 263 должно вычисляться абсолютно точно с мантиссой строго равной 1 без всякого дребезга в младших разрядах.
                                                                Цитата leo @
                                                                Учти, что вычисляется степень двойки, а двоичная мантисса любого числа 2N строго равна 1 и представляется абсолютно точно в любом вещественном формате.
                                                                Это понятно, только ты из 2N не сможешь вычесть единицу, если N > 63 (для 80-битных чисел) или > 52 (для double), или > 23 (для single/float). А поскольку точность была настроена на double, то вычесть из 263 единицу не получалось, и в unsigned __int64 преобразовывалось не _I64_MAX, а _I64_MAX+1, и возникало исключение.

                                                                Добавлено
                                                                Цитата leo @
                                                                в дельфях math.Power(..) для целых степеней считается через IntPower методом умножения
                                                                И надо сказать, что это значительно быстрее, чем через fyl2x/f2xm1/fscale. Иногда в 2 раза, а иногда раз в 5-6 (при малых степенях). Специально мерил... :)
                                                                Но там есть косяк (в Delphi): он не может вычислять степень > MaxInt (по модулю) из отрицательных чисел. Возникает исключение. Что в Delphi 7, что в Berlin. Т.е. если число > MaxInt или нецелое, он выполняет вычисление через fyl2x/f2xm1/fscale (и вылетает на fyl2x, соответственно).

                                                                Добавлено
                                                                Если кому надо – вот рабочая функция вычисления степени любого числа из любого. Вариант без IntPower (есть ещё и с IntPower).
                                                                ExpandedWrap disabled
                                                                  ;##############################################
                                                                  ;##                                          ##
                                                                  ;##         -= FPU POWER FUNCTION =-         ##
                                                                  ;##       ФУНКЦИЯ ВОЗВЕДЕНИЯ В СТЕПЕНЬ       ##
                                                                  ;##                [ v1.00 ]                 ##
                                                                  ;##            MASM/TASM (16/32)             ##
                                                                  ;##                                          ##
                                                                  ;##   (c) 2017 by Jin X (jin.x@sources.ru)   ##
                                                                  ;##            http://xk7.ru/p/a/i           ##
                                                                  ;##                                          ##
                                                                  ;##############################################
                                                                   
                                                                  ;-- FPPower: функция возведения X в степень Y --------------------------------------------------------------------------
                                                                  ; Входные данные: st(0) = основание X, st(1) = показатель степени Y
                                                                  ; Результат: st(0) = st(0)^st(1) = X^Y (или 0 при ошибке), st(1) пуст; CF=NC=0 - успешный результат, CF=CY=1 - ошибка (при X<0 и нецелом Y)
                                                                  ; Функция изменяет AX, DX и флаги
                                                                   
                                                                  FPPower     proc
                                                                          ; Сравнение X с нулём
                                                                          ftst                ; st(0)=X=0 ?
                                                                          fstsw   ax
                                                                          mov dl,ah           ; dl and 1=1, если X<0 (используется в чистом FPU-расчёте и при проверке ошибки), иначе dl and 1=0
                                                                          sahf
                                                                          jz  short @@Zero        ; если X=0, результат=st(0)=0 (записываем чистый ноль - если допустим ноль со знаком, можно сделать прыжок на @@Free1AndExit); st(1)=Y; CF=NC=0
                                                                   
                                                                          ; Проверка Y на целое
                                                                          fld st(1)           ; st(0)=Y, st(1)=X, st(2)=Y
                                                                          frndint             ; st(0)=Round(st(0))=Round(Y), st(1)=X, st(2)=Y
                                                                          fcomp   st(2)           ; сравнение st(0) и st(2): Round(Y) и Y; st(0)=X, st(1)=Y, st(2) пуст
                                                                          fstsw   ax
                                                                          sahf
                                                                          jne short @@ErrChk      ; прыгаем, если Y нецелое
                                                                   
                                                                          ; Степень Y - целое число, проверяем знак X
                                                                          shr dl,1            ; проверяем знак X (dl and 1)
                                                                          jnc short @@PosX        ; прыгаем, если X>0 (dl=0)
                                                                   
                                                                          ; Проверка чётности целого Y (при X<0)
                                                                          fld1                ; st(0)=1, st(1)=X, st(2)=Y
                                                                          fld1                ; st(0)=st(1)=1, st(2)=X, st(3)=Y
                                                                          faddp               ; st(0)=2.0, st(1)=X, st(2)=Y
                                                                          fld st(2)           ; st(0)=Y, st(1)=2.0, st(2)=X, st(3)=Y
                                                                          fabs                ; st(0)=|Y|, st(1)=2.0, st(2)=X, st(3)=Y
                                                                          fprem               ; st(0)=|Y| mod 2.0 (если Y не чересчур большой, иначе st(0) будет содержать неверное значение, и тогда мы будем считать, что число чётное), st(1)=2.0, st(2)=X, st(3)=Y
                                                                          fld1                ; st(0)=1, st(1)=Y mod 2.0, st(2)=2.0, st(3)=X, st(4)=Y
                                                                          fcompp              ; сравнение st(0) и st(1): 1 и (Y mod 2.0) с удалением этих чисел; st(0)=2.0, st(1)=X, st(2)=Y, st(3) и st(4) пусты
                                                                          fstsw   ax
                                                                          shr ah,6            ; CF=ZF (т.к. нам надо получить CF=нечётный Y)
                                                                          sahf                ; CF=NC=0, если Y чётное; CF=CY=1, если Y нечётное
                                                                          fstp    st          ; удаляем 2.0; st(0)=X, st(1)=Y, st(2) пуст
                                                                          fabs                ; st(0)=|X|, st(1)=Y
                                                                      @@PosX: ; CF=NC=0, если dl and 1=0 (т.е. X>0, сюда был прыжок) или Y чётное
                                                                          fyl2x               ; st(0)=Y*log2(|X|)=Z,          st(1) пуст[st(0)=st(1)*log2(st(0))]
                                                                          fld st          ; st(0)=Z,              st(1)=Z
                                                                          frndint             ; st(0)=Round(Z),           st(1)=Z
                                                                          fxch                ; st(0)=Z,              st(1)=Round(Z)
                                                                          fsub    st,st(1)        ; st(0)=Z-Round(Z),         st(1)=Round(Z)
                                                                          f2xm1               ; st(0)=2^(E-Round(Z))-1,       st(1)=Round(Z)
                                                                          fld1                ; st(0)=1,              st(1)=2^(E-Round(Z))-1, st(2)=Round(Z)
                                                                          faddp   st(1),st        ; st(0)=st(0)+1 = 2^(E-Round(Z)),   st(1)=Round(Z),     st(2) пуст
                                                                          fscale              ; st(0)=2^(E-Round(Z))*2^Round(Z)=2^Z,  st(1)=Round(Z)  [st(0)=st(0)*2^RoundTowardZero(st(1))]
                                                                          ; st(0)=результат=X^Y
                                                                          jnc short @@Free1AndExit    ; прыгаем, если X>0, либо Y чётное
                                                                          fchs                ; иначе меняем знак результата
                                                                          clc             ; ошибок нет, CF=NC=0
                                                                      @@Free1AndExit:
                                                                          ffree   st(1)           ; удаляем лишнее значение st(1)=Round(Z) или st(1)=X; st(0)=результат
                                                                          ret
                                                                          ; Проверка ошибки (Y нецелое!)
                                                                      @@ErrChk:
                                                                          shr dl,1            ; проверяем знак X (dl and 1)
                                                                          jnc short @@PosX        ; прыгаем, если X>0 (dl=0; CF=NC=0)
                                                                          ; иначе ошибка (dl=1: X<0 и Y нецелое); CF=CY=1
                                                                      @@Zero:
                                                                          fldz                ; st(0)=0, st(1)=X, st(2)=Y
                                                                          ffree   st(2)           ; удаляем лишнее значение st(2)=Y
                                                                          jmp short @@Free1AndExit
                                                                  FPPower     endp
                                                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                                                0 пользователей:


                                                                Рейтинг@Mail.ru
                                                                [ Script execution time: 0,0778 ]   [ 16 queries used ]   [ Generated: 28.03.24, 18:01 GMT ]