Skip to content

Commit

Permalink
Adding a routine to do the double to string conversion
Browse files Browse the repository at this point in the history
This adds a method to do a double to string conversion. The
method can only handle a very simple form of a double: NNNN.NNNN

RTC:212701
Change-Id: I005c29ad077ce0d2d6bb5bb6f84f595b52bb827a
Reviewed-on: http://rchgit01.rchland.ibm.com/gerrit1/81880
Tested-by: Jenkins Server <pfd-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP Build CI <op-jenkins+hostboot@us.ibm.com>
Tested-by: Jenkins OP HW <op-hw-jenkins+hostboot@us.ibm.com>
Tested-by: FSP CI Jenkins <fsp-CI-jenkins+hostboot@us.ibm.com>
Reviewed-by: Ilya Smirnov <ismirno@us.ibm.com>
Reviewed-by: Glenn Miles <milesg@ibm.com>
Reviewed-by: Christian R Geddes <crgeddes@us.ibm.com>
Reviewed-by: Daniel M Crowell <dcrowell@us.ibm.com>
  • Loading branch information
velozr authored and dcrowell77 committed Aug 14, 2019
1 parent 199b355 commit 7dce747
Showing 1 changed file with 174 additions and 20 deletions.
194 changes: 174 additions & 20 deletions src/lib/sprintf.C
Expand Up @@ -30,6 +30,9 @@
namespace Util
{

// Create a map, to map a numeric value to its character equivalence
static const char* digits = "0123456789abcdef";

struct format_options
{
enum
Expand Down Expand Up @@ -181,6 +184,10 @@ void parse_format_options(format_options& opt, const char*& fmt)
++fmt;
break;

// Although this modifier can be added to a double, "%lf", it is
// ignored. The double to string is a very simple implementation,
// not meant to be a full blown implementation, but adding the 'l'
// will not cause any harm.
case 'l':
if ((opt.length == opt.LEN_LONG) ||
(opt.length == opt.LEN_LONGLONG))
Expand Down Expand Up @@ -256,6 +263,10 @@ void parse_format_options(format_options& opt, const char*& fmt)
opt.type = opt.TYPE_PTR;
break;

// Considering that the current implementation of doubles is not being
// displayed in hexadecimal, there is no need for 'F' in the output
// display. Difference between 'f' and 'F' is that F is uppercase and
// only useful when displaying hex values.
case 'f':
opt.type = opt.TYPE_DOUBLE;
break;
Expand Down Expand Up @@ -302,6 +313,40 @@ size_t display_post_header(ConsoleBufferInterface& func,
return count;
}

/* @brief Converts a number into it's ASCII string representation
*
* @param[out] o_stringOutput - The recipient of the converted number to ASCII
* @param[in/out] io_stringOutputIndex - The index into buffer o_stringOutput
* @pre The io_stringOutputIndex is an index to where the first character
* will be placed
* @post The io_stringOutputIndex is one index location after the last
* placed character
* @param[in] i_number - The number to convert to a string.
* @param[in] i_base - The base of the number; 10, 8, etc
*
* @pre i_base must not be 0. Catastrophic results if so
*/
void convert_number_to_ascii(char o_stringOutput[],
size_t & io_stringOutputIndex,
uint64_t i_number,
const uint64_t i_base)
{
if (0 == i_number)
{
// If no number then use zero
o_stringOutput[io_stringOutputIndex++] = digits[0];
}
else
{
// Convert number to ASCII.
while(i_number)
{
o_stringOutput[io_stringOutputIndex++] = digits[i_number % i_base];
i_number /= i_base;
}
}
}

size_t display_string(ConsoleBufferInterface& func,
const format_options& f, const char* string)
{
Expand All @@ -319,20 +364,9 @@ size_t display_string(ConsoleBufferInterface& func,
return count;
}

size_t display_double(ConsoleBufferInterface& func,
const format_options& f, double number)
{
// TODO: CQ SW464805 Put in an implementation for double

size_t count(0);

return count;
}

size_t display_number(ConsoleBufferInterface& func,
const format_options& f, uint64_t number)
{
static const char* digits = "0123456789abcdef";
size_t count = 0;

char output[64];
Expand Down Expand Up @@ -414,15 +448,7 @@ size_t display_number(ConsoleBufferInterface& func,
}

// Convert number to ascii.
while(number)
{
output[len++] = digits[number % base];
number /= base;
}
if (len == 0)
{
output[len++] = digits[0];
}
convert_number_to_ascii(output, len, number, base);

// Fix up zero pad.
while(len < f.precision)
Expand Down Expand Up @@ -476,6 +502,134 @@ size_t display_number(ConsoleBufferInterface& func,
return count;
}

/* @brief This is just a rudimentary double to string routine.
*
* @details This routine takes in a value of type double, converts it
* two a string, and writes it to the provided ConsoleBufferInterface.
*
* @note This is a very simple double to string implementation that will
* display the double in a format of NNNN.NNNN in base 10, positive
* number only. It is advised to only use a double of the simplest
* form: NNNN.NNN. Some complicated forms of a double do not convert
* well, example 1.2345e-4 converts to 0.958747220502779 (incorrect),
* while 1.2345e+4 converts to 12345.0 (correct). Also working with
* doubles can produce imprecise results, example 12334.1469 produces
* 12334.146899999999 (technically correct), while 3.14159 produces
* 3.14158 (also technically correct).
* Also some printf modifiers are not honored such as 'F', 'l', 'L',
* etc. 'F' is for uppercase which is irrelevant when dealing with only
* base 10. 'l' and 'L' deal with long double. Again this is just a
* very simple implementation. Also modifiers 'width.precision' punting
* on this as well. Not getting into the minutia of how to display
* 1.2345e+4 given print format '%3.2f'.
* This algorithm works best with doubles of type NNNN.NNNN and print
* format of '%f'.
*
*
* @param[out] o_consoleBufferInterface - The recipient of the double in string
* form.
* @param[in] i_formatOptions - Formatting options of the double. Currently not
* being used. Kept to be consistent with current
* interfaces and if someone decides to actually
* implement all the formatting options.
* @param[in] i_doubleNumber - The double to convert to a string.
*/
size_t display_double(ConsoleBufferInterface& o_consoleBufferInterface,
const format_options& i_formatOptions,
double i_doubleNumber)
{
// Extract the integer part from the double. Example: 3 from 3.1415,
// 1 from 1.0, 31258 from 31258.00001, 0 from 0.123, etc
uint64_t l_integerPart = static_cast<uint64_t>(i_doubleNumber);

// Make a copy of the double for manipulation purposes.
double l_doubleTemp = i_doubleNumber;

// Make a copy of the integer part for manipulation purposes.
uint64_t l_integerPartTemp = l_integerPart;

// The double multiplier to move all digits found after the decimal point
// to before the decimal point.
uint64_t l_doubleMultiplier(1);

// Determine how many digits are there after the decimal point by taking a
// double such as 3.1415 multiply it by 10 to get 31.415. Get a copy of the
// integer part (31). Subtract the integer part (31.0) from the current
// double (31.415) to get 0.415. If 0.415 is greater than 0.0, then repeat
// the process until there are no more digits after the decimal point.
// The l_doubleMultiplier will be a factor of 10 that is needed to mutiply
// the double to move all digits after the decimal point to before the
// decimal point.
while ((l_doubleTemp - static_cast<double>(l_integerPartTemp)) > 0.0)
{
// Increase the multiplier until the value is exactly large enough to
// move all digits after the decimal point to before the decimal point.
l_doubleMultiplier*=10;
// Move 'X' digits after the decimal point to before the decimal point.
l_doubleTemp*=10;
// Extract the integer part out of the double
l_integerPartTemp = static_cast<uint64_t>(l_doubleTemp);
}

// Variable to capture to the digits after the decimal point.
uint64_t l_integerPartAfterDecimalPoint(0);

// If there are digits after the decimal point then extract those digits.
if (l_doubleMultiplier > 1)
{
// Extract the integer part, after the decimal point, by removing the
// integer part from the double then multiplying whats left by the
// multiplier calculated above.
l_integerPartAfterDecimalPoint = (i_doubleNumber -
static_cast<double>(l_integerPart)) *
l_doubleMultiplier;
}

// Length of the character buffer.
size_t l_bufferLength(0);

// Buffer to hold the double in string form.
char l_stringBuffer[64] = { 0 };

// Default base to 10.
const uint64_t l_base(10);

// Convert integer part, after decimal point, to ASCII.
convert_number_to_ascii(l_stringBuffer, l_bufferLength,
l_integerPartAfterDecimalPoint, l_base);

// Add the decimal point
l_stringBuffer[l_bufferLength++] = '.';

// Convert integer part, before decimal point, to ASCII.
convert_number_to_ascii(l_stringBuffer, l_bufferLength,
l_integerPart, l_base);

// End the string with a NIL terminator. The l_bufferLength is one index
// past the last digit character.
l_stringBuffer[l_bufferLength] = 0;

// Reverse the string for printing, by swapping to the two ends until they
// meet in the middle.
char* l_beginChar = l_stringBuffer;
char* l_endChar = &(l_stringBuffer[l_bufferLength -1]);
char l_tempChar;
while (l_beginChar < l_endChar)
{
l_tempChar = *l_beginChar;
*l_beginChar = *l_endChar;
*l_endChar = l_tempChar;
++l_beginChar;
--l_endChar;
}

// Display the double in string form.
return display_string(o_consoleBufferInterface,
i_formatOptions,
l_stringBuffer);
}


size_t vasprintf(ConsoleBufferInterface& func, const char* fmt, va_list& args)
{
int count = 0;
Expand Down

0 comments on commit 7dce747

Please sign in to comment.