Convert Double Precision Data to Unsigned Integer Words#
Version |
Date |
Author |
Purpose |
|---|---|---|---|
1 |
April 2020 |
Brent Caughron |
Initial version |
1.1 |
June 2021 |
Tony Varesic |
Added |
2 |
September 2026 |
Nino Tarantino |
Converted to reStructuredText and major overhaul of content |
Introduction#
This model provides several interfaces for converting a double-precision value to a vector of unsigned integer words. The bit-resolution (i.e. significance of each bit), number of bits per word, and number of words to use are all user-configurable.
Consider a simple case, where a double-precision value must be stored in a 4-bit register with each bit representing a resolution of 0.1. This is a common case for flight software telemetry, which tends to pack floating point numbers into smaller buffers of known resolution per bit due to limited bandwidth.
In this case, the value 0.9 would be stored as:
This model extends this concept to allow for a user-defined word count and bits per word. Consider the case of a 4-word register with 4 bits per word and a resolution of 0.001 bits per word.
In this case, the value 5.001 would be stored as:
Requirements#
In addition to the value being converted into words, three user-specified inputs must be passed into the model:
Resolution (i.e. significance of the least significant bit)
Bit-size of each word (e.g. 8-bit, 32-bit)
Number of words available to represent the double
Model Specifications#
Model Structure#
This model is usable as:
Free functions, useful for one-off conversions.
An instantiated class,
ConvertDoubleToUintWords. Useful for when a conversion is needed as a simulation scheduled job.
Functions
-
void convert_double_to_uint_words(double convert_value, double resolution, std::vector<uint32_t> &words, unsigned int bit_size)#
Convert a
doubleinto n-bit unsigned integer wordsFor example, convert a double-precision value to a set of four unsigned integer words, each of size 8 bits, with a resolution of 0.1 per bit:
std::vector<unit32_t> words(4); convert_double_to_uint_words(value, 0.1, words, 8U);
Instantiates a ConvertDoubleToUintWords class to do the conversion.
Note
wordsis returned with the least-significant-word at position 0.- Parameters:
convert_value -- [in] Value to convert
resolution -- [in] Significance of the least significant bit
words -- [inout] Output vector to store the converted data
bit_size -- [in] Number of bits in each word
- Pre:
The size of
wordsmust be sized to the number of words that should be used to store the converted data.
-
void convert_double_to_uint_words(double convert_value, double resolution, uint32_t *words, const unsigned int array_size, unsigned int bit_size)#
Convert a
doubleinto n-bit unsigned integer wordsFor example, convert a double-precision value to a set of four unsigned integer words, each of size 8 bits, with a resolution of 0.1 per bit:
unit32_t words[4]; convert_double_to_uint_words(value, 0.1, words, std::size(words), 8U);
Instantiates a ConvertDoubleToUintWords class to do the conversion.
Note
wordsis returned with the least-significant-word at position 0.- Parameters:
convert_value -- [in] Value to convert
resolution -- [in] Significance of the least significant bit
words -- [out] Output array to store the converted data
array_size -- [in] Size of the output array, i.e. number of words to use
bit_size -- [in] Number of bits in each word
-
class ConvertDoubleToUintWords#
- #include "models/utilities/double_to_words/include/convert_double_to_words.hh"
Provides an interface to convert a
doubleinto n-bit unsigned integer wordsPublic Functions
-
ConvertDoubleToUintWords(const double &in_convert_value, double in_resolution, unsigned int in_word_count, unsigned int in_bit_size)#
Constructor
For example, set up the class to convert a double-precision value to a set of four unsigned integer words, each of size 8 bits, with a resolution of 0.1 per bit:
ConvertDoubleToUintWords converter(value_ref, 0.1, 4U, 8U);
- Parameters:
in_convert_value -- [out] Reference to value to convert
in_resolution -- [in] Significance of the least significant bit
in_word_count -- [in] Number of words to use for the conversion
in_bit_size -- [in] Number of bits in each word
-
ConvertDoubleToUintWords(const ConvertDoubleToUintWords&) = delete#
Copy constructor deleted.
-
ConvertDoubleToUintWords &operator=(const ConvertDoubleToUintWords&) = delete#
Copy assignment operator deleted.
-
virtual ~ConvertDoubleToUintWords() = default#
Default destructor
-
void update()#
Perform the conversion.
-
void compute_significance()#
Calculates the significance of each word-array column
Automatically called during construction. Used to convert the input value into words.
-
bool check_values()#
Checks to make sure that the value to convert is convertible
Checks that:
The value to convert is at least half of the specified bit-resolution.
The value to convert is not too large to be representable in the number of words specified with the given resolution.
The separation between words ("resolution") is not finer than the separation between adjacent values representable by a double.
- Returns:
trueif conversion is possible, otherwisefalse
Public Members
-
std::vector<uint32_t> words#
Output values, the set of integers that represent convert_value.
- Units:
--
Note
The least-significant word is at index 0.
Protected Attributes
-
const double &convert_value#
User inputted value to be converted to a words value based off of specified bit size.
- Units:
--
-
double resolution#
Incrementation stepping for inputted value.
- Units:
--
-
unsigned int word_count#
Number of words; size of the words vector.
- Units:
--
-
std::vector<double> significance#
The amount by which the real number being represented by the words array will increment for every integral increment of the word at the same index.
- Units:
--
-
double max_uint = {0.0}#
Max value each word can be as determined by bit size.
- Units:
--
-
ConvertDoubleToUintWords(const double &in_convert_value, double in_resolution, unsigned int in_word_count, unsigned int in_bit_size)#
Mathematical Formulation#
See the Introduction for a graphical representation of the conversion logic.
User's Guide#
Important
Regardless of the API chosen, data is returned as an array of words with word 0 corresponding to the least-significant word.
Free Function API#
For one-off conversions, one of the free functions may be used. If possible, you should use the version which accepts
an std::vector.
The following demonstrates how to convert the value 5.001 to a 4-word register with 4 bits per word and a resolution of 0.001 bits per word.
const double value = 5.001;
const unsigned int num_words = 4U;
const unsigned int bits_per_word = 4U;
const double resolution = 0.001;
/* Vector version */
std::vector<uint32_t> words(num_words);
convert_double_to_uint_words(value, resolution, words, bits_per_word);
// words stores [9 8 3 1]
/* C-style array version */
uint32_t words[num_words];
convert_double_to_uint_words(value, resolution, words, std::size(words), bits_per_word);
// words stores [9 8 3 1]
Class-Based API#
For repeated conversions, it is recommended to create a long-lived instance of the ConvertDoubleToUintWords
class and repeatedly use its update() method. The resulting words are stored in its
words member.
The same example shown for the free function APIs is repeated below with the class API.
double value = 5.001;
const unsigned int num_words = 4U;
const unsigned int bits_per_word = 4U;
const double resolution = 0.001;
ConvertDoubleToUintWords converter(value, resolution, num_words, bits_per_word);
// converter.words stores [9 8 3 1].
converter.update();
// The value has changed. Now converter.words stores [0 0 0 0].
value = 0.0;
converter.update();
Extension#
N/A
Verification#
Code Coverage#
------------------------------------------------------------------------------
GCC Code Coverage Report
Directory: .
------------------------------------------------------------------------------
File Lines Exec Cover Missing
------------------------------------------------------------------------------
src/convert_double_to_uint_words.cc
70 70 100%
------------------------------------------------------------------------------
TOTAL 70 70 100%
-----------------------------------------------------------------------------
See detailed coverage information here.
Exceptions#
N/A
Unit-Test Cases#
This model uses GoogleTest to test its functionality. Since the model has three different APIs, each one is tested with the same conditions to ensure that each interface produces the same output.
ConvertDoubleToUintWords.InvalidConfiguration#
This test ensures that invalid configuration options or invalid conversion values are caught and handled properly. The following cases are checked:
Case |
Expected Resolution |
Result |
|---|---|---|
|
Error message emitted |
Pass |
A word count of 0 is specified |
Error message emitted and output array of words is of size 0 |
Pass |
A bit size of 64 is requested, which is larger than the maximum allowable 32 |
Error message emitted |
Pass |
The value to convert is too large to be representable given the specified number of words, bits, and resolution |
Error message emitted and all bits set to 1 |
Pass |
The value to convert is too small to be representable given the specified bit resolution |
Error message emitted and all bits set to 0 |
Pass |
The value to convert has a specified resolution that is smaller than the distance to the next value that can be represented as a double |
Error message emitted |
Pass |
ConvertDoubleToUintWords.NoRoundoff#
A variety of cases are run where no round-off correction is required: i.e., either the value is convertible to bits with no remainder or the remainder is less than half the bit resolution, in which case the remainder is lost due to rounding down.
For the following cases, all three APIs are used for each case and the expected value is checked against the output from each. Expected values are described in detail in the test source file, so are repeated here with no explanation.
Value |
Resolution |
Bits Per Word |
Expected Words (Least to Most Significant) |
Result |
|---|---|---|---|---|
0.001 |
0.001 |
4 |
\(\begin{pmatrix} 1 & 0 & 0 & 0 \end{pmatrix}\) |
Pass |
5.002 |
0.001 |
4 |
\(\begin{pmatrix} 10 & 8 & 3 & 1 \end{pmatrix}\) |
Pass |
5.001 |
0.001 |
4 |
\(\begin{pmatrix} 9 & 8 & 3 & 1 \end{pmatrix}\) |
Pass |
123456.0 |
1.0 |
16 |
\(\begin{pmatrix} 57920 & 1 & 0 & 0 \end{pmatrix}\) |
Pass |
123457.0 |
1.0 |
16 |
\(\begin{pmatrix} 57921 & 1 & 0 & 0 \end{pmatrix}\) |
Pass |
123458.0 |
1.0 |
16 |
\(\begin{pmatrix} 57922 & 1 & 0 & 0 \end{pmatrix}\) |
Pass |
900987654321.181 |
0.001 |
16 |
\(\begin{pmatrix} 13341 & 35093 & 13169 & 3 \end{pmatrix}\) |
Pass |
Per-case decomposition#
Case 1 — 0.001, resolution 0.001
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(0.001 \times (2^4)^3 = 4.096\) |
0 |
0.001 |
2 |
\(0.001 \times (2^4)^2 = 0.256\) |
0 |
0.001 |
1 |
\(0.001 \times (2^4)^1 = 0.016\) |
0 |
0.001 |
0 |
\(0.001 \times (2^4)^0 = 0.001\) |
1 |
0.000 |
Case 2 — 5.002, resolution 0.001
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(0.001 \times (2^4)^3 = 4.096\) |
1 |
0.906 |
2 |
\(0.001 \times (2^4)^2 = 0.256\) |
3 |
0.138 |
1 |
\(0.001 \times (2^4)^1 = 0.016\) |
8 |
0.010 |
0 |
\(0.001 \times (2^4)^0 = 0.001\) |
10 |
0.000 |
Case 3 — 5.001, resolution 0.001
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(0.001 \times (2^4)^3 = 4.096\) |
1 |
0.905 |
2 |
\(0.001 \times (2^4)^2 = 0.256\) |
3 |
0.137 |
1 |
\(0.001 \times (2^4)^1 = 0.016\) |
8 |
0.009 |
0 |
\(0.001 \times (2^4)^0 = 0.001\) |
9 |
0.000 |
Case 4 — 123456.0, resolution 1.0
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(1.0 \times (2^{16})^3 = 281474976710656.0\) |
0 |
123456.0 |
2 |
\(1.0 \times (2^{16})^2 = 4294967296.0\) |
0 |
123456.0 |
1 |
\(1.0 \times (2^{16})^1 = 65536.0\) |
1 |
57920.0 |
0 |
\(1.0 \times (2^{16})^0 = 1.0\) |
57920 |
0.0 |
Case 5 — 123457.0, resolution 1.0
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(1.0 \times (2^{16})^3 = 281474976710656.0\) |
0 |
123457.0 |
2 |
\(1.0 \times (2^{16})^2 = 4294967296.0\) |
0 |
123457.0 |
1 |
\(1.0 \times (2^{16})^1 = 65536.0\) |
1 |
57921.0 |
0 |
\(1.0 \times (2^{16})^0 = 1.0\) |
57921 |
0.0 |
Case 6 — 123458.0, resolution 1.0
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(1.0 \times (2^{16})^3 = 281474976710656.0\) |
0 |
123458.0 |
2 |
\(1.0 \times (2^{16})^2 = 4294967296.0\) |
0 |
123458.0 |
1 |
\(1.0 \times (2^{16})^1 = 65536.0\) |
1 |
57922.0 |
0 |
\(1.0 \times (2^{16})^0 = 1.0\) |
57922 |
0.0 |
Case 7 — 900987654321.181, resolution 0.001
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(0.001 \times (2^{16})^3 = 281474976710.656\) |
3 |
56562724189.21301 |
2 |
\(0.001 \times (2^{16})^2 = 4294967.296\) |
13169 |
2299868.18901062 |
1 |
\(0.001 \times (2^{16})^1 = 65.536\) |
35093 |
13.34101061988622 |
0 |
\(0.001 \times (2^{16})^0 = 0.001\) |
13341 |
1.061988621842147e-05 |
Note
The final remainder (\(1.06 \times 10^{-5}\)) is less than half the resolution, so no round-off correction is applied.
ConvertDoubleToUintWords.RoundoffCorrection#
A variety of cases are run where round-off correction is required: i.e., the value is not exactly convertible to bits given the resolution and the remainder is greater than half the bit resolution. In that case, the least significant word will be incremented by 1 (round-up). This has the potential to spill over into the next most significant word if the round-up results in the least significant word surpassing its maximum value. Cases were designed to exercise the no spillover, single spillover, and multi-word spillover scenarios.
For the following cases, all three APIs are used for each case and the expected value is checked against the output from each. Expected values are described in detail in the following subsection.
Value |
Resolution |
Bits Per Word |
Expected Words (Least to Most Significant) |
Result |
|---|---|---|---|---|
0.003153 |
0.0001 |
4 |
\(\begin{pmatrix} 0 & 2 & 0 & 0 \end{pmatrix}\) |
Pass |
12287.8 |
1.0 |
4 |
\(\begin{pmatrix} 0 & 0 & 0 & 3 \end{pmatrix}\) |
Pass |
900987654321.182 |
0.0001 |
16 |
\(\begin{pmatrix} 13342 & 35093 & 13169 & 3 \end{pmatrix}\) |
Pass |
Per-case decomposition#
Case 1 — 0.003153, resolution 0.0001
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(0.0001 \times (2^4)^3 = 0.4096\) |
0 |
0.003153 |
2 |
\(0.0001 \times (2^4)^2 = 0.0256\) |
0 |
0.003153 |
1 |
\(0.0001 \times (2^4)^1 = 0.0016\) |
1 |
0.001553 |
0 |
\(0.0001 \times (2^4)^0 = 0.0001\) |
15 |
0.000053 |
Note
The final remainder (0.000053) is greater than half the resolution
(0.00005), so word 0 is incremented from 15 to 16. This overflows the
4-bit range, spilling over to 0 and incrementing word 1 from 1 to 2.
Final words: [2, 0, 0, 0] (MSW-first).
Case 2 — 12287.8, resolution 1.0
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(1.0 \times (2^4)^3 = 4096\) |
2 |
4095.8 |
2 |
\(1.0 \times (2^4)^2 = 256\) |
15 |
255.8 |
1 |
\(1.0 \times (2^4)^1 = 16\) |
15 |
15.8 |
0 |
\(1.0 \times (2^4)^0 = 1\) |
15 |
0.8 |
Note
The final remainder (0.8) is greater than half the resolution (0.5), so
word 0 is incremented. This starts a chain of spillovers: word 0
(15 → 16 → 0), word 1 (15 → 16 → 0), and word 2 (15 → 16 → 0) each
overflow in turn, finally incrementing word 3 from 2 to 3.
Final words: [3, 0, 0, 0] (MSW-first).
Case 3 — 900987654321.182, resolution 0.001
Word |
Significance |
Value |
Remainder |
|---|---|---|---|
3 |
\(0.001 \times (2^{16})^3 = 281474976710.656\) |
3 |
56562724189.21399 |
2 |
\(0.001 \times (2^{16})^2 = 4294967.296\) |
13169 |
2299868.1899871826 |
1 |
\(0.001 \times (2^{16})^1 = 65.536\) |
35093 |
13.34198718238622 |
0 |
\(0.001 \times (2^{16})^0 = 0.001\) |
13341 |
0.0009871823862184215 |
Note
The final remainder (\(\approx 0.000987\)) is greater than half the
resolution (0.0005), so word 0 is incremented from 13341 to 13342. No
overflow occurs, so no further spillover is needed.
Final words: [3, 13169, 35093, 13342] (MSW-first).