Table of Contents
The motivation in writing this CNumberFormat
class was:
- Fast parsing of data retrieved from some hardware registers and interpret the values stored as separated fields. (E.g., look only on the field located between bit
15
and bit12
as a signed 4 bit width value.)
- Capable to interpret the values as signed or unsigned values according to the needs.
- Provide formatted strings in case the values are displayed on some kind of user interface.
The following considerations were taken:
- Numbers are stored as Little-Endian numbers. If you plan to use this class for Big-Endian byte ordering, you should tailor it for your needs. Currently there is no check about the Endianness used. (See Basic concepts on Endianness[^] in this site if you need help with Endianness...)
- Numbers are limited up to 32 bits. If you plan to use this class for 64 bits, some changes are also required.
I used this class to parse hundreds of fields stored in registers retrieved from some hardware to produce some statistics and graphs. The whole register-map was read periodically at a rate of 10 msec.
This class was initially developed using VC 6.0 (Visual Studio C++ 6.0), and now I'm using VC 7.1 (Visual Studio C++ .NET 2003). The demo application will compile only on VC 7.1 due that I'm using some MFC stuff not present in VC 6.0. The class CNumberFormat
will compile also in VC 6.0..
Adding the CNumberFormat class to your project
Make sure you have got two files: NumberFormat.h and NumberFormat.cpp, copy them to your project directory.
Open your project in your developing environment and add these files to your project.
To the relevant files, add the following line in your includes section:
...
#include "NumberFormat.h"
...
Now you are ready to use this class.
The CNumberFormat
has a default constructor, but you also can build one by providing some attributes such as startBit
, endBit
and initial value. If these values are not provided, the new CNumberFormat
item will have a startBit=0
, endBit=31
and value=0
.
...
CNumberFormat format;
CNumberFormar *pFormat = new CNumberFormat(15,18);
...
Copy-Construction is also possible from a reference or pointer (see below).
Duplicating a CNumberFormat
also is possible in different ways:
...
CNumberFormat newFormat(format);
CNumberFormar anotherFormat(pFormat);
newFormat = anotherFormat;
newFormat.Copy(pSourceNumberFormat);
newFormat.Copy(sourceNumberFormat);
...
Just take care not to use a NULL
pointer, otherwise you will get a default CNumberFormat
item.
Any time, the different attributes can be set using any of the provided setter functions.
The following table lists the different attributes stored on a CNumberFormat
. For clarity, the names here are not the same as the private members used on the class.
radix |
The format of the field, used to format strings. (ERadix enum type) |
startBit
endBit
|
The range is built from the startBit and endBit .
The startBit is the offset to the less-significant-bit on the field.
The endBit is the offset to the most-significant-bit on the field.
The startBit should be less or equal to the endBit value.
Using these values, an internal mask is built for getting the relevant field from the stored/provided number. |
noBits |
Number of bits the field contains. ((endBit-startBit)+1 ) |
value |
The 32 bit unsigned value provided. From this value, the field is extracted. |
There is no setter for the noBits
attribute. This attribute is set using the SetBitRange()
, or SetStartBit()
, or SetEndBit()
, or during construction.
There is no getter for the value
attribute. (I did not need such one!)
Let's assume we read a register containing a 'phase-value' field that is stored on bits 11
down to 8
. (A 4 bit field.)
The CNumberFormat
that describes the phase-value can be defined and used as follows:
...
CNumberFormat phaseValueFormat;
unsigned long ul;
signed long sl;
unsigned long value;
phaseValueFormat.SetBitRange(8,11);
...
value = *HW_REGISTER;
phaseValueFormat.SetValue(value);
...
ul = phaseValueFormat.GetUnsigned();
sl = phaseValueFormat.GetSigned();
..
ul = phaseValueFormat.GetUnsigned(value);
sl = phaseValueFormat.GetSigned(*HW_REGISTER);
...
From the experience I have with this class, most of the time you will be doing 'on-the-fly' translations, as shown in the last line on the code above.
If you need to provide a string containing the field value, you need also to set the radix
of the value you want to display. The following enum
is defined on this class for this purpose. (You may use the enumerated constants or just an integer for setting the radix for the format of the field to be extracted. From the NumberFormat.h header file:)
enum ERadix
{
UNSIGNED_RADIX = 0,
For generating a CString
containing the formatted number, do as follows:
...
phaseValueFormat.SetRadix(CNumberFormat::HEXADECIMAL_RADIX);
CString phaseValueText1 = phaseValueFormat.GetString();
CString phaseValueText2 = phaseValueFormat.GetString(*HW_REGISTER);
...
If you need a string describing the field range, you can use the GetRangeString()
method.
That is all Folks!
The code contains DOxygen comments, used to generate HTML documentation out of the code. I strongly advice you to use such a tool, directly or indirectly.
DOxygen is a documentation system for C++, C, Java, Objective-C, IDL (CORBA and Microsoft flavors) and to some extent PHP, C# and D. It can be found in www.doxygen.org[^].
The companion help file was generated using the KingsTools[^] Visual Studio .NET Add-In by SteveKing[^], This add-in contains several useful tools you may like to have integrated in your developing environment. (DOxygen, code-statistics, syntax coloring, etc.).
Thanks to SteveKing for this tool, and his disclaimer note I'm using in my code.
Source files:
- NumberFormat.h and
- NumberFormat.cpp
Although the demo application is overkill for showing the usage of this class, I hope that this demo may help others also on how to use timers, virtual-list controls, etc. If you want to focus only on the CNumberFormat
class, take a look on the addition of new ranges using the CArray
template and in the method CDemoDlg::OnListLvnGetdispinfo()
on the provided demo source-code.
The demo application will shuffle a 32 bit value out of the ::GetTickCount()
system call every ~2sec to give the user the time to look on the results. The shuffled value is displayed on a read-only edit box (hexadecimal and binary formats).
The table shows the same value parsed according to the bit-range and radix selected. The columns of the table are:
Bits: |
Number of bits on the extracted value (from 1 up to 32). |
Range: |
The endBit and startBit that describes the extracted value. The formatted string is as follows: "[endBit:startBit] ". |
Unsigned: |
The unsigned decimal representation of the extracted field. |
Signed: |
The signed or twos-complement representation of the extracted field. |
Hexa: |
The unsigned hexadecimal representation of the extracted field. |
Two buttons are also provided, one for adding a new range to the end of the list (you may add any number or new range descriptions), and a delete-all button for removing the contents of the list.
All the numbers on the table are extracted from the same victim value as displayed on the read-only edit box.
This code and the accompanying files are provided "as is" with no expressed or implied warranty. No responsibilities for possible damages, or side effects in its functionality. The user must assume the entire risk of using this code. The author accepts no liability if it causes any damage to your computer, causes your pet to fall ill, increases baldness or makes your car start emitting strange noises when you start it up. This code has no bugs, just undocumented features!.
This code is free for personal use, or freeware applications. If you plan to use this code in a commercial or shareware application, you are politely asked to contact the author for his permission.
Date |
Rev. |
Description |
2005/Jan/09 |
1.0 |
Debut in CodeProject. |
2002/May/11 |
--- |
Created. |
Hope that this simple class is useful for you.
To help other users in CodeProject, please Rate this Article :)