Is there a 24Bit primitive integral datatype in C++?
If there is none, would it be possible to create a class int24 (, u开发者_Go百科int24 ) ?
Its purpose could be:
- manipulating soundfiles in 24 bit format
- manipulating bitmapdata without alphachannel
Depending on the requirements I'd use a bitfield for it.
struct int24{
unsigned int data : 24;
};
Or, if a separation is easier, just use 3 bytes (chars).
Btw, both use cases you mention in the question generally use 32bit integers. In the case of audio processing you'll generally convert to 32 bit ints (or floats, preferably, to prevent overflow situations you'd get with fixed point or integer math) when loading in chunks of audio because you're not going to have the entire file in memory at once.
For image data, people just tend to use 32 bit integers and ignore the alpha 8 alpha bits all together, or if you're dealing with a tightly packed format you're probably better of just manipulating them as char-pointers anyway because you'll have all channels separate. It's going to be a performance/memory trade-off anyway because writing one int is generally faster than three chars separately; however it will take 25% more memory.
Packing structs like this is compiler specific. However, in Visual Studio you'd do the following to make the struct exactly 24 bits.
#pragma pack(push, 1)
struct int24{
unsigned int data : 24;
};
#pragma pack(pop)
I wrote this to help me with audio manipulation. Its not the fastest but it works for me :)
const int INT24_MAX = 8388607;
class Int24
{
protected:
unsigned char m_Internal[3];
public:
Int24()
{
}
Int24( const int val )
{
*this = val;
}
Int24( const Int24& val )
{
*this = val;
}
operator int() const
{
if ( m_Internal[2] & 0x80 ) // Is this a negative? Then we need to siingn extend.
{
return (0xff << 24) | (m_Internal[2] << 16) | (m_Internal[1] << 8) | (m_Internal[0] << 0);
}
else
{
return (m_Internal[2] << 16) | (m_Internal[1] << 8) | (m_Internal[0] << 0);
}
}
operator float() const
{
return (float)this->operator int();
}
Int24& operator =( const Int24& input )
{
m_Internal[0] = input.m_Internal[0];
m_Internal[1] = input.m_Internal[1];
m_Internal[2] = input.m_Internal[2];
return *this;
}
Int24& operator =( const int input )
{
m_Internal[0] = ((unsigned char*)&input)[0];
m_Internal[1] = ((unsigned char*)&input)[1];
m_Internal[2] = ((unsigned char*)&input)[2];
return *this;
}
/***********************************************/
Int24 operator +( const Int24& val ) const
{
return Int24( (int)*this + (int)val );
}
Int24 operator -( const Int24& val ) const
{
return Int24( (int)*this - (int)val );
}
Int24 operator *( const Int24& val ) const
{
return Int24( (int)*this * (int)val );
}
Int24 operator /( const Int24& val ) const
{
return Int24( (int)*this / (int)val );
}
/***********************************************/
Int24 operator +( const int val ) const
{
return Int24( (int)*this + val );
}
Int24 operator -( const int val ) const
{
return Int24( (int)*this - val );
}
Int24 operator *( const int val ) const
{
return Int24( (int)*this * val );
}
Int24 operator /( const int val ) const
{
return Int24( (int)*this / val );
}
/***********************************************/
/***********************************************/
Int24& operator +=( const Int24& val )
{
*this = *this + val;
return *this;
}
Int24& operator -=( const Int24& val )
{
*this = *this - val;
return *this;
}
Int24& operator *=( const Int24& val )
{
*this = *this * val;
return *this;
}
Int24& operator /=( const Int24& val )
{
*this = *this / val;
return *this;
}
/***********************************************/
Int24& operator +=( const int val )
{
*this = *this + val;
return *this;
}
Int24& operator -=( const int val )
{
*this = *this - val;
return *this;
}
Int24& operator *=( const int val )
{
*this = *this * val;
return *this;
}
Int24& operator /=( const int val )
{
*this = *this / val;
return *this;
}
/***********************************************/
/***********************************************/
Int24 operator >>( const int val ) const
{
return Int24( (int)*this >> val );
}
Int24 operator <<( const int val ) const
{
return Int24( (int)*this << val );
}
/***********************************************/
Int24& operator >>=( const int val )
{
*this = *this >> val;
return *this;
}
Int24& operator <<=( const int val )
{
*this = *this << val;
return *this;
}
/***********************************************/
/***********************************************/
operator bool() const
{
return (int)*this != 0;
}
bool operator !() const
{
return !((int)*this);
}
Int24 operator -()
{
return Int24( -(int)*this );
}
/***********************************************/
/***********************************************/
bool operator ==( const Int24& val ) const
{
return (int)*this == (int)val;
}
bool operator !=( const Int24& val ) const
{
return (int)*this != (int)val;
}
bool operator >=( const Int24& val ) const
{
return (int)*this >= (int)val;
}
bool operator <=( const Int24& val ) const
{
return (int)*this <= (int)val;
}
bool operator >( const Int24& val ) const
{
return (int)*this > (int)val;
}
bool operator <( const Int24& val ) const
{
return (int)*this < (int)val;
}
/***********************************************/
bool operator ==( const int val ) const
{
return (int)*this == val;
}
bool operator !=( const int val ) const
{
return (int)*this != val;
}
bool operator >=( const int val ) const
{
return (int)*this >= val;
}
bool operator <=( const int val ) const
{
return (int)*this <= val;
}
bool operator >( const int val ) const
{
return ((int)*this) > val;
}
bool operator <( const int val ) const
{
return (int)*this < val;
}
/***********************************************/
/***********************************************/
};
Working with anything smaller than an integer (32 or 64 bit depending on your architecture) is not ideal. All CPU operations of the smaller data types (short, etc) are done using integer arithmetic. Conversion to and from the CPU has to be done, slowing your application down (even if it is just a tad).
My advice: Store them as 32 (or 64 bit) integers to improve your overall speed. When it comes time to do I/O, then you'll have to do the conversion yourself.
As far as manipulating audio data, there are many libraries available that take care of the I/O for you - unless you want to start learning how PCM, etc are stored - as well as other DSP functions. I would suggest using one of the many libraries out there.
I know I'm ten years late, but what do you think about a bitset solution ?
class i24
{
std::bitset<24> m_value;
public:
constexpr i24(int value) noexcept: m_value {static_cast<unsigned long long>(value)} {}
operator int() const
{
constexpr std::uint32_t negative_mask = (0xff << 24);
return (m_value[23] ? negative_mask : 0) | m_value.to_ulong();
}
};
No - all you can really do is:
typedef int32_t int24_t;
which helps to make code/intent more readable/obvious, but doesn't impose any limits on range or storage space.
The best way is creating an Int24 class and use it like primitive types. It would be like:
Int24.h
#ifndef INT24_H
#define INT24_H
class Int24
{
public:
Int24();
Int24(unsigned long);
Int24 operator+ (Int24 value);
Int24 operator* (int value);
Int24 operator/ (int value);
void operator= (unsigned long value);
void operator= (Int24 value);
operator int() const;
// Declare prefix and postfix increment operators.
Int24 &operator++(); // Prefix increment operator.
Int24 operator++(int); // Postfix increment operator.
// Declare prefix and postfix decrement operators.
Int24 &operator--(); // Prefix decrement operator.
Int24 operator--(int); // Postfix decrement operator.
unsigned long value() const;
private:
unsigned char mBytes[3] ;
};
#endif // INT24_H
Int24.cpp
#include "Int24.h"
Int24::Int24()
{
mBytes[0] = 0;
mBytes[1] = 0;
mBytes[2] = 0;
}
Int24::Int24(unsigned long value)
{
mBytes[0] = ( value & 0xff);
mBytes[1] = ((value >> 8) & 0xff);
mBytes[2] = ((value >> 16 ) & 0xff);
}
Int24 Int24::operator+(Int24 value)
{
Int24 retVal;
unsigned long myValue;
unsigned long addValue;
myValue = this->mBytes[2];
myValue <<= 8;
myValue |= this->mBytes[1];
myValue <<= 8;
myValue |= this->mBytes[0];
addValue = value.mBytes[2];
addValue <<= 8;
addValue |= value.mBytes[1];
addValue <<= 8;
addValue |= value.mBytes[0];
myValue += addValue;
retVal = myValue;
return retVal;
}
Int24 Int24::operator*(int value)
{
(*this) = (*this).value() * value;
return (*this);
}
Int24 Int24::operator/(int value)
{
(*this) = (*this).value() / value;
return (*this);
}
void Int24::operator=(unsigned long value)
{
mBytes[0] = ( value & 0xff);
mBytes[1] = ((value >> 8) & 0xff);
mBytes[2] = ((value >> 16 ) & 0xff);
}
void Int24::operator=(Int24 value)
{
mBytes[0] = value.mBytes[0];
mBytes[1] = value.mBytes[1];
mBytes[2] = value.mBytes[2];
}
Int24 &Int24::operator++()
{
(*this) = (*this).value() + 1;
return *this;
}
Int24 Int24::operator++(int)
{
Int24 temp = (*this);
++(*this);
return temp;
}
Int24 &Int24::operator--()
{
(*this) = (*this).value() - 1;
return *this;
}
Int24 Int24::operator--(int)
{
Int24 temp = (*this);
--(*this);
return temp;
}
Int24::operator int() const
{
return value();
}
unsigned long Int24::value() const
{
unsigned long retVal;
retVal = this->mBytes[2];
retVal <<= 8;
retVal |= this->mBytes[1];
retVal <<= 8;
retVal |= this->mBytes[0];
return retVal;
}
You can download the Int24 and Int48 classes from my GitHub link. Also there is an example which shows you how to use it.
精彩评论