From 5d7f77b2e633677bf9d678dbc7f75c35ed604ebf Mon Sep 17 00:00:00 2001 From: Teo-CD Date: Fri, 6 Nov 2020 22:34:02 +0100 Subject: [PATCH] Functional NegBin implementaion --- .gitignore | 2 + CMakeLists.txt | 6 ++ NegBin/CMakeLists.txt | 8 ++ NegBin/NegBin.cpp | 158 +++++++++++++++++++++++++++++++++++++ NegBin/NegBin.h | 57 +++++++++++++ NegBin/NegBinBenchmark.cpp | 68 ++++++++++++++++ NegBin/README.md | 6 ++ NegBin/main.cpp | 20 +++++ README.md | 7 ++ 9 files changed, 332 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 NegBin/CMakeLists.txt create mode 100644 NegBin/NegBin.cpp create mode 100644 NegBin/NegBin.h create mode 100644 NegBin/NegBinBenchmark.cpp create mode 100644 NegBin/README.md create mode 100644 NegBin/main.cpp create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2617d2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cmake-build-* +.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..03ca505 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +cmake_minimum_required(VERSION 3.17) +project(Misc) + +set(CMAKE_CXX_STANDARD 17) + +add_subdirectory(NegBin) \ No newline at end of file diff --git a/NegBin/CMakeLists.txt b/NegBin/CMakeLists.txt new file mode 100644 index 0000000..155794c --- /dev/null +++ b/NegBin/CMakeLists.txt @@ -0,0 +1,8 @@ +add_executable(NegBin main.cpp NegBin.cpp NegBin.h) + +target_compile_options(NegBin PRIVATE -Wall -Wpedantic -Wextra) +target_link_options(NegBin PRIVATE -static -static-libgcc -static-libstdc++) + +add_executable(NegBinBenchmark NegBinBenchmark.cpp NegBin.cpp NegBin.h) + +target_compile_options(NegBinBenchmark PRIVATE -O3) \ No newline at end of file diff --git a/NegBin/NegBin.cpp b/NegBin/NegBin.cpp new file mode 100644 index 0000000..479bc76 --- /dev/null +++ b/NegBin/NegBin.cpp @@ -0,0 +1,158 @@ +// +// Created by trotfunky on 04/11/2020. +// + +#include +#include "NegBin.h" + + +NegBin::NegBin(longBitField binaryRepresentation) : + value(binaryRepresentation.to_ulong()) +{} + +NegBin::NegBin(long decimalValue) : value(0) +{ + unsigned int currentPower = 0; + decimalValue *= -1; + // This works by checking the remainder by -2, giving the lowest bit of the converted number. + // Then, shift to the right to drop the bit we just checked and start again until every set bit has been checked. + // The sign flips each time as we are in base -2 and not 2. + // Miiight be UDB... + while (decimalValue != 0) + { + value |= std::abs(decimalValue%-2) << currentPower; + decimalValue = -decimalValue >> 1; + currentPower++; + } +} + +NegBin& NegBin::operator+=(const NegBin& rhs) +{ + unsigned long carry = 0; + + for(unsigned int i = 0;i> i; + unsigned long temp_rhs = rhs.value >> i; + unsigned long temp_lhs = this->value >> i; + + // Nothing remaining to add + if (!(temp_rhs | temp_carry)) break; + + // Both carry and right hand side are 1 so we need to add a carry + if (temp_carry & temp_rhs & 1) + { + // If the carry is already set for this bit, it needs to be cleared for this bit and the next + if (temp_carry >> 1 & 1) + { + carry &= ~(0b11 << i); + } + else + { + // Else add a two bit carry + carry |= 0b11 << (i+1); + } + } + // If either of them are set, add to lhs, otherwise don't bother + else if ((temp_carry ^ temp_rhs) & 1) + { + // If a carry was already set for the next bit too, discard both carries and the current bit + if (temp_lhs & temp_carry & 1 && temp_carry >> 1 & 1) + { + carry &= ~(0b11 << i); + this->value &= ~(0b1 << i); + } + else if (temp_lhs & 1) + { + // If the current bit was set, clear and add a two bit carry + carry |= 0b11 << (i+1); + this->value &= ~(0b1 << i); + } + else + { + this->value |= 0b1 << i; + } + } + } + return *this; +} + +NegBin operator+(NegBin lhs, const NegBin& rhs) +{ + lhs += rhs; + return lhs; +} + +NegBin& NegBin::operator-=(const NegBin& rhs) +{ + // lhs - rhs <=> lhs + (-rhs) + *this += ~rhs; + return *this; +} + +NegBin operator-(NegBin lhs, const NegBin& rhs) +{ + lhs -= rhs; + return lhs; +} + +NegBin& NegBin::operator*=(const int& rhs) +{ + auto unit = *this; + for (int i=0;i> 1; +} + +NegBin NegBin::operator>>(const int& rhs) +{ + this->value >>= rhs; + return *this; +} + +NegBin NegBin::operator<<(const int& rhs) +{ + this->value <<= rhs; + return *this; +} + +NegBin::operator long() const +{ + long result = 0; + for(unsigned int i = 0;i> i) & 1)*std::pow(-2,i); + } + return result; +} + +NegBin operator ""_nb(unsigned long long number) +{ + return NegBin(longBitField(number)); +} + +std::ostream& operator<<(std::ostream& ostream, const NegBin& number) +{ + ostream << static_cast(number); + return ostream; +} diff --git a/NegBin/NegBin.h b/NegBin/NegBin.h new file mode 100644 index 0000000..edb0066 --- /dev/null +++ b/NegBin/NegBin.h @@ -0,0 +1,57 @@ +// +// Created by trotfunky on 04/11/2020. +// + +#ifndef MISC_NEGBIN_H +#define MISC_NEGBIN_H + +#include +#include + +/// Bitset the size of a long, for convenience +typedef std::bitset longBitField; + +/*** + * Represents a number in base negative two. + * For ex. : 0b1 is 1, 0b10 is -2, 0b1010 is 6. + */ +class NegBin { +public: + NegBin() = default; + + /// Create a NegBin with a specific binary representation + explicit NegBin(longBitField binaryRepresentation); + /// Converts from long to NegBin. For ex 3 would set value to 0b111 + explicit NegBin(long number); + + NegBin& operator+=(const NegBin& rhs); + // Uses += and ~ to do a subtraction + NegBin& operator-=(const NegBin& rhs); + NegBin& operator*=(const int& rhs); + /// Used to obtain the binary representation of value's opposite + NegBin operator~() const; + + /// Shifts the underlying binary representation + NegBin operator>>(const int& rhs); + NegBin operator<<(const int& rhs); + + friend NegBin operator+(NegBin lhs, const NegBin& rhs); + friend NegBin operator-(NegBin lhs, const NegBin& rhs); + // Doubled, for symmetry + friend NegBin operator*(NegBin lhs, const int& rhs); + friend NegBin operator*(const int& lhs, NegBin rhs); + + /// Converts from NegBin to signed long, converting to unsigned would not make sense + explicit operator long() const; + +private: + /// Binary description of the number. + unsigned long value; // TODO : Maybe use bitset everywhere ? +}; + +/// Define literal to create NegBin numbers. Calls the NegBin::NegBin(longBitField) constructor +NegBin operator "" _nb(unsigned long long); + +std::ostream& operator<<(std::ostream& ostream, const NegBin& number); + +#endif //MISC_NEGBIN_H diff --git a/NegBin/NegBinBenchmark.cpp b/NegBin/NegBinBenchmark.cpp new file mode 100644 index 0000000..e5d4e21 --- /dev/null +++ b/NegBin/NegBinBenchmark.cpp @@ -0,0 +1,68 @@ +// +// Created by trotfunky on 06/11/2020. +// + +#include +#include +#include +#include "NegBin.h" + +int main() +{ + std::random_device rd; + std::default_random_engine gen(rd()); + + std::uniform_int_distribution<> distribution(0,2147483647); + + std::vector randoms1; + std::vector randoms2; + randoms1.reserve(1000000); + randoms2.reserve(1000000); + + for (int i = 0;i<1000000;i++) + { + randoms1.emplace_back(longBitField(distribution(gen))); + randoms2.emplace_back(longBitField(distribution(gen))); + } + + NegBin temp; + + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0;i<1000000;i++) + { + temp = randoms1.at(i) + randoms2.at(i); + } + auto end = std::chrono::high_resolution_clock::now(); + + auto total_duration = std::chrono::duration_cast(end-start); + + std::cout << "Total time taken for 1 000 000 iterations is :" << total_duration.count()/1000 << "µs" << std::endl; + std::cout << "Time per addition is roughly : " << total_duration.count()/1000000 << "ns" << std::endl; + + std::vector randomsInts1; + std::vector randomsInts2; + randomsInts1.reserve(1000000); + randomsInts2.reserve(1000000); + + for (int i = 0;i<1000000;i++) + { + randomsInts1.push_back(distribution(gen)); + randomsInts2.push_back(distribution(gen)); + } + + long tempInt; + + auto startInts = std::chrono::high_resolution_clock::now(); + for (int i = 0;i<1000000;i++) + { + tempInt = randomsInts1.at(i) * randomsInts2.at(i); + } + auto endInts = std::chrono::high_resolution_clock::now(); + + auto total_int_duration = std::chrono::duration_cast(endInts-startInts); + + std::cout << "Total time taken for 1 000 000 iterations is :" << total_int_duration.count()/1000 << "µs" << std::endl; + std::cout << "Time per addition is roughly : " << total_int_duration.count()/1000000 << "ns" << std::endl; + + return 0; +} \ No newline at end of file diff --git a/NegBin/README.md b/NegBin/README.md new file mode 100644 index 0000000..a14688a --- /dev/null +++ b/NegBin/README.md @@ -0,0 +1,6 @@ +# NegBin + +Implements a class handling numbers in base -2. + +Currently only the `+` operation is specific, all other operations (`-`, `~`, `*` with a scalar) are based on this one. +The implementation is quite naive and a bit slow but it works (~30x slower than integer addition on my machine). It can also convert to and from decimal ! \ No newline at end of file diff --git a/NegBin/main.cpp b/NegBin/main.cpp new file mode 100644 index 0000000..765ba59 --- /dev/null +++ b/NegBin/main.cpp @@ -0,0 +1,20 @@ +#include +#include "NegBin.h" + +int main() +{ + NegBin a = 0b11011_nb; + NegBin b = 0b10010_nb; + + std::cout << a << "-" << b << " = " << a-b << std::endl; + std::cout << a << "*" << 5 << " = " << a*5 << std::endl; + + auto c = NegBin(6); + auto d = NegBin(-12); + + std::cout << "6 is " << c << std::endl; + std::cout << "-12 is " << d << std::endl; + + + return 0; +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..45b5fc4 --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +# A bunch of stuff + +This repository holds a bunch of small C++ projects which might not deserve their own repository. + +Here is a list and summary of each of them : + + - [NegBin](NegBin) : What if binary, but base -2 instead of 2 ? \ No newline at end of file