From 7dce747041c57cddf8ee88727745c18a3b4ae53c Mon Sep 17 00:00:00 2001 From: Roland Veloz Date: Wed, 7 Aug 2019 00:45:17 -0500 Subject: Adding a routine to do the double to string conversion 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 Tested-by: Jenkins OP Build CI Tested-by: Jenkins OP HW Tested-by: FSP CI Jenkins Reviewed-by: Ilya Smirnov Reviewed-by: Glenn Miles Reviewed-by: Christian R Geddes Reviewed-by: Daniel M Crowell --- src/lib/sprintf.C | 194 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 174 insertions(+), 20 deletions(-) diff --git a/src/lib/sprintf.C b/src/lib/sprintf.C index 2cac40421..a8f941448 100644 --- a/src/lib/sprintf.C +++ b/src/lib/sprintf.C @@ -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 @@ -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)) @@ -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; @@ -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) { @@ -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]; @@ -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) @@ -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(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(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(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(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; -- cgit v1.2.1