Functional NegBin implementaion
This commit is contained in:
commit
5d7f77b2e6
9 changed files with 332 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
cmake-build-*
|
||||||
|
.idea/
|
6
CMakeLists.txt
Normal file
6
CMakeLists.txt
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
cmake_minimum_required(VERSION 3.17)
|
||||||
|
project(Misc)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
|
||||||
|
add_subdirectory(NegBin)
|
8
NegBin/CMakeLists.txt
Normal file
8
NegBin/CMakeLists.txt
Normal file
|
@ -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)
|
158
NegBin/NegBin.cpp
Normal file
158
NegBin/NegBin.cpp
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
//
|
||||||
|
// Created by trotfunky on 04/11/2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#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<sizeof(long)*8;i++)
|
||||||
|
{
|
||||||
|
unsigned long temp_carry = carry >> 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<rhs;i++)
|
||||||
|
{
|
||||||
|
*this += unit;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
NegBin operator*(NegBin lhs, const int& rhs)
|
||||||
|
{
|
||||||
|
lhs *= rhs;
|
||||||
|
return lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
NegBin operator*(const int& lhs, NegBin rhs)
|
||||||
|
{
|
||||||
|
return rhs*lhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
NegBin NegBin::operator~() const
|
||||||
|
{
|
||||||
|
// Shifting to the right is equivalent to dividing by the base. As we are using base -2,
|
||||||
|
// multiplying by 2 and shifting to the right gives us the opposite of the number.
|
||||||
|
return (*this+*this) >> 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<sizeof(long)*8;i++)
|
||||||
|
{
|
||||||
|
result += ((value >> 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<long>(number);
|
||||||
|
return ostream;
|
||||||
|
}
|
57
NegBin/NegBin.h
Normal file
57
NegBin/NegBin.h
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
//
|
||||||
|
// Created by trotfunky on 04/11/2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef MISC_NEGBIN_H
|
||||||
|
#define MISC_NEGBIN_H
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
/// Bitset the size of a long, for convenience
|
||||||
|
typedef std::bitset<sizeof(long)*8> 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
|
68
NegBin/NegBinBenchmark.cpp
Normal file
68
NegBin/NegBinBenchmark.cpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
//
|
||||||
|
// Created by trotfunky on 06/11/2020.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <random>
|
||||||
|
#include "NegBin.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
std::random_device rd;
|
||||||
|
std::default_random_engine gen(rd());
|
||||||
|
|
||||||
|
std::uniform_int_distribution<> distribution(0,2147483647);
|
||||||
|
|
||||||
|
std::vector<NegBin> randoms1;
|
||||||
|
std::vector<NegBin> 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<std::chrono::nanoseconds>(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<long> randomsInts1;
|
||||||
|
std::vector<long> 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<std::chrono::nanoseconds>(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;
|
||||||
|
}
|
6
NegBin/README.md
Normal file
6
NegBin/README.md
Normal file
|
@ -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 !
|
20
NegBin/main.cpp
Normal file
20
NegBin/main.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#include <iostream>
|
||||||
|
#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;
|
||||||
|
}
|
7
README.md
Normal file
7
README.md
Normal file
|
@ -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 ?
|
Loading…
Add table
Reference in a new issue