mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-30 19:30:17 +00:00
285 lines
9.0 KiB
C++
285 lines
9.0 KiB
C++
//
|
|
// Storage.hpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 10/07/2016.
|
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#ifndef Storage_hpp
|
|
#define Storage_hpp
|
|
|
|
#include "../NumberTheory/Factors.hpp"
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <limits>
|
|
|
|
namespace Storage {
|
|
|
|
/*!
|
|
Contains either an absolute time or a time interval, described as a quotient, in terms of a
|
|
clock rate to which the time is relative and its length in cycles based on that clock rate.
|
|
*/
|
|
struct Time {
|
|
unsigned int length, clock_rate;
|
|
Time() : length(0), clock_rate(1) {}
|
|
Time(unsigned int unsigned_int_value) : length(unsigned_int_value), clock_rate(1) {}
|
|
Time(int int_value) : Time((unsigned int)int_value) {}
|
|
Time(unsigned int length, unsigned int clock_rate) : length(length), clock_rate(clock_rate) {}
|
|
Time(int length, int clock_rate) : Time((unsigned int)length, (unsigned int)clock_rate) {}
|
|
Time(uint64_t length, uint64_t clock_rate) {
|
|
install_result(length, clock_rate);
|
|
}
|
|
Time(float value) {
|
|
install_float(value);
|
|
}
|
|
|
|
/*!
|
|
Reduces this @c Time to its simplest form — eliminates all common factors from @c length
|
|
and @c clock_rate.
|
|
*/
|
|
void simplify() {
|
|
unsigned int common_divisor = NumberTheory::greatest_common_divisor(length, clock_rate);
|
|
length /= common_divisor;
|
|
clock_rate /= common_divisor;
|
|
}
|
|
|
|
/*!
|
|
@returns the floating point conversion of this @c Time. This will often be less precise.
|
|
*/
|
|
inline float get_float() const {
|
|
return (float)length / (float)clock_rate;
|
|
}
|
|
|
|
inline unsigned int get_unsigned_int() const {
|
|
return length / clock_rate;
|
|
}
|
|
|
|
inline bool operator < (const Time &other) const {
|
|
return (uint64_t)other.clock_rate * (uint64_t)length < (uint64_t)clock_rate * (uint64_t)other.length;
|
|
}
|
|
|
|
inline bool operator <= (const Time &other) const {
|
|
return (uint64_t)other.clock_rate * (uint64_t)length <= (uint64_t)clock_rate * (uint64_t)other.length;
|
|
}
|
|
|
|
inline bool operator > (const Time &other) const {
|
|
return (uint64_t)other.clock_rate * (uint64_t)length > (uint64_t)clock_rate * (uint64_t)other.length;
|
|
}
|
|
|
|
inline bool operator >= (const Time &other) const {
|
|
return (uint64_t)other.clock_rate * (uint64_t)length >= (uint64_t)clock_rate * (uint64_t)other.length;
|
|
}
|
|
|
|
inline bool operator == (const Time &other) const {
|
|
return (uint64_t)other.clock_rate * (uint64_t)length == (uint64_t)clock_rate * (uint64_t)other.length;
|
|
}
|
|
|
|
inline Time operator + (const Time &other) const {
|
|
if(!other.length) return *this;
|
|
|
|
uint64_t result_length;
|
|
uint64_t result_clock_rate;
|
|
if(clock_rate == other.clock_rate) {
|
|
result_length = (uint64_t)length + (uint64_t)other.length;
|
|
result_clock_rate = clock_rate;
|
|
} else {
|
|
result_length = (uint64_t)length * (uint64_t)other.clock_rate + (uint64_t)other.length * (uint64_t)clock_rate;
|
|
result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.clock_rate;
|
|
}
|
|
return Time(result_length, result_clock_rate);
|
|
}
|
|
|
|
inline Time &operator += (const Time &other) {
|
|
if(!other.length) return *this;
|
|
if(!length) {
|
|
*this = other;
|
|
return *this;
|
|
}
|
|
|
|
uint64_t result_length;
|
|
uint64_t result_clock_rate;
|
|
if(clock_rate == other.clock_rate) {
|
|
result_length = (uint64_t)length + (uint64_t)other.length;
|
|
result_clock_rate = (uint64_t)clock_rate;
|
|
} else {
|
|
result_length = (uint64_t)length * (uint64_t)other.clock_rate + (uint64_t)other.length * (uint64_t)clock_rate;
|
|
result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.clock_rate;
|
|
}
|
|
install_result(result_length, result_clock_rate);
|
|
return *this;
|
|
}
|
|
|
|
inline Time operator - (const Time &other) const {
|
|
if(!other.length) return *this;
|
|
|
|
uint64_t result_length;
|
|
uint64_t result_clock_rate;
|
|
if(clock_rate == other.clock_rate) {
|
|
result_length = (uint64_t)length - (uint64_t)other.length;
|
|
result_clock_rate = clock_rate;
|
|
} else {
|
|
result_length = (uint64_t)length * (uint64_t)other.clock_rate - (uint64_t)other.length * (uint64_t)clock_rate;
|
|
result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.clock_rate;
|
|
}
|
|
return Time(result_length, result_clock_rate);
|
|
}
|
|
|
|
inline Time operator -= (const Time &other) {
|
|
if(!other.length) return *this;
|
|
|
|
uint64_t result_length;
|
|
uint64_t result_clock_rate;
|
|
if(clock_rate == other.clock_rate) {
|
|
result_length = (uint64_t)length - (uint64_t)other.length;
|
|
result_clock_rate = (uint64_t)clock_rate;
|
|
} else {
|
|
result_length = (uint64_t)length * (uint64_t)other.clock_rate - (uint64_t)other.length * (uint64_t)clock_rate;
|
|
result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.clock_rate;
|
|
}
|
|
install_result(result_length, result_clock_rate);
|
|
return *this;
|
|
}
|
|
|
|
inline Time operator * (const Time &other) const {
|
|
uint64_t result_length = (uint64_t)length * (uint64_t)other.length;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.clock_rate;
|
|
return Time(result_length, result_clock_rate);
|
|
}
|
|
|
|
inline Time &operator *= (const Time &other) {
|
|
uint64_t result_length = (uint64_t)length * (uint64_t)other.length;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.clock_rate;
|
|
install_result(result_length, result_clock_rate);
|
|
return *this;
|
|
}
|
|
|
|
inline Time operator * (unsigned int multiplier) const {
|
|
uint64_t result_length = (uint64_t)length * (uint64_t)multiplier;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate;
|
|
return Time(result_length, result_clock_rate);
|
|
}
|
|
|
|
inline Time &operator *= (unsigned int multiplier) {
|
|
uint64_t result_length = (uint64_t)length * (uint64_t)multiplier;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate;
|
|
install_result(result_length, result_clock_rate);
|
|
return *this;
|
|
}
|
|
|
|
inline Time operator / (const Time &other) const {
|
|
uint64_t result_length = (uint64_t)length * (uint64_t)other.clock_rate;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.length;
|
|
return Time(result_length, result_clock_rate);
|
|
}
|
|
|
|
inline Time &operator /= (const Time &other) {
|
|
uint64_t result_length = (uint64_t)length * (uint64_t)other.clock_rate;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate * (uint64_t)other.length;
|
|
install_result(result_length, result_clock_rate);
|
|
return *this;
|
|
}
|
|
|
|
inline Time operator / (unsigned int divisor) const {
|
|
uint64_t result_length = (uint64_t)length;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate * (uint64_t)divisor;
|
|
return Time(result_length, result_clock_rate);
|
|
}
|
|
|
|
inline Time &operator /= (unsigned int divisor) {
|
|
uint64_t result_length = (uint64_t)length;
|
|
uint64_t result_clock_rate = (uint64_t)clock_rate * (uint64_t)divisor;
|
|
install_result(result_length, result_clock_rate);
|
|
return *this;
|
|
}
|
|
|
|
inline void set_zero() {
|
|
length = 0;
|
|
clock_rate = 1;
|
|
}
|
|
|
|
inline void set_one() {
|
|
length = 1;
|
|
clock_rate = 1;
|
|
}
|
|
|
|
static Time max() {
|
|
return Time(std::numeric_limits<unsigned int>::max());
|
|
}
|
|
|
|
private:
|
|
inline void install_result(uint64_t long_length, uint64_t long_clock_rate) {
|
|
if(long_length <= std::numeric_limits<unsigned int>::max() && long_clock_rate <= std::numeric_limits<unsigned int>::max()) {
|
|
length = (unsigned int)long_length;
|
|
clock_rate = (unsigned int)long_clock_rate;
|
|
return;
|
|
}
|
|
|
|
// TODO: switch to appropriate values if the result is too large or small to fit, even with trimmed accuracy.
|
|
if(!long_length) {
|
|
length = 0;
|
|
clock_rate = 1;
|
|
return;
|
|
}
|
|
|
|
while(!(long_length&0xf) && !(long_clock_rate&0xf)) {
|
|
long_length >>= 4;
|
|
long_clock_rate >>= 4;
|
|
}
|
|
|
|
while(!(long_length&1) && !(long_clock_rate&1)) {
|
|
long_length >>= 1;
|
|
long_clock_rate >>= 1;
|
|
}
|
|
|
|
if(long_length > std::numeric_limits<unsigned int>::max() || long_clock_rate > std::numeric_limits<unsigned int>::max()) {
|
|
uint64_t common_divisor = NumberTheory::greatest_common_divisor(long_length, long_clock_rate);
|
|
long_length /= common_divisor;
|
|
long_clock_rate /= common_divisor;
|
|
|
|
// Okay, in desperation accept a loss of accuracy.
|
|
while(
|
|
(long_length > std::numeric_limits<unsigned int>::max() || long_clock_rate > std::numeric_limits<unsigned int>::max()) &&
|
|
(long_clock_rate > 1)) {
|
|
long_length >>= 1;
|
|
long_clock_rate >>= 1;
|
|
}
|
|
}
|
|
|
|
if(long_length <= std::numeric_limits<unsigned int>::max() && long_clock_rate <= std::numeric_limits<unsigned int>::max()) {
|
|
length = (unsigned int)long_length;
|
|
clock_rate = (unsigned int)long_clock_rate;
|
|
} else {
|
|
length = std::numeric_limits<unsigned int>::max();
|
|
clock_rate = 1u;
|
|
}
|
|
}
|
|
|
|
inline void install_float(float value) {
|
|
int exponent;
|
|
float mantissa = frexpf(value, &exponent);
|
|
float loaded_mantissa = ldexpf(mantissa, 24);
|
|
|
|
uint64_t result_length;
|
|
uint64_t result_clock_rate;
|
|
if(exponent < 0) {
|
|
int right_shift = -exponent;
|
|
result_length = (uint64_t)loaded_mantissa >> right_shift;
|
|
result_clock_rate = 1;
|
|
} else {
|
|
if(exponent <= 24) {
|
|
result_length = (uint64_t)loaded_mantissa;
|
|
result_clock_rate = 1 << (24 - exponent);
|
|
} else {
|
|
result_length = std::numeric_limits<uint64_t>::max();
|
|
result_clock_rate = 1;
|
|
}
|
|
}
|
|
install_result(result_length, result_clock_rate);
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif /* Storage_h */
|