Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: как перехватить переполнение (допустим для __int64) в С++  (Прочитано 26808 раз)
0 Пользователей и 1 Гость смотрят эту тему.
VladimirH
Гость
« : 11-09-2003 05:50 » 

как перехватить переполнение (допустим, конкретно для __int64, если это важно), используя С++ и, естественно, WinAPI.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #1 : 11-09-2003 07:38 » 

Что имеется ввиду - перехват exception???
Записан

А птичку нашу прошу не обижать!!!
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #2 : 11-09-2003 07:55 » 

Вот есть такой пример
Код:
Int add(int s, int y)

{

         if  ((x>0 && y>0 && x>INT_MAX-y)

                  || x<0 && y<0 && x<INT_MIN-y))

                  throw Int_overflow("+",x,y);

         return x+y;

}


void f()

{

         try {

                  int  i1 = add(1, 2);

                  int  i2 = add(INT_MAX, 2);

         }

         catch (Matherr& m) {

                  m.debug_print();

         }

}

переделай его под себя
« Последнее редактирование: 19-11-2007 18:24 от Алексей1153++ » Записан
VladimirH
Гость
« Ответ #3 : 11-09-2003 13:51 » 

От:  VladimirH
Кому: SlavaI
Добавлено: Чт Сен 11, 2003 7:00 am
Тема: как перехватить переполнение (допустим для __int64) в С++

Здравствуйте!

До сих пор я использовал следующее:

bool MulAndTest(__int64 op1, __int64 op2, __int64 &result)
{
        result = op1 * op2;
        if( op2 ) return ( op1 == result/op2 );
        return true;
}

void f()
{
        __int64 x=..., y=..., res;
        if( !MulAndTest(x, y, res) )
           ПЕРЕПОЛНЕНИЕ
}

Получив от Вас вариант, я, честно говоря, подумал, что он будет работать медленее. Однако, сравнение показало ( 5 запусков по 10,000,000 раз в циклах), что мой вариант медленее в среднем на 8,5%. Еще раз спасибо.
Однако, мне кажется, что если суметь добраться до бита переполнения регистра флагов процессора (надеюсь мне удалось выдать вразумительную фразу), то отпадет необходимость в выполнении обратного действия в функциях "add" и "MulAndTest", что и приведет к ускорению. Мне это пока что не удалось. Так что мне "слабо", а Вам... (шутка).

------------------------------------------------------------------------------------

От:  SlavaI
Кому: VladimirH
Добавлено: Чт Сен 11, 2003 7:59 am
Тема: Re: как перехватить переполнение (допустим для __int64) в С++ Ответить с цитатой

Я привел платформенно независмое решение, можно и с флагом переполнения поработать.
У IA-32 это флаг OF его можно проверять инструкцией JO (jump if overflow).

------------------------------------------------------------------------------------

От:  VladimirH
Кому: SlavaI
Добавлено: Чт Сен 11, 2003 8:48 am
Тема: Re: как перехватить переполнение (допустим для __int64) в С+

Я попробовал так:
1.
void fff()
{
}

void main()
{
        ...
        __int64 x=_I64_MAX, у=2, result;
        res = x * y;
        __asm
        {
        jo fff;
        }
        ...
}

и так

2.
void main()
{
        ...
        __int64 x=_I64_MAX, у=2, result;
        res = x * y;
        __asm
        {
        jo fff;
        }
        ...
        fff: что-то
}

и в обоих случаях инструкция "jo" не сработала (пробовал и "JO").

------------------------------------------------------------------------------------

От:  SlavaI
Кому: VladimirH
Добавлено: Чт Сен 11, 2003 9:04 am
Тема: Re: как перехватить переполнение (допустим для __int64) в С+ Ответить с цитатой

И правильно что не заработало. И не заработает по двум причинам.
1) _I64_MAX = 0x7FFFFFFFFFFFFFFF
Поэтому 2*_I64_MAX = 0xFFFFFFFFFFFFFFFE. Нет переполнения!
Просто _I64_MAX определена для знакового типа, когда старший разряд отвечает за знак. А интелловские процессоры показывают переполнение только для всех разрядов- они не различают знаковое число или нет при установке флага. Для знаковых есть флаг CF.

2) Не сработает по причине отсутствия 64 разрядных регистров, как там сделано сложение и умножение 64 разрядных целых чисел на 32 разрядном процессоре- это к производителю компиллятора, тут может быть поставлен флаг при переполнении, а может и нет, все зависит от способа перемножения 64 разрядных ЦЕЛЫХ чисел, примененным производителем компилятора.
Примененный тобой способ работает только для 32 разрядных чисел.

По приведенным двум причинам я сторонник платформенно независисмого решения, которое я приводил.

------------------------------------------------------------------------------------
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #4 : 11-09-2003 13:53 » 

Спасибо что выложил.
Это наша дискуссия в привате выложена на сайт. Может у кого еще какие предложения и дополнения будут.
Записан
VladimirH
Гость
« Ответ #5 : 11-09-2003 14:28 » 

Кстати, я пытался последовать рекомендациям Д. Рихтера ("Windows", издание 4-е, стр. 615) и в его примере заменил "case EXCEPTION_INT_DIVIDE_BY_ZERO:" на "case EXCEPTION_INT_OVERFLOW:". Тоже не сработало.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #6 : 11-09-2003 14:44 » 

Если это не срочно - то к завтрему я разгребусь и если не навалится что-либо уж очень тяжклое Улыбаюсь то я посмотрю.
Записан

А птичку нашу прошу не обижать!!!
VladimirH
Гость
« Ответ #7 : 11-09-2003 14:48 » 

Не срочно.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #8 : 11-09-2003 14:55 » 

Ок - значит завтра посмотрю.
Записан

А птичку нашу прошу не обижать!!!
NetRaider
Гость
« Ответ #9 : 11-09-2003 23:50 » 

Цитата: VladimirH
как перехватить переполнение (допустим, конкретно для __int64, если это важно), используя С++ и, естественно, WinAPI.


В С++ (для __int64) никак.
Для беззнаковых типов в языках C и C++ переполнений при вычислениях не бывает. Контроль переполнения для знаковых типов средствами языка неосуществим.
Записан
Kuzmich
Гость
« Ответ #10 : 12-09-2003 05:00 » 

Цитата: NetRaider

В С++ (для __int64) никак.
Для беззнаковых типов в языках C и C++ переполнений при вычислениях не бывает. Контроль переполнения для знаковых типов средствами языка неосуществим.

Ну что Вы, для проверки переполнения нужно всеголишь проверить флаг  переполнения OV. Как уже писалось это делается функцией jo, только вот с типами больше 32бита неработает (зависит от компилятора).
void main()
{
    int a=2147483647;
    int b=1;
    int r;
    int f=0;      // флаг переполнения

    r = a + b;
    _asm {
        jo  overflow   // переход по переполнению
        jmp endover;   // небыло переполнения
    overflow:      // переполнение
        mov dword ptr [f],1   // установим флаг
    endover:                  
    }
    if (f==1)
        cout << "Переполнение" << endl;
    else
        cout << "OK" << endl;
}
Проверил на VC6 работает, если необходимо проверить переполнение при работе с типами _int64, то тут без написания собственных asm функций не обойтись. Для беззнаковых типов переполнения дествительно не бывает (по крайней мере на VC6)
Записан
Anonymous
Гость
« Ответ #11 : 12-09-2003 07:36 » 

Очередная безуспешная попытка (взято у того же Д.Рихтера, стр. 645):

Cоздаем новый класс

#include <eh.h>
class CSE  
{
public:
   static void MapSEtoCE()   { _set_se_translator(TranslateSEtoCE); }
   operator DWORD()         { return(m_er.ExceptionCode); }
private:
   CSE(PEXCEPTION_POINTERS pep)   {   m_er = *pep->ExceptionRecord;
                                    m_context = *pep->ContextRecord; }
   static void _cdecl TranslateSEtoCE(UINT dwEC, PEXCEPTION_POINTERS pep)   { throw CSE(pep); }
private:
   EXCEPTION_RECORD m_er;
   CONTEXT   m_context;
};

В OnInitDialog() вызываем CSE::MapSEtoCE();.  Далее вроде бы должно быть ОК, но... в коде

   __int64   x=2,y=8,z=16,t=0,r1,r2,r3,r4;
   try
   {
      r1 = _I64_MAX * x;
      r2 = _I64_MAX * y;
      r3 = _I64_MAX * z;
      r4 = _I64_MAX / t;
   }
   catch( CSE se )
   {
      switch( se )
      {
      case EXCEPTION_INT_DIVIDE_BY_ZERO:
         r4 = 0;
         break;
      case   EXCEPTION_INT_OVERFLOW:
         r4 = 0;
         break;
      default :   throw;   break;
      }
   }

попадаем в catch только при делении на 0.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #12 : 12-09-2003 07:37 » 

Тут пробемка выяснилась , что проверит могу только под VC а делать что либо более обширное не удается...
Какая ОС больше нужна в данном случае?
Записан

А птичку нашу прошу не обижать!!!
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #13 : 12-09-2003 07:57 » 

Цитата

попадаем в catch только при делении на 0.


А ты чего хотел? Ну не будет переполнение показывать для 64 разрядов 32 разрядный процессор, так как действия с большими числами он напрямую не выполняет- компилятор генерит код для действий с большими числами посредством использования 32 разрядных регистров.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #14 : 12-09-2003 08:00 » 

Вобще то есть книга

Генри Уоррен "Алгоритмические трюки для программистов".
Издательство Вильямс.
http://www.williamspublishing.com/Books/5-8459-0471-4.html

http://www.ozon.ru/?context=detail&id=1458852&partner=williams&isbn=5-8459-0471-4

Там приводится куча способов для проверки на переполнение, арифметические операции над большими числами и т.д. Я ее полистал, немного почитал, но пересказывать не буду- лучше сами читайте, а то только запутаю.
Записан
VladimirH
Гость
« Ответ #15 : 12-09-2003 08:18 » 

Спасибо. Без сомнения книга полезна и будет интересно ее прочитать. Остается приобрести ее.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #16 : 12-09-2003 08:34 » 

Блин, я перепутал флаги CF и OF, давно не занимался программированием на на уровне процессора. На самом деле вот так: СF- для беззнаковых означает перенос из старшего разряда, а OF для знаковых вылезание за пределы, то есть результат изменил знак(изменился старший бит). Но тебя это не спасет(ты это уже с OF проверил- ничего не меняется), так как на 64 разрядные числа это не распространяется- у них своя арифметика на 32 разрядных процессорах.

Цитата

Остается приобрести ее.


Да она везде продается. Я на Савеловском купил(ну это если ты в Москве).
Записан
VladimirH
Гость
« Ответ #17 : 12-09-2003 08:46 » 

Пошел в книжный.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #18 : 12-09-2003 08:50 » 

Цитата

Пошел в книжный.


Правильно. Книга - источник знаний!
Только книга для чтения трудная. И нет гарантии отсутствия опечаток(все таки работа с битами- ошибиться при напечатывании текста легко)- будь внимателен.
Записан
VladimirH
Гость
« Ответ #19 : 12-09-2003 08:57 » 

Тогда может заменить форум списком литературы (надеюсь не обидел).
Хотя насчет необходимости чтения полностью с тобой согласен.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #20 : 12-09-2003 09:09 » 

Кстати вопрос - а если просто проверять значение данных в переменной и если происходит прибавление к макимально возможному числу, то тогда самому генерить код переполнения???
Будет намного медленее???
Записан

А птичку нашу прошу не обижать!!!
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #21 : 12-09-2003 09:13 » 

Цитата

Тогда может заменить форум списком литературы (надеюсь не обидел).


Я могу конечно заняться пересказом книги, но не хочется. Достать эту книгу пока не проблема.
Записан
SlavaI
Главный специалист

ru
Offline Offline

« Ответ #22 : 12-09-2003 09:19 » 

Цитата

Кстати вопрос - а если просто проверять значение данных в переменной и если происходит прибавление к макимально возможному числу, то тогда самому генерить код переполнения???


Так так и сделано впримерах, которые тут приведены.

Цитата

Будет намного медленее???


Медленней чего?
Я привел код с эксепшеном. Это стандартный прием никто сильно не жаловался на медленность. Все же зависит от частоты возникновения ситуации переполнения.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #23 : 12-09-2003 09:26 » 

Я и говорю, что если не делать exception а просто проверять тип операции, скажем ты делаешь + к значению, перед тем как это сделать проверить размер числа внутри переменной и если оно = максимуму в int64 то просто вернуть false или ошибку функции...

Т.е. заменить вызов exception....

О быстродействии говорю при проверках - как это делал ВладимирH
Записан

А птичку нашу прошу не обижать!!!
VladimirH
Гость
« Ответ #24 : 12-09-2003 10:09 » 

1. На счет "проверки перед прибавлением": заранее неизвестно сколько прибавляем, да еще есть умножение.
2. На счет "кода с эксепшеном": вопрос даже не в том, как часто происходит переполнение, а в том, что происходит постоянное выполнение обратной операции, что для вычитания(в случае сложения) еще куда ни шло, а для деления (в случае умножения)...? Так что если бы удалось проверять какой-то флаг или просто в catch-блоке обрабатывать переполнение, генерируемое не мною же (искусственно, используя обратную операцию), то эту дополнительную обратную операцию можно было-бы не выполнять. Работай себе либо с одним очень быстро выполняющимся "if", либо даже без него во втором варианте.
P.S. У меня барахлила связь, так что ответ подзадержался.
Записан
Гром
Птычк. Тьфу, птычник... Вот!
Готовлюсь к пенсии

il
Offline Offline
Пол: Мужской
Бодрый птах


« Ответ #25 : 12-09-2003 10:20 » 

Понятно - но как бы пока решения универсального для 64 битного числа я не нашел - остается предполагать - что можно проверять каждое увеличение на прдмет результата - распределяемого в два 32 разрядных числа - но в переменные 64 разрядов - тогда ожно отслеживать размер оставшегося, т.е. в знаковых числах отнимать от максимально возможного int64 значение первого передаваемого, а потом второго и смотрет на полученный резукльтат - тот должен быть больше 0.

Я вроде к тому, что в 32 разрядном вариаенте трудно угадать как будут работать различные процессоры, а при машиннонезависимой реализации лучшен оперировать цифрами...
Записан

А птичку нашу прошу не обижать!!!
VladimirH
Гость
« Ответ #26 : 12-09-2003 11:20 » 

Спасибо за советы. К сожалению, и сдесь не обходится без дополнительных действий. Но, как говорится "на нет и суда нет".
В оправдании того, что я занял у всех столько времени, скажу "за что воевал": вариант "SlavaI" (переработанный под умножение и в inline версии) по сравнению с вариантом "чистого" умножения (без каких-либо проверок) при отсутствии переполнения, в среднем (опять таки 5 запусков по 10,000,000 раз) - 175.5 наносекунды против 19.3 наносекунды на выполнение одной операции умножения.
Пошел искать Генри С. Уоррена.
Записан
NetRaider
Гость
« Ответ #27 : 12-09-2003 22:24 » 

Цитата: Kuzmich
Цитата: NetRaider

В С++ (для __int64) никак.
Для беззнаковых типов в языках C и C++ переполнений при вычислениях не бывает. Контроль переполнения для знаковых типов средствами языка неосуществим.

Ну что Вы, для проверки переполнения нужно всеголишь проверить флаг  переполнения OV. Как уже писалось это делается функцией jo, только вот с типами больше 32бита неработает (зависит от компилятора).


А Вы форумом часом не ошиблись? Нет в С++ никаких флагов OV.

Цитата

Для беззнаковых типов переполнения дествительно не бывает (по крайней мере на VC6)


Да не по крайней мере, а по определению - тип 'unsigned' использует арифметику по модулю(3.9.1)
Записан
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #28 : 13-09-2003 07:18 » 

не пойму вот что

а нельзя просто написать кусок кода на ассемблере, который возьмёт обе переменных за шкирку, сложит младшие 32 бита, затем с учётом флага переноса - старшие 32, а затем вернёт значение флага переноса, которое и покажет переполнение?

не знаю мнемокоманд для intel - нужно будет подучить, а в контроллерах только так (синтаксис адресации неверен - не помню)

Код:
;пусть регистр а - 32 битный , переменные v- 64 битные

ld      a, low v1    ; складаем младшие слова переменных .
add   a,low v2     ;
ld     v3,a            ;помещаем в младшее слово результата.
xra   a                ;обнуляем аккум.
addc a, high v1    ;складаем его с переносом и старшими словами.
add  a, high v2    ;
ld     (v3+4),a     ;кладём в старшее слово результата.
xra   a               ;значение флага переноса помещаем в a.
addc  a,0           ;

;теперь в a - перенос, дальше делаешь что надо

но может я и не прав
« Последнее редактирование: 19-11-2007 18:29 от Алексей1153++ » Записан

VladimirH
Гость
« Ответ #29 : 15-09-2003 08:57 » 

Цитата: Алексей1153
не пойму вот что

а нельзя просто написать кусок кода на ассемблере...


Для сложения вроде бы можно. (Не владея Ассемблером, не смог проверить.) А как с умножением? Факт переполнения так или иначе ловится. Но будет ли соответствующий код работать быстрее, чем функция MulAndTest или вариант с сatch-ем? Похоже тему форума я выбрал неудачно и надо было бы ее озаглавить так: "Достижение максимального быстродействия при контроле переполнения для типа данных __int64". С книгой "Алгоритмические трюки для программистов" еще не разобрался, но боюсь, что придется  мои пожелания оставить до перехода на 64-битный процессор. Всем спасибо.
Записан
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines