1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
|
/**
* Decimal field type for AMQP
*
* @copyright 2014 - 2020 Copernica BV
*/
/**
* Include guard
*/
#pragma once
/**
* Dependencies
*/
#include <cmath>
#include <ostream>
#include "field.h"
#include "outbuffer.h"
#include "inbuffer.h"
/**
* Set up namespace
*/
namespace AMQP {
/**
* Class implementation
*/
class DecimalField : public Field
{
/**
* To preserve precision the decision is made to work with the places and number.
* These values are sent in the framedata, so no precision will be lost.
* Other options, such as floats, doubles, Decimal32 etc result in loss of precision
* and this is something which is not acceptable.
*
* Only (in)equality and assignment operators are implemented since the decimalfield
* is not supposed to be altered.
* e.q. ==, != and =
*
* When requesting the value of this object there are 3 choices;
* float, double or DecimalField
* e.g. valueFloat(), valueDouble() and value()
*/
private:
/**
* The number of places, which means the number of decimals
* e.g. number = 1234, places = 2, true value is 12.34
*/
uint8_t _places;
/**
* The number without the decimals
*/
uint32_t _number;
protected:
/**
* Write encoded payload to the given buffer.
*/
virtual void fill(OutBuffer& buffer) const override
{
// encode fields
buffer.add(_places);
buffer.add(_number);
}
public:
/**
* Construct decimal field
*
* @param places the number of places
* @param number the integer number
*/
DecimalField(uint8_t places = 0, uint32_t number = 0) :
_places(places),
_number(number)
{}
/**
* Construct based on incoming data
* @param frame
*/
DecimalField(InBuffer &frame)
{
_places = frame.nextUint8();
_number = frame.nextUint32();
}
/**
* Destructor
*/
virtual ~DecimalField() {}
/**
* Create a new identical instance of this object
* @return unique_ptr
*/
virtual std::unique_ptr<Field> clone() const override
{
return std::unique_ptr<Field>(new DecimalField(_places, _number));
}
/**
* Output the object to a stream
* @param std::ostream
*/
virtual void output(std::ostream &stream) const override
{
// output floating point value
stream << "decimal(" << _number / pow(10.0f, _places) << ")";
}
/**
* Assign a new value
*
* @param value new value for field
* @return DecimalField
*/
DecimalField& operator=(const DecimalField& value)
{
// if it's the same object, skip assignment and just return *this
if (this == &value) return *this;
// not the same object, copy values to this object.
_places = value._places;
_number = value._number;
// allow chaining
return *this;
}
/**
* Casts decimalfield to double
* e.g. "double x = decimalfield" will work
*
* @return double value of decimalfield in double format
*/
virtual operator double() const override
{
return _number / pow(10.0f, _places);
}
/**
* Casts decimalfield to float
* e.g. "float x = decimalfield" will work
*
* @return float value of decimalfield in float format
*/
virtual operator float() const override
{
return static_cast<float>(_number / pow(10.0f, _places));
}
/**
* Check for equality between this and another DecimalField
*
* @param value value to be checked for equality
* @return boolean whether values are equal
*/
bool operator==(const DecimalField& value) const
{
// check if everything is the same
// precision is taken into account, e.q. 1.0 != 1.00
// meaning number:10, places:1 is not equal to number:100, places:2
return _number == value.number() && _places == value.places();
}
/**
* Check for inequality between this and another DecimalField
*
* @param value value to be checked for inequality
* @return boolean whether values are inequal
*/
bool operator!=(const DecimalField& value) const
{
return !(*this == value);
}
/**
* Get the size this field will take when
* encoded in the AMQP wire-frame format
*/
virtual size_t size() const override
{
// the sum of all fields
return 5;
}
/**
* Get the number of places
* @return uint8_t
*/
uint8_t places() const
{
return _places;
}
/**
* Get the number without decimals
* @return uint32_t
*/
uint32_t number() const
{
return _number;
}
/**
* Return the DecimalField
* To preserve precision DecimalField is returned, containing the number and places.
* @return return DecimalField
*/
DecimalField value() const
{
return *this;
}
/**
* We are a decimal field
*
* @return true, because we are a decimal field
*/
bool isDecimal() const override
{
return true;
}
/**
* Get the type ID that is used to identify this type of
* field in a field table
*/
virtual char typeID() const override
{
return 'D';
}
};
/**
* end namespace
*/
}
|