Skip to content

Commit

Permalink
feat(add): Uint support (#6)
Browse files Browse the repository at this point in the history
* add unit

* update README desc
  • Loading branch information
guuzaa authored Feb 14, 2024
1 parent 21bb932 commit e69ce86
Show file tree
Hide file tree
Showing 8 changed files with 994 additions and 35 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ numbers

- **Signed Integers**: Int8 Int16 Int32 Int64

- **Unsigned Integers**: Uint8 Uint16 Uint32 Uint64 (not support yet)
- **Unsigned Integers**: Uint8 Uint16 Uint32 Uint64

## How to use

Expand Down
2 changes: 1 addition & 1 deletion examples/main.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#include <iostream>
#include <tuple>

#include "integer.hh"
#include "numbers.h"

void checked_sub_example() {
std::cout << "==== checked_sub_example ==== \n";
Expand Down
15 changes: 1 addition & 14 deletions src/include/integer.hh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <optional>
#include <type_traits>

template <typename T, typename = std::enable_if_t<std::is_signed<T>::value>>
template <typename T, typename = std::enable_if_t<std::is_signed_v<T>>>
class Integer {
public:
constexpr static T MIN = std::numeric_limits<T>::min();
Expand Down Expand Up @@ -268,17 +268,4 @@ class Integer {
T num_;
};

using Int8 = Integer<int8_t>;
using Int16 = Integer<int16_t>;
using Int32 = Integer<int32_t>;
using Int64 = Integer<int64_t>;

Integer<int8_t> operator"" _i8(unsigned long long val) { return Integer<int8_t>(val); }

Integer<int16_t> operator"" _i16(unsigned long long val) { return Integer<int16_t>(val); }

Integer<int32_t> operator"" _i32(unsigned long long val) { return Integer<int32_t>(val); }

Integer<int64_t> operator"" _i64(unsigned long long val) { return Integer<int64_t>(val); }

#endif
33 changes: 33 additions & 0 deletions src/include/numbers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef HEADER_NUMBERS_H
#define HEADER_NUMBERS_H

#include "integer.hh"
#include "uinteger.hh"

using Int8 = Integer<int8_t>;
using Int16 = Integer<int16_t>;
using Int32 = Integer<int32_t>;
using Int64 = Integer<int64_t>;

Integer<int8_t> operator"" _i8(unsigned long long val) { return Integer<int8_t>(val); }

Integer<int16_t> operator"" _i16(unsigned long long val) { return Integer<int16_t>(val); }

Integer<int32_t> operator"" _i32(unsigned long long val) { return Integer<int32_t>(val); }

Integer<int64_t> operator"" _i64(unsigned long long val) { return Integer<int64_t>(val); }

using Uint8 = Uinteger<uint8_t>;
using Uint16 = Uinteger<uint16_t>;
using Uint32 = Uinteger<uint32_t>;
using Uint64 = Uinteger<uint64_t>;

Uint8 operator"" _u8(unsigned long long val) { return Uint8(val); }

Uint16 operator"" _u16(unsigned long long val) { return Uint16(val); }

Uint32 operator"" _u32(unsigned long long val) { return Uint32(val); }

Uint64 operator"" _u64(unsigned long long val) { return Uint64(val); }

#endif
206 changes: 201 additions & 5 deletions src/include/uinteger.hh
Original file line number Diff line number Diff line change
@@ -1,13 +1,209 @@
#ifndef HEADER_UNINTEGER_H
#define HEADER_UNINTEGER_H

#include <iostream>
#include <limits>
#include <optional>
#include <type_traits>

template <typename T, typename = std::enable_if_t<std::is_unsigned<T>::value>>
template <typename T, typename = std::enable_if_t<std::is_unsigned_v<T>>>
class Uinteger {
public:
// unimplemented!
public:
constexpr static T MIN = std::numeric_limits<T>::min();
constexpr static T MAX = std::numeric_limits<T>::max();

Uinteger() noexcept : num_{} {}
Uinteger(T num) noexcept : num_{num} {}

constexpr Uinteger operator+(const Uinteger<T> &other) noexcept(false) {
if (add_overflow(num_, other.num_)) {
throw std::runtime_error("add overflow");
}
return Uinteger(num_ + other.num_);
}

constexpr Uinteger wrapping_add(const Uinteger<T> &other) noexcept { return Uinteger(num_ + other.num_); }

constexpr std::optional<Uinteger> checked_add(const Uinteger<T> &other) noexcept {
if (add_overflow(num_, other.num_)) {
return {};
}
return Uinteger(num_ + other.num_);
}

constexpr std::tuple<Uinteger<T>, bool> overflowing_add(const Uinteger<T> &other) noexcept {
return {Uinteger(num_ + other.num_), add_overflow(num_, other.num_)};
}

constexpr Uinteger<T> saturating_add(const Uinteger<T> &other) noexcept {
if (add_overflow(num_, other.num_)) {
return Uinteger(MAX);
}
return Uinteger(num_ + other.num_);
}

constexpr Uinteger operator-(const Uinteger<T> &other) noexcept(false) {
if (sub_overflow(num_, other.num_)) {
throw std::runtime_error("sub overflow");
}
return Uinteger(num_ - other.num_);
}

constexpr Uinteger wrapping_sub(const Uinteger<T> &other) noexcept { return Uinteger(num_ - other.num_); }

constexpr std::optional<Uinteger> checked_sub(const Uinteger<T> &other) noexcept {
if (sub_overflow(num_, other.num_)) {
return {};
}
return Uinteger(num_ - other.num_);
}

constexpr std::tuple<Uinteger<T>, bool> overflowing_sub(const Uinteger<T> &other) noexcept {
return {Uinteger(num_ - other.num_), sub_overflow(num_, other.num_)};
}

constexpr Uinteger saturating_sub(const Uinteger<T> &other) noexcept {
if (sub_overflow(num_, other.num_)) {
return MIN;
}
return Uinteger(num_ - other.num_);
}

constexpr Uinteger operator/(const Uinteger<T> &other) {
if (div_overflow(num_, other.num_)) {
throw std::runtime_error("div overflow");
}
return Uinteger(num_ / other.num_);
}

constexpr Uinteger wrapping_div(const Uinteger<T> &other) noexcept(false) { return Uinteger(num_ / other.num_); }

constexpr std::optional<Uinteger> checked_div(const Uinteger<T> &other) noexcept {
if (div_overflow(num_, other.num_)) {
return {};
}
return Uinteger(num_ / other.num_);
}

constexpr std::tuple<Uinteger<T>, bool> overflowing_div(const Uinteger<T> &other) noexcept {
return {Uinteger(num_ / other.num_), div_overflow(num_, other.num_)};
}

constexpr Uinteger saturating_div(const Uinteger<T> &other) noexcept {
if (div_overflow(num_, other.num_)) {
return MIN;
}
return Uinteger(num_ / other.num_);
}

constexpr Uinteger operator*(const Uinteger<T> &other) {
if (mul_overflow(num_, other.num_)) {
throw std::runtime_error("mul overflow");
}
return Uinteger(num_ * other.num_);
}

constexpr Uinteger wrapping_mul(const Uinteger<T> &other) noexcept(false) { return Uinteger(num_ * other.num_); }

constexpr std::optional<Uinteger> checked_mul(const Uinteger<T> &other) noexcept {
if (mul_overflow(num_, other.num_)) {
return {};
}
return Uinteger(num_ * other.num_);
}

constexpr std::tuple<Uinteger<T>, bool> overflowing_mul(const Uinteger<T> &other) noexcept {
return {Uinteger(num_ * other.num_), mul_overflow(num_, other.num_)};
}

constexpr Uinteger saturating_mul(const Uinteger<T> &other) noexcept {
if (mul_overflow(num_, other.num_)) {
return MAX;
}
return Uinteger(num_ * other.num_);
}

constexpr Uinteger operator-() const noexcept(false) {
if (num_ == MIN) {
return Uinteger(num_);
}
throw std::runtime_error("neg overflow");
}

constexpr std::optional<Uinteger> checked_neg() noexcept {
if (num_ == MIN) {
return Uinteger(MIN);
}
return {};
}

constexpr std::tuple<Uinteger, bool> overflowing_neg() noexcept {
if (num_ == MIN) {
return {Uinteger(MIN), false};
}
return {Uinteger(-num_), true};
}

constexpr Uinteger wrapping_neg() noexcept {
if (num_ == MIN) {
return Uinteger(MIN);
}
return Uinteger(-num_);
}

constexpr bool operator==(const Uinteger<T> &other) const { return num_ == other.num_; }
constexpr bool operator==(const T &other) const { return num_ == other; }

constexpr bool operator<(const Uinteger<T> &other) const { return num_ < other.num_; }
constexpr bool operator<(const T &other) const { return num_ < other; }

constexpr bool operator>(const Uinteger<T> &other) const { return num_ > other.num_; }
constexpr bool operator>(const T &other) const { return num_ > other; }

// prefix ++
constexpr Uinteger &operator++() {
if (add_overflow(num_, 1)) {
throw std::runtime_error("prefix ++ overflow");
}
num_ += 1;
return *this;
}

// postfix ++
constexpr Uinteger operator++(int) {
if (add_overflow(num_, 1)) {
throw std::runtime_error("postfix ++ overflow");
}
Uinteger<T> tmp = *this;
num_ += 1;
return tmp;
}

template <typename U, typename = std::enable_if<std::is_convertible_v<T, U>>>
explicit operator Uinteger<U>() const {
return Uinteger<U>(static_cast<U>(num_));
}

explicit operator T() const { return num_; }

friend std::ostream &operator<<(std::ostream &os, const Uinteger<T> &num) {
os << num.num_;
return os;
}

private:
constexpr bool add_overflow(T a, T b) const { return b > MAX - a; }

constexpr bool sub_overflow(T minuend, T subtrahend) const { return minuend < subtrahend; }

constexpr bool div_overflow(T a, T b) const { return false; }

constexpr bool mul_overflow(T a, T b) const {
T res;
return __builtin_mul_overflow(a, b, &res);
}

private:
T num_;
};
};

#endif
13 changes: 2 additions & 11 deletions test/integer/integer_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#include <vector>
#include "gtest/gtest.h"

#include "integer.hh"
#include "numbers.h"
#include "test/utils.hh"

TEST(IntegerTest, IntegerTemplate) {
Expand Down Expand Up @@ -363,10 +363,6 @@ TEST(IntegerTest, IntegerCheckedSub) {
auto ret = num.checked_sub(1);
ASSERT_EQ(ret.value(), Int16(n - 1));
}

Int16 num = Int16::MIN;
auto ret = num.checked_sub(1);
ASSERT_EQ(ret, std::nullopt);
}

TEST(IntegerTest, IntegerCheckedSubNoSideEffects) {
Expand All @@ -387,7 +383,7 @@ TEST(IntegerTest, IntegerOverflowingSub) {
Int16 num1 = Int16::MIN;
auto [ret, flag] = num1.overflowing_sub(num);
ASSERT_TRUE(flag);
ASSERT_EQ(ret, Int16::MIN - n);
ASSERT_EQ(ret, Int16::MIN - n);
}

for (int16_t n = std::numeric_limits<int16_t>::max(); n > std::numeric_limits<int16_t>::min(); n--) {
Expand All @@ -396,11 +392,6 @@ TEST(IntegerTest, IntegerOverflowingSub) {
ASSERT_FALSE(flag);
ASSERT_EQ(ret, Int16(n - 1));
}

Int16 num = Int16::MIN;
auto [ret, flag] = num.overflowing_sub(1);
ASSERT_TRUE(flag);
ASSERT_EQ(ret, Int16::MIN - 1);
}

TEST(IntegerTest, IntegerOverflowingSubNoSideEffects) {
Expand Down
2 changes: 1 addition & 1 deletion test/uinteger/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ add_executable(
${test_files}
)

target_link_libraries(${PROJECT_NAME} PRIVATE gtest gmock_main)
target_link_libraries(${PROJECT_NAME} PRIVATE test_utils gtest gmock_main)

gtest_discover_tests(${PROJECT_NAME}
EXTRA_ARGS
Expand Down
Loading

0 comments on commit e69ce86

Please sign in to comment.