Я пытаюсь написать простую библиотеку для работы с длинными целыми числами. Использование специальной оптимизации на основе встроенного c метода _addcarry_u64 64-битного компилятора дает неожиданный результат в следующем коде, если используется оптимизация. Версия отладки работает должным образом.
inline uint64_t addc(const uint64_t& value1, const uint64_t& value2, uint64_t& carry) noexcept
{
uint64_t result;
carry = _addcarry_u64(static_cast<uint8_t>(carry), value1, value2, &result);
return result;
}
template<typename native_t = uintmax_t>
class long_t
{
public:
static_assert(std::is_unsigned_v<native_t>, "unsigned long integer native type must be unsigned");
using native_array_t = std::array<native_t, 2>;
long_t() noexcept;
constexpr long_t(const long_t& that) noexcept = default;
constexpr long_t(long_t&& that) noexcept = default;
constexpr long_t(native_array_t digits) noexcept;
constexpr long_t(const native_t& value) noexcept;
template<typename type_t, std::enable_if_t<std::is_unsigned_v<type_t>, int> = 0>
constexpr long_t(type_t value) noexcept;
template<typename type_t, std::enable_if_t<std::is_signed_v<type_t>, int> = 0>
constexpr long_t(type_t value) noexcept;
constexpr bool operator==(const long_t& that) const noexcept;
constexpr long_t& operator+=(const long_t& that) noexcept;
constexpr long_t operator+(const long_t& that) const noexcept;
native_array_t digits;
};
template<typename native_t>
inline long_t<native_t>::long_t() noexcept
{
}
template<typename native_t>
inline constexpr long_t<native_t>::long_t(native_array_t digits) noexcept
: digits(digits)
{
}
template<typename native_t>
inline constexpr long_t<native_t>::long_t(const native_t& value) noexcept
: long_t({ value, 0})
{
}
template<typename native_t>
template<typename type_t, std::enable_if_t<std::is_unsigned_v<type_t>, int>>
inline constexpr long_t<native_t>::long_t(type_t value) noexcept
: long_t({ native_t(value), 0 })
{
}
template<typename native_t>
template<typename type_t, std::enable_if_t<std::is_signed_v<type_t>, int>>
inline constexpr long_t<native_t>::long_t(type_t value) noexcept
: long_t({ static_cast<native_t>(value), (value >= 0 ? 0 : native_t(~0)) })
{
}
template<typename native_t>
inline constexpr bool long_t<native_t>::operator==(const long_t& that) const noexcept
{
if (digits[1] != that.digits[1])
return false;
if (digits[0] != that.digits[0])
return false;
return true;
}
template<typename native_t>
inline constexpr long_t<native_t>& long_t<native_t>::operator+=(const long_t& that) noexcept
{
native_t carry = 0;
digits[0] = addc(digits[0], that.digits[0], carry);
digits[1] = addc(digits[1], that.digits[1], carry);
return *this;
}
template<typename native_t>
inline constexpr long_t<native_t> long_t<native_t>::operator+(const long_t& that) const noexcept
{
return long_t(*this) += that;
}
int main()
{
const bool result = long_t<uintmax_t>(0) + -1 == -1;
std::cout << "result:" << result;
return 0;
}
Результат:
result:0
Замена оптимизированной версии add c привела к предсказуемому результату:
template<typename type_t, std::enable_if_t<std::is_unsigned_v<type_t>, int>>
inline constexpr type_t addc(const type_t& value1, const type_t& value2, type_t& carry) noexcept
{
const type_t tmp = value2 + carry;
const type_t result = value1 + tmp;
carry = (tmp < value2) || (result < value1);
return result;
}
Результат:
result:1
Проблема проявляется в Microsoft Visual Studio 2017 версии 15.9.23 на 64-битном компиляторе C ++ в режиме полной оптимизации.