pragma runLim, 1000000 /* fStringOfReal Discussion: converting a real number to a string using concatenation automatically formats it with 6 decimal digits: string s = r "" This function fStringOfReal() provides the mechanism for displaying all the precision of the real number, and to truncate unwanted trailing zeros if needed. The Precision of real numbers in DXL is shown to be 17 decimal digits. That is, for any real number, there can be only 17 digits between the left-most non-zero digit and the right-most non-zero digit; excluding the decimal point. Note that the previous paragraph suggests that huge real numbers can have lots of zeros before the decimal point (with no decimal precision), and that extremely tiny real numbers (with no whole number part) may have lots of zeros after the decimal point. e.g. these are all valid Real numbers: 12345678.901234567 123456789012345670000000.0 -0.000000012345678901234567 e.g. this is invalid, since there are more than 17 digits between left and right: 12345678901234.5670044 If we calculate that, the trailing "0044" is removed from the result. Real number calculations, however, show that extra right-most digits can be introduced, but these are inaccurate and are ignored (set to zero). Developer's note: 17-Digits is real close, the real number is no-doubt some very high power of 2. However, since these functions deal with string manipulation and high precision real number calculations introduce errors, 17-digits is used to truncate results, rather than trying some clever calculations on the results. */ Buffer gl_bufStringOfReal = create() // Temp Buffer, used locally int cl_fStringOfRealPrecision = 17, // Calibration, precision of DXL 'real' numbers - cl_fStringOfRealExponent = 50 // Arbitrary limit of Digits away from decimal point real cl_fStringOfRealExponentReal = realOf(cl_fStringOfRealExponent) // The Exponent multiplier above implies that, for purposes of this function, // no real number can have more than that many digits from its left-most to the decimal point, // nor from the decimal point to the right-most digit. //***************************** void fSplitRealIntoParts(real rInput, string &sWholeNumberOut, real &rDecimalOut) { // Split the input real number into a string version of the WholeNumber part // and the real version of the Decimal part. // The WholeNumberOut part is the part of the real on the left of the period, // and can be positive or negative, e.g. "-123" // The rDecimalOut is always positive, and looks like "0.567" // e.g. convert "-123.567" into "-123" and "0.567". // Strategy: // Convert the real to a string, strip off the Whole Number part from the string, // then subtract that number from the original real number, leaving the Decimal part. string WholePart int PeriodPosition, i real rFractionalPart gl_bufStringOfReal = rInput "" // Convert to string // Find the Position of the Period in the string. PeriodPosition = contains(gl_bufStringOfReal, '.', 0) if (PeriodPosition < 0) { // print "PeriodPosition< 0????\n" // Don't think this is possible. sWholeNumberOut = "" rDecimalOut = rInput } else { WholePart = gl_bufStringOfReal[0:PeriodPosition-1] rFractionalPart = rInput - realOf(WholePart) // Subtract the whole number part if (rFractionalPart < 0.0) rFractionalPart = rFractionalPart*-1.0 // Make it Positive // Set output parameters sWholeNumberOut = WholePart rDecimalOut = rFractionalPart } } // end fSplitRealIntoParts() //***************************** string fStringOfReal(real rInput, int DesiredPrecision, bool TruncateZeros) { // Convert the real number to a natural string. // The string looks like: iii.ddd: Integer Digits, period, Decimal digits, it does // not look like real*10eN // The maximum precision of the real number is limited by DXL. // We enforce this limit by stating a maximum number of characters cl_fStringOfRealPrecision, // which is the max characters between the first (left-most) non-zero digit of the number // and the last (right-most) non-zero digit. Digits further to right are beyond Precision // and while they are calculated they are set to zero. // There will be at least one decimal digit, even if its zero: 231.0 // There will be at least one integer digit, even it its zero: 0.231 // Without violating the above, the caller may further restrict the output: // When DesiredPrecision is zero or negative then give the full precision of the decimal part. // When DesiredPrecision is positive then limit the Decimal part digits to DesiredPrecision. // When TruncateZeros then remove any trailing zeros from the decimal part of the string. // e.g. truncate "23.3400" to "23.34" // and truncate "23.0000" to "23.0" // TruncateZeros is ignored when DesiredPrecision <=0, in that case zeros are Truncated // Note: folks that want a pretty display of 4 decimal digits would call this function with 4 and false. // Strategy: // [1] Split the real number into a Whole Number part (string) and the decimal part (real). // [2] Calculate the number of Precision Digits taken up by the Whole Number part, the WholePrecision. // WholePrecision does not count a leading minus sign, and is zero when part is "0". // [3] Convert the Decimal Part into a string. // When the WholePrecision is already at max, then the Decimal part is "0". // Otherwise, steps [4] to [6] are used to get the Decimal Part. // [4] Get the string version of the Decimal Part, without any Decimal Point. // Multiply the decimal part by some arbitrarily huge power of 10 to make sure that the Decimal // Part is converted accurately to a string. Convert it to a string, ignoring the new decimal part. // [5] Prefix the string Decimal part with needed leading zeros. // [6] Truncate the imprecise right-most parts. // [7] If needed, Suffix Decimal Part with zeros to make it at least as long as DesiredPrecision // [8] If needed, Truncate Decimal part to max DesiredPrecision. // [9] If needed, Truncate trailing zeros from Decimal part //[10] Construct the final result. string WholePart, DecimalPart, Result int WholePrecision, DecimalLen, MaxPrecision, DecimalPrecision, NumZeros, i real rRemainder, rDecimal char FirstChar // [1] Split number into its parts // e.g. split <123.004567> into "123" and <0.004567> // The whole number part may have a minus sign and if zero // looks like zero, perhaps "-0" or "0". // The rRemainder is the fractional part. fSplitRealIntoParts(rInput, WholePart, rRemainder) // [2] Now count the number of Precision digits taken // up by the Whole Number part. WholePrecision = length(WholePart) if (WholePart[0] == '-') { WholePrecision -=1 // Don't count minus sign FirstChar = WholePart[1] // Next is the first char } else { FirstChar = WholePart[0] // First is the first char } if (FirstChar == '0') WholePrecision -=1 // rInput is "-0.xx" or "0.xx", so there is zero Precision // in the Whole Number part. // [3] Now calculate the Decimal part of the Number if (WholePrecision >= cl_fStringOfRealPrecision) { // Precision of the real is completely taken up by // the Whole Number Part. Don't bother calculating // the Decimal part, just make it "0". DecimalPart = "0" } else { // There is some precision needed in the Decimal Part, // so we need to calculate that. // [4] Get a string version of the Decimal Part. // Multiple Decimal Part by huge power of 10. // e.g. turn <004567> into <4567000000000......0> rDecimal = rRemainder*(10.0^cl_fStringOfRealExponentReal) // Split new number, ignoring the rRemainder // e.g. turn <4567000000000......0> into "4567000000000......". fSplitRealIntoParts(rDecimal, DecimalPart, rRemainder) // ** At this point, WholePart and DecimalPart are both strings of digits DecimalLen = length(DecimalPart) if (DecimalLen == 1 and DecimalPart == "0") { // There are no Decimal Digits to display // Do Nothing, keep it 'zero', will suffix zeros as needed below } else { // There are some Decimal Digits to display // [5] If needed, prefix DecimalPart with leading zeros // e.g. <.004567> has been turned into "4567000", // now turn it into "004567000". NumZeros = cl_fStringOfRealExponent-DecimalLen if (NumZeros > 0) { gl_bufStringOfReal = "" for (i=0; i 0) //- { DecimalPrecision = cl_fStringOfRealPrecision-WholePrecision // Must be at least 1 DecimalPart = DecimalPart[0:DecimalPrecision-1] } } // end Decimal to display, Prefixed with leading zeros } // end calculating the DecimalPart //print ">>[" rInput "]\t<" WholePart ">\traw: <" DecimalPart ">\n" // [7] If needed, Suffix Decimal Part with trailing zeros to make it // at least as long as DesiredPrecision. DecimalLen = length(DecimalPart) if (TruncateZeros) //- then{} // Don't bother suffixing zeros now since they will be truncated below. elseif (DesiredPrecision <= DecimalLen) //- then{} // DecimalPart already long enough, no suffixing needed else { // Suffix DecimalPart with requested zeros gl_bufStringOfReal = DecimalPart for (i=0; i 0 and DesiredPrecision < DecimalLen) // Desired is non-zero, but less than Actual - then DecimalPart = DecimalPart[0:DesiredPrecision-1] // print "\t\tWholeLen,DecPrec,NumZeros = " WholePrecision "\t" DecimalPrecision "\t" NumZeros "\n" // [9] If desired and needed, truncate all trailing zeros from Decimal part // Exception. There must be at least one decimal digit even if its zero. DecimalLen = length(DecimalPart) if (DesiredPrecision <= 0 or TruncateZeros) { for (i=DecimalLen-1; i>=0; i--) { if (DecimalPart[i] != '0') break } // print "\t\tMax/i = " DesiredPrecision "\t" i "\n" //if (DecimalPart[i] == '.') // i++ // Oops, leave a single zero after the period. if (i < 0) i = 0 // Oops, all Decimal Digits are zero so leave at least one zero DecimalPart = DecimalPart[0:i] } // end truncating trailing zeros // [10] Finally, Construct the final number. // e.g. turn "123" and "004567000" into "123.004567000" Result = WholePart "." DecimalPart return(Result) } // end fStringOfReal() //------------------------------ string fStringOfReal(real rInput) { // Overloaded, presume all valid decimal digits and truncate zeros return(fStringOfReal(rInput, 0, true)) } // end overloaded(fStringOfReal(max)) void DoStringOfTest(real rInput) { string Full = fStringOfReal(rInput) string Partial10 = fStringOfReal(rInput, 10, true) string Partial04 = fStringOfReal(rInput, 4, false) print rInput "\t" Full "\t" Partial10 "\t" Partial04 "\n" // print rInput "\t[" Full "]\t<" Partial10 ">\t^" Partial04 "^\n" } // end DoStringOfTest() void DriverStringOf() { print "StringOf\tDefault\t10 Max Precision\t4 Exact Precision\n" //print "StringOf\t[Default]\t<10 Max Precision>\t^4 Exact Precision^\n" // DoStringOfTest(11.5) // DoStringOfTest(11.0) // DoStringOfTest(-11.0) DoStringOfTest(real null) DoStringOfTest( 1112223330005556667778889.99) // DoStringOfTest( 11122233300055566677788.8999) DoStringOfTest( 911222333000555666777.888999) // DoStringOfTest( 1112223330005556667.77888999) DoStringOfTest( 11122233300055566.6777888999) // DoStringOfTest( 111222333000555.666777888999) DoStringOfTest( 1112223330005.55666777888999) // DoStringOfTest( 11122233300.0555666777888999) DoStringOfTest( 111222333.000555666777888999) // DoStringOfTest( 1112223.33000555666777888999) // DoStringOfTest( 11122.2333000555666777888999) DoStringOfTest( 111.222333000555666777888999) DoStringOfTest( 1.11222333000555666777888999) DoStringOfTest( 0.111222333000555666777888999) DoStringOfTest( 0.0111222333000555666777888999) // DoStringOfTest( 0.00111222333000555666777888999) DoStringOfTest( 0.000111222333000555666777888999) DoStringOfTest( 0.00000111222333000555666777888999) DoStringOfTest( -111.222333000555666777888999) DoStringOfTest( -0.111222333000555666777888999) DoStringOfTest( -0.0111222333000555666777888999) // DoStringOfTest( -0.00111222333000555666777888999) DoStringOfTest( -0.000111222333000555666777888999) DoStringOfTest(-0.00000111222333000555666777888999) DoStringOfTest( 0.0 ) DoStringOfTest(-0.011) DoStringOfTest(-0.012) DoStringOfTest(-0.0113) DoStringOfTest(-0.0114) DoStringOfTest(-0011.0) DoStringOfTest(-0.0113) DoStringOfTest( 0.0113) /* DoStringOfTest(11.2) DoStringOfTest(11.2000000000000000) DoStringOfTest(11.2000000000001) DoStringOfTest(11.20000000000001) DoStringOfTest(11.200000000000001) DoStringOfTest(11.200000000000001999999) DoStringOfTest(11.200000000000009) DoStringOfTest(11.2000000000000001) DoStringOfTest(11.20000000000123456789) DoStringOfTest(112.0000000000123456789) DoStringOfTest(1120.000000000123456789) DoStringOfTest(11200.00000000123456789) DoStringOfTest(112000.0000000123456789) DoStringOfTest(112000.0000000123456789) */ /* DoStringOfTest(1120456789123456.0001) DoStringOfTest(1120456789123456.0001000000001) DoStringOfTest(1120456789123456.00010000000001) DoStringOfTest(1120456789123456.000100000000001) DoStringOfTest(1120456789123456.0001000000000001) DoStringOfTest(1120456789123456.00010000000000001) */ // DoStringOfTest(1120000000.000001) // DoStringOfTest(-1120000000.000001) } // end DriverStringOf() void DoFractionalTest(real rInput) { string WholePart = "" real FractionalPart = 0.0 fSplitRealIntoParts(rInput, WholePart, FractionalPart) print "[" rInput "]\t^" WholePart "^\t<" FractionalPart ">\n" } void DriveFractionalTests() { DoFractionalTest(0.0) DoFractionalTest(100000.0) DoFractionalTest( 12345.67890123456689034567) DoFractionalTest(-12345.67890123456689034567) DoFractionalTest(112045678912345.90010000000001) DoFractionalTest(112045678912345.94010000000001) DoFractionalTest(112045678912345.94910000000001) DoFractionalTest(1120456789123456.94910000000001) DoFractionalTest(11204567891234567.94910000000001) DoFractionalTest(112045678912345678.94910000000001) } // DriveFractionalTests() // DriveFractionalTests() DriverStringOf() // ---- end file Driver_fStringOfReal.dxl ----d file Driver_fStringOfReal.dxl -