Re[3]: Проверка выполнения сложения
От: rg45 СССР  
Дата: 11.04.12 19:47
Оценка:
Здравствуйте, netch80, Вы писали:

N>Поэтому простейший вариант анализа на переполнение без поддержки аппаратуры выглядит примерно так:


N>
N>int checked_add(int a, int b) {
N>  int sum = a + b;
N>  int ssd = a ^ b; // на самом деле нам нужен только старший бит
N>  if ((ssd >= 0) && ((ssd ^ sum) < 0))
N>    throw integer_overflow();
N>  return sum;
N>}
N>


Не находишь, что мой вариант
Автор: rg45
Дата: 10.04.12
проще и универсальнее? Универсальнее в том плане, что годится и для знаковых и для беззнаковых типов.

P.S. Предложил еще позавчера утром, никто и внимания не обратил, обидно
--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: Проверка выполнения сложения
От: MasterZiv СССР  
Дата: 12.04.12 06:15
Оценка:
> А без знака там, увы, никакого автомата нет.

Вот странно. Чем беззнаковые им так не угодили ?
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Проверка выполнения сложения
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 12.04.12 06:27
Оценка: 1 (1) +1
Здравствуйте, rg45, Вы писали:

N>>Поэтому простейший вариант анализа на переполнение без поддержки аппаратуры выглядит примерно так:

[...]

R>Не находишь, что мой вариант
Автор: rg45
Дата: 10.04.12
проще и универсальнее? Универсальнее в том плане, что годится и для знаковых и для беззнаковых типов.


Загнал в автоматический тест — вроде да, отклонений не замечено. Проверка такого рода будет работать.

С другой стороны, надо ещё измерить, какой из этих вариантов эффективнее в каком случае. Визуально для человека твой однозначно проще. Но мне кажется, что для ARM, например, мой соберётся в более короткую и прямую последовательность. Также у тебя проверка на (y>0) избыточна для беззнаковых; если заменить на >= в обеих сторонах, то левая для беззнаковых вырождается в true и экономится операция сравнения, но компилятор может пожаловаться на condition is always true.

R>P.S. Предложил еще позавчера утром, никто и внимания не обратил, обидно


Почему не заметил — не знаю. Видимо, слишком увлёкся опровержением.
Поставил +1 и 2.

Интересно, почему MS в своём SafeInt не использует такие простые приёмы. Вместо этого, например, для двух 32-разрядных чисел они складывают используя 64 бита (причём это даже при компиляции в 32-битный код) и проверяют результат на выход за границы. И вообще у них пример того, как не надо писать на C++...
The God is real, unless declared integer.
integer overflow
Re[4]: Проверка выполнения сложения
От: MasterZiv СССР  
Дата: 12.04.12 06:31
Оценка: :)
> P.S. Предложил еще позавчера утром, никто и внимания не обратил, обидно

Почему ? Я обратил, хороший вариант. (тебе легче ?)
Posted via RSDN NNTP Server 2.1 beta
Re[7]: Проверка выполнения сложения
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 12.04.12 06:50
Оценка:
Здравствуйте, MasterZiv, Вы писали:

>> А без знака там, увы, никакого автомата нет.


MZ>Вот странно. Чем беззнаковые им так не угодили ?


Наверно, решили, что для них и ручной проверки будет достаточно — написать BC 12, label после команды. А ещё, что имеет смысл отделить целевые команды от вспомогательных, которые в основном адресные и потому им переполнение не так важно, а автоматическая проверка проверяет или не то, или не так.

А вообще учти, что эта разработка зафиксировалась в 1964-м году, когда ещё теория была очень слаба. Я не могу сказать, в какой машине и когда была придуманы операции типа ADC и универсальная считалочка NZVC, но до неё не было варианта делать знаковые и беззнаковые операции унифицированно. Подозреваю, что она появилась с PDP-11 (1970-й год), но фактов у меня нет.
Что они после этого не ввели более привычную нам арифметику — не удивляюсь, если родная пусть странно, но работает. Вообще там система команд существенно не менялась, даже таких мелких реформ, как в amd64 по отношению к push/pop, не делалось; можно на ходу переключаться между режимами адресации на 24/31/64 бита без шлюзов, а возможность работы с 64-битными данными AFAIR зависит от модели, но не от режима.
В S/360 и потомках нет аналога ADC (сложение двух операндов и флага переноса), его надо эмулировать. Зато отдельные команды для знаковой арифметики с автодетектом переполнения.

P.S. Кажется, я один из немногих вообще на RSDN, кто эту линию (S/360...S/390) знает и в чём-то любит

P.S.[2]. Я бы вообще сделал что-то вроде того, как сейчас в стандарте IEEE754 — залипающий (sticky) флаг переполнения (в дополнение к обычному).
The God is real, unless declared integer.
Re[5]: Проверка выполнения сложения
От: rg45 СССР  
Дата: 12.04.12 07:35
Оценка: :)
Здравствуйте, MasterZiv, Вы писали:


>> P.S. Предложил еще позавчера утром, никто и внимания не обратил, обидно


MZ>Почему ? Я обратил, хороший вариант. (тебе легче ?)


Вот теперь легче
--
Справедливость выше закона. А человечность выше справедливости.
Re[5]: Проверка выполнения сложения
От: rg45 СССР  
Дата: 12.04.12 08:23
Оценка:
Здравствуйте, netch80, Вы писали:

N>Также у тебя проверка на (y>0) избыточна для беззнаковых; если заменить на >= в обеих сторонах, то левая для беззнаковых вырождается в true и экономится операция сравнения...


В-о-от! Я это чуть позже тоже сообразил, но уже не стал заморачиваться исправлением. Действительно, если a + b = c, где a, b, c — беззнаковые, то выражение: b >= 0 == c >= a оптимизатор с радостью упростит до c >= a

N>Интересно, почему MS в своём SafeInt не использует такие простые приёмы. Вместо этого, например, для двух 32-разрядных чисел они складывают используя 64 бита (причём это даже при компиляции в 32-битный код) и проверяют результат на выход за границы. И вообще у них пример того, как не надо писать на C++...


Да набирают туда непонятно кого
--
Справедливость выше закона. А человечность выше справедливости.
Re[2]: Улучшенный вариант
От: rg45 СССР  
Дата: 12.04.12 08:37
Оценка:
Здравствуйте, rg45, Вы писали:

А>>Например, нам необходимо сложить две переменные типа int, результат сложения которых будет приводить к переполнению. Что можно сделать?


Улучшенный вариант:

R>
R>template<typename T>
R>T add (T a, T b)
R>{
R>  T c = a + b;
R>  assert(b >= 0 == c >= a);
R>  return c;  
R>}
R>


Теперь если T — беззнаковый тип, то выражение b >= 0 == c >= a оптимизатор сможет упростить до c >= a
--
Справедливость выше закона. А человечность выше справедливости.
Re[5]: Проверка выполнения сложения
От: B0FEE664  
Дата: 12.04.12 08:49
Оценка:
Здравствуйте, netch80, Вы писали:

N>Интересно, почему MS в своём SafeInt не использует такие простые приёмы. Вместо этого, например, для двух 32-разрядных чисел они складывают используя 64 бита (причём это даже при компиляции в 32-битный код) и проверяют результат на выход за границы. И вообще у них пример того, как не надо писать на C++...


А что не так с SafeInt ? (кроме того, что они используют 64 бита для 32-разрядных чисел)
И каждый день — без права на ошибку...
Re[7]: Проверка выполнения сложения
От: watch-maker  
Дата: 12.04.12 09:03
Оценка:
Здравствуйте, netch80, Вы писали:

N>Интересно, чем определяется название функции? Потому что на FreeBSD/i386 мне в таком же случае затребовали __addvsi3. Я правильно понимаю, что __addvdi3 это для двойной длины (64 бита)?

Про буквы SI и DI можно прочитать в Machine Modes. И похоже что именно название и есть причина сломанности на 64-х битной архитектуре, ибо на amd64 нужно в этом случае вызывать SI версию функции, а не DI.
Но вообще, это же служебная функция компилятора, по идее не важно как она называется. Если бы она работала, то и писать бы её не пришлось.
Да, проверял на FreeBSD/amd64 компиляторами gcc от 4.2 до 4.7.
Re[3]: Улучшенный вариант
От: Lorenzo_LAMAS  
Дата: 12.04.12 09:09
Оценка: 15 (2) +2
Не понял, вроде до сих пор есть в стандарте:

If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.


R>>
R>>template<typename T>
R>>T add (T a, T b)
R>>{
R>>  T c = a + b;//если это знаковые целые? 
R>>  assert(b >= 0 == c >= a);
R>>  return c;  
R>>}
R>>
Of course, the code must be complete enough to compile and link.
Re[4]: Улучшенный вариант
От: rg45 СССР  
Дата: 12.04.12 09:18
Оценка:
Здравствуйте, Lorenzo_LAMAS, Вы писали:

L_L>Не понял, вроде до сих пор есть в стандарте:


L_L>

L_L>If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.


R>>>
R>>>template<typename T>
R>>>T add (T a, T b)
R>>>{
R>>>  T c = a + b;//если это знаковые целые? 
R>>>  assert(b >= 0 == c >= a);
R>>>  return c;  
R>>>}
R>>>


Строго говоря, это UB. Но в качестве отладочного диагностического средства для конкретных платформ такое решение может приносить пользу. Повышение надежности работы программ как частный случай неопределенного поведения, о как!
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: Улучшенный вариант
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 12.04.12 09:19
Оценка: +1
Здравствуйте, Lorenzo_LAMAS, Вы писали:

L_L>Не понял, вроде до сих пор есть в стандарте:


L_L>

L_L>If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.


Это в стандарте.
А реальная практика показывает, что на 99.99% встреченных установок сложение целых в C и C++ выполняется как сложение двух чисел в "дополнении до 2" с игнорированием всех переполнений.

Кстати, именно поэтому непонятно, почему стандарт до сих пор не ввёл средства идентификации таких типичных реализаций. Почему-то куча макросов для возможностей stdatomic или fenv — есть, а такой базы — нет.

Недавно тут видел упоминание ещё одного случая — сдвигов знаковых целых. Опять же формально не определено, что при этом будет. Но практика где-то в таком же количестве случаев использует один и тот же метод.
The God is real, unless declared integer.
Re[5]: Улучшенный вариант
От: Lorenzo_LAMAS  
Дата: 12.04.12 09:23
Оценка:
N>Это в стандарте.
N>А реальная практика показывает, что на 99.99% встреченных установок сложение целых в C и C++ выполняется как сложение двух чисел в "дополнении до 2" с игнорированием всех переполнений.

на самом деле, я не дочитал вопрос автора топика он хочет "оптимальнее", так что да, можно игнорировать UB (на бумаге) и т.д.
Of course, the code must be complete enough to compile and link.
Re[8]: Проверка выполнения сложения
От: MasterZiv СССР  
Дата: 12.04.12 12:56
Оценка:
> А вообще учти, что эта разработка зафиксировалась в 1964-м году, когда ещё
> теория была очень слаба.

Какая теория слаба ?


> P.S. Кажется, я один из немногих вообще на RSDN, кто эту линию (S/360...S/390)

> знает и в чём-то любит

Ну я когда-то работал на наших аналогах 360. Ассемблер соотв. учили и писали на
нём в школе. Был курсовик. Вот только не помню уже, что я писал.
Posted via RSDN NNTP Server 2.1 beta
Re[6]: Проверка выполнения сложения
От: MasterZiv СССР  
Дата: 12.04.12 13:01
Оценка:
> MZ>Почему ? Я обратил, хороший вариант. (тебе легче ?)
>
> Вот теперь легче

Я рад
Posted via RSDN NNTP Server 2.1 beta
Re[7]: Проверка выполнения сложения
От: Lieh_Tzu  
Дата: 12.04.12 14:08
Оценка:
Здравствуйте, MasterZiv, Вы писали:


>> MZ>Почему ? Я обратил, хороший вариант. (тебе легче ?)

>>
>> Вот теперь легче

MZ>Я рад


Я бы рекомендовал использовать участок ассемблерного кода:

asm
{
MOV EAx, x
ADD EAx, y
JC OverflowLabel
}
...
OverflowLabel:
...

Поскольку инструкция процессора ADD выполняет операцию сложения и устанавливает флаг переноса/переполнения.

Смотрим документацию Интел:

"The ADD instruction performs integer addition. It evaluates the result for both signed
and unsigned integer operands and sets the OF and CF flags to indicate a carry (overflow)
in the signed or unsigned result, respectively. The SF flag indicates the sign of
the signed result."
Re[6]: Проверка выполнения сложения
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 12.04.12 18:28
Оценка: 8 (2)
Здравствуйте, B0FEE664, Вы писали:

BFE>А что не так с SafeInt ? (кроме того, что они используют 64 бита для 32-разрядных чисел)


Ну с моей точки зрения оно зверски переусложнено.
Хотя компиляторы сейчас с этой сложностью справляются. gcc, например, вообще при уровнях начиная с -O1 выкинул все промежуточные шаблонные методы и оставил голый код:

   SafeInt<int> a, b;
   <...>
   a += b;


дало такое сложение:

 804857a:       8d 04 1e                lea    (%esi,%ebx,1),%eax
 804857d:       39 c6                   cmp    %eax,%esi
 804857f:       0f 9e c2                setle  %dl
 8048582:       c1 eb 1f                shr    $0x1f,%ebx
 8048585:       38 da                   cmp    %bl,%dl
 8048587:       74 45                   je     80485ce <main+0xde>


Это я подпилил на сравнение по методу rg45 вместо прежней конверсии к int64 и выходу за границы:

    template < typename E >
    static void AdditionThrow( const T& lhs, const U& rhs, T& result )
    {
        // 32-bit or less - one or both are signed
        __int32 x = (__int32)lhs;
        __int32 y = (__int32)rhs;
        __int32 tmp = x + y;

        if( (y >= 0) == (tmp >= x))
        {
            result = (T)tmp;
            return;
        }
        
        E::SafeIntOnOverflow();


Упрощение получилось невероятное
Так что беру заявление обратно — оно сейчас такое вполне сойдёт.
Осталось вычистить чисто алгоритмические неадекватности.
The God is real, unless declared integer.
Re[8]: Проверка выполнения сложения
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 13.04.12 06:06
Оценка:
Здравствуйте, Lieh_Tzu, Вы писали:

L_T>Я бы рекомендовал использовать участок ассемблерного кода:


1) Баян, я уже это писал в данной теме.
2) Hint: уже давно ARM не менее интересен, чем x86 а есть и другие платформы.
The God is real, unless declared integer.
Re[7]: Проверка выполнения сложения
От: rg45 СССР  
Дата: 13.04.12 10:32
Оценка:
Здравствуйте, netch80, Вы писали:

N>Это я подпилил на сравнение по методу rg45 вместо прежней конверсии к int64 и выходу за границы:


N>
N>    template < typename E >
N>    static void AdditionThrow( const T& lhs, const U& rhs, T& result )
N>    {
N>        // 32-bit or less - one or both are signed
N>        __int32 x = (__int32)lhs;
N>        __int32 y = (__int32)rhs;
N>        __int32 tmp = x + y;

N>        if( (y >= 0) == (tmp >= x))
N>        {
N>            result = (T)tmp;
N>            return;
N>        }
        
N>        E::SafeIntOnOverflow();
N>


Гм, а зачем приводить слагаемые к __int32? А что будет в случае, если T — беззнаковый 32-битный тип? В результате интерпретации беззнакового типа как знакового возможны две ошибочные ситуации:
  1. мы "не замечаем" беззнаковое переполнение. Пример: складываем 0xFFFFFFFFU и 1U, переполнение налицо. Но при интерпретации этих слагаемых как знаковых, получим -1 + 1 = 0, т.е. все Ok;
  2. выдаем "ложное срабатывание". Пример: складываем 0x7FFFFFFFU и 1U — для беззнаковых чисел это нормально, переполнения нет. Но при интерпретации этих слагаемых как знаковых, придем к ошибочному заключению, что было переполнение.
--
Справедливость выше закона. А человечность выше справедливости.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.