Как сделать двойной кусок добавить без неопределенного поведения?

StackOverflow https://stackoverflow.com/questions/3569424

  •  01-10-2019
  •  | 
  •  

Вопрос

РЕДАКТИРОВАТЬ Предупреждение о здравоохранении общественного здравоохранения - этот вопрос включает в себя ложное предположение о неопределенном поведении. См. Принятый ответ.

После чтения Последние блог Post., Я много думал о практичности избежать всех стандартов-неопределенных предположений в C и C ++ код. Вот фрагмент вырезан из C ++, чтобы сделать без знаки 128 бит ...

void c_UInt64_Pair::operator+= (const c_UInt64_Pair &p)
{
  m_Low  += p.m_Low;
  m_High += p.m_High;

  if (m_Low < p.m_Low)  m_High++;
}

Это четко зависит от предположений о переполнении поведения. Очевидно, что большинство машин могут поддерживать двоичное целое число правильного типа (хотя, возможно, построение из 32-битных кусков или что-то еще), но, по-видимому, растущему вероятному, что оптимизатор может использовать стандарты-неопределенное поведение здесь. То есть единственный способ, которым m_Low < p.m_Low Состояние может пройти, если m_Low += p.m_Low переполняет, что неопределенное поведение, поэтому оптимизатор может законно решать, что условие всегда не удается. В этом случае этот код просто сломан.

Вопрос в том, поэтому ...

Как вы можете написать достаточно эффективную версию вышеперечисленного, не полагаясь на неопределенное поведение?

Предположим, что у вас есть соответствующая 64-битная бинарная машина Integer, но у вас есть вредоносный компилятор, который всегда будет интерпретировать ваше неопределенное поведение в худшем случае возможным (или невозможным). Также предположим, что вы нет есть специальный встроенный, внутренний, библиотечный или что-то, что нужно сделать для вас.

РЕДАКТИРОВАТЬ Незначительное разъяснение - это не только об обнаружении переполнения, но и обеспечения того, чтобы оба M_LOW и M_HIGH заканчиваются правильными результатами Modulo 2 ^ 64, которые также являются стандартами - не определены.

Это было полезно?

Решение

От стандарта C ++ 1998 года, 3.9.1 (4): «целые числа unsigned, объявленные без знаки, должны соблюдать законы арифметического модуля 2 ^ n, где n представляет собой количество битов в представлении значения этого конкретного размера целогомента». Обратите внимание, что «целое число», здесь, ссылается на любой целочисленный тип, а не просто int.

Поэтому, предполагая, что это unsigned целые числа, такие как «uint64» в типе, предполагает, что это определенное поведение в C ++ и должно работать, как ожидалось.

Другие советы

Если вы хотите на самом деле эффективный метод, вам придется код в чем-то, кроме C или C ++. Для разумно Эффективно, вы должны убедиться, что переполнение никогда не бывает, а обнаружить и компенсировать, когда оно будет.

По сути, для каждого 64-битного компонента необходимо отдельно рассчитать дополнения, используя низкие 63 бита и самые высокие биты. Из этих отдельных расчетов вы можете отработать, что было 64-битным общем, и если произошло перенос.

Затем, когда вы делаете верхнюю 64-битное добавление, вы добавляете в несущуюся, если есть один. Если отслеживать результаты от этого, вы переполнены вашу 128-битную переменную, и вам нужно будет вызвать исключение, или иным образом обрабатывать дело.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top