Скажем, у каждого есть 8 неподписанных символов x1,x2,...x8
и мы хотим рассчитать:
abs((x1 + x2 + x3 + x4) - (x5 + x6 + x7 + x8)) / 4
Каков наилучший способ обеспечить наиболее точные результаты, не допуская больших переполнений или ошибок переполнения?
Я использую это в шаблонном классе, поэтому я не могу просто конвертировать неподписанные значения в подписанные.
operator +
работает только для int
и больше. Таким образом, когда вы используете его с объектами типа char
(который меньше, чем int) эти значения автоматически преобразуются в int до того, как произойдет операция.
таким образом
abs((x1 + x2 + x3 + x4) - (x5 + x6 + x7 + x8)) / 4
Преобразуется компилятором в:
abs((static_cast<int>(x1) + static_cast<int>(x2) + static_cast<int>(x3) + static_cast<int>(x4)) -
(static_cast<int>(x5) + static_cast<int>(x6) + static_cast<int>(x7) + static_cast<int>(x8))) / 4
Поэтому, если вы не добавляете много символов, вы вряд ли переполнитесь.
Существует проблема с присвоением результата обратно unsigned char
, Если результат выражения отрицателен, то у вас будет преобразование, которое делает значение положительным (но четко определенным).
Похоже, вы хотите, чтобы метафункция сообщала вам, какой промежуточный тип данных использовать в ваших вычислениях.
template <class T>
struct arithmetic_type
{
typedef std::int64_t type;
};
template <>
struct arithmetic_type<float>
{
typedef double type;
};
template <>
struct arithmetic_type<double>
{
typedef double type;
};
template <>
struct arithmetic_type<std::uint64_t>
{
typedef std::uint64_t type;
};
typedef typename arithmetic_type<T>::type ar_type;
abs(((ar_type)x1 + x2 + x3 + x4) - ((ar_type)x5 + x6 + x7 + x8)) / 4;
Конечно, вы можете настроить специализацию и добавлять / удалять в соответствии с вашими потребностями, но это должно дать вам правильное представление.
Как и с любыми данными фиксированного размера, лучший подход — привести их к типу, достаточно большому, чтобы соответствовать сценарию наихудшего случая. В этом случае приведение их к int
будет достаточно хорошим — он будет соответствовать диапазону любых возможных значений и позволит обрабатывать потенциальные потери.
Обратите внимание, что вы должны быть осторожны с вычитанием — результат будет зависеть от семантики, которую вы хотите прикрепить к нему: либо вы предполагаете, что оно никогда не будет недооценено (и любое отрицательное значение является ошибкой, либо должно быть равно 0), либо отрицательным Значение имеет значение, и вы хотите извлечь из него абсолютное значение.