Real number formatting in DXL

Bonjour,

quelqu'un sait-il comment formatter un nombre réel, par exemple en C on
fait

printf("%.3f", 3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803483) , et ça sort 3.142.

Mais en DXL ?
SystemAdmin - Mon Feb 14 09:38:36 EST 2011

Re: Real number formatting in DXL
llandale - Mon Feb 14 11:34:47 EST 2011

Let me guess what you are asking. Wrote this a couple years ago.


/*        ====================== 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<NumZeros; i++) 
{  gl_bufStringOfReal       += 
'0' 
} gl_bufStringOfReal      += DecimalPart DecimalPart      = stringOf(gl_bufStringOfReal) 
}           
// end prefixing Decimal Part with needed Zeros 
// [6]Truncate the Imprecise right-most parts of DecimalPart 
// When there is no Whole Number part Precision, then we need 
//    all the Precision Decimal Parts including leading zeros. 
// When there is indeed Whole Number part Precision, then we need 
//   only the first so many digits of the DecimalPart 

if (WholePrecision > 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<">\traw: <">\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<DesiredPrecision - DecimalLen; i++) 
{  gl_bufStringOfReal    += 
'0' 
} DecimalPart     = stringOf(gl_bufStringOfReal) 
}   
// end Suffixing Decimal Part with needed trailing Zeros   
// [8] Truncate Decimal Part to max DesiredPrecision DecimalLen   = length(DecimalPart) 

if(DesiredPrecision > 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))   print (fStringOfReal(3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803483, 3, 

true)) 
"\n" print (fStringOfReal(3141592653589793238462643383279502884197169399375105820974944592307816.40628620899862803483, 3, 

true)) 
"\n" print (fStringOfReal(314159265358.97, 5, 

false)) 
"\n"

prints this:
3.141
3141592653589793100000000000000000000000000000000000000000000000000000.0
314159265358.96997

I see it truncates, returning 3.141 not 3.142. I see there is some approximation issues in precision.

Someone want to have a go at truncating, please feel free.

  • Louie

I have a nagging feeling that this clumsy function can be made vastly simpler, and that I'll be embarrased when someone posts a much more elegant solution. And I think I know who can do so!

Re: Real number formatting in DXL
SystemAdmin - Mon Feb 14 12:42:59 EST 2011

I quickly sketched the following code to do the same thing. It uses regular expression and text manipulation.

I am quite surprised & disappointed that such a thing does not exist de-facto in DXL.

BTW, what markup do you use to post code with syntax highlight

string fStringOfReal(real rInput, int iDesiredPrecision, bool bTruncateZeros){
real rPrecision = (exp ((realOf iDesiredPrecision) * 2.302585092994046))
string sInput = ((realOf (intOf (rInput * rPrecision)))/rPrecision) ""
Regexp re = regexp2 "^(0-9+)?(\\.(0-9+))?$"

if(re sInput){
int iEndFactionalPart = start 2
if(iEndFactionalPart >= 0){
iEndFactionalPart = 1+iDesiredPrecision
if((length sInput) > iEndFactionalPart+1){
if bTruncateZeros and matches("^0*$",sInput(iEndFactionalPart+1):) then{
re = regexp2 "0+$"
if re sInput then{
sInput = sInput0 : ((start 0)-1)
}
}
else
sInput = sInput0 : iEndFactionalPart
}
else if(!bTruncateZeros){
Buffer oBuff = create
oBuff = sInput
int i = (length sInput) - iEndFactionalPart
for i in i : 0 by -1 do oBuff += "0"
sInput = stringOf oBuff
}
}
else if(bTruncateZeros){
re = regexp2 "0+$"
if re sInput then{
sInput = sInput0 : ((start 0)-1)
}
}
else
{
Buffer oBuff = create
oBuff = sInput
oBuff += "."
int i
for i in iDesiredPrecision : 1 by -1 do oBuff += "0"
sInput = stringOf oBuff
}

}

return sInput

}

Re: Real number formatting in DXL
llandale - Mon Feb 14 14:43:58 EST 2011

SystemAdmin - Mon Feb 14 12:42:59 EST 2011
I quickly sketched the following code to do the same thing. It uses regular expression and text manipulation.

I am quite surprised & disappointed that such a thing does not exist de-facto in DXL.

BTW, what markup do you use to post code with syntax highlight

string fStringOfReal(real rInput, int iDesiredPrecision, bool bTruncateZeros){
real rPrecision = (exp ((realOf iDesiredPrecision) * 2.302585092994046))
string sInput = ((realOf (intOf (rInput * rPrecision)))/rPrecision) ""
Regexp re = regexp2 "^(0-9+)?(\\.(0-9+))?$"

if(re sInput){
int iEndFactionalPart = start 2
if(iEndFactionalPart >= 0){
iEndFactionalPart = 1+iDesiredPrecision
if((length sInput) > iEndFactionalPart+1){
if bTruncateZeros and matches("^0*$",sInput(iEndFactionalPart+1):) then{
re = regexp2 "0+$"
if re sInput then{
sInput = sInput0 : ((start 0)-1)
}
}
else
sInput = sInput0 : iEndFactionalPart
}
else if(!bTruncateZeros){
Buffer oBuff = create
oBuff = sInput
int i = (length sInput) - iEndFactionalPart
for i in i : 0 by -1 do oBuff += "0"
sInput = stringOf oBuff
}
}
else if(bTruncateZeros){
re = regexp2 "0+$"
if re sInput then{
sInput = sInput0 : ((start 0)-1)
}
}
else
{
Buffer oBuff = create
oBuff = sInput
oBuff += "."
int i
for i in iDesiredPrecision : 1 by -1 do oBuff += "0"
sInput = stringOf oBuff
}

}

return sInput

}

Need to post with the { c o d e } braces, without the spaces. Cleaned it up and added a print.

Not much of a fan of Regexp due to ignorance. Not much of a number cruncher. Don't recall anything I learned in school, using punch cards. Don't recall much about writing this function but do recall is was quite the pain of 'discovery', took about 3 days.

My original precision was limiting the decimal part, yours seems to be limiting the entire precision. I don't see why someone would want to turn 1234560.0 into "1230000.0", but they would want to limit the decimal part, or specify it exactly, in order to get columns of numbers to align right.

Your precision seems to be off by 1 since when you send "4" you get 5 digits of precision. Also, large numbers seem to fall apart with yours, see results below.

Forgive ignorance, what is "2.302585092994046"? and please explain "rPrecision". Would you not want to divide first by rPrecision, turn into int, then multiply by it?

Regular Expressions are extremely inefficient to declare and therefore you should be tempted to move your constant Regexp functions as global variables outside the function and should translate your "matches" into a Regexp.

Regexp g_SOR_re1 = regexp2 "^(0-9+)?(\\.(0-9+))?$",
g_SPR_re2 = regexp2 "0+$"


string f1_StringOfReal(real rInput, 

int iDesiredPrecision, bool bTruncateZeros) 
{ real rPrecision = (exp ((realOf iDesiredPrecision) * 2.302585092994046)) string sInput = ((realOf (intOf (rInput * rPrecision)))/rPrecision) 
"" Regexp re = regexp2 
"^([0-9]+)?(\\.([0-9]+))?$" 
//print "\tDesires/Real Precision: " iDesiredPrecision "\t" rPrecision "\n" 

if(re sInput) 
{ 

int iEndFactionalPart = start 2 

if(iEndFactionalPart >= 0) 
{  iEndFactionalPart = 1+iDesiredPrecision 

if((length sInput) > iEndFactionalPart+1) 
{ 

if (bTruncateZeros and matches(
"^0*$",sInput[(iEndFactionalPart+1):])) then 
{  re = regexp2 
"0+$" 

if re sInput then 
{ sInput = sInput[0:((start 0)-1)] 
} 
} 

else sInput = sInput[0:iEndFactionalPart] 
} elseif(!bTruncateZeros) 
{  Buffer oBuff = create oBuff = sInput 

int i = (length sInput) - iEndFactionalPart 

for i in i : 0 by -1 

do oBuff += 
"0" sInput = stringOf oBuff 
} 
} 

else 

if(bTruncateZeros) 
{  re = regexp2 
"0+$" 

if re sInput then 
{  sInput = sInput[0:((start 0)-1)] 
} 
} 

else 
{  Buffer oBuff = create oBuff = sInput oBuff += 
"." 

int i 

for i in iDesiredPrecision : 1 by -1 

do oBuff += 
"0" sInput = stringOf oBuff 
}       
} 

return sInput   
}

I added this, calling my function from my library:

string f2_StringOfReal(real rInput, 

int iDesiredPrecision, bool bTruncateZeros) 
{        

return(fStringOfReal(rInput, iDesiredPrecision, bTruncateZeros))  
// Louie Version 
}   

void  DoTest(real r, 

int iPrec, bool Trunc) 
{   string  s1 = f1_StringOfReal(r, iPrec, Trunc) string    s2 = f2_StringOfReal(r, iPrec, Trunc)   print (s1 == s2) 
"\t" r 
"\t" iPrec 
"\t" Trunc 
"\t[" s1 
"]\t[" s2 
"]\n" 
}   DoTest(1234.456789, 4, 

true) DoTest(12344567.89, 4, 

true) DoTest(123456789000000000.0, 6, 

true)

I see that the print statement prints this:
Desires/Real Precision: 4 10000.000000
I get these results:



false        1234.456789     4       

true   [1234.4]        [1234.4567] 

false      12344567.890000 4       

true   [-214748.364800]        [12344567.89] 

false    123456789000000000.000000       6       

true   [-2147.483648]  [123456789000000000.0]

{code}

Re: Real number formatting in DXL
SystemAdmin - Tue Feb 15 02:46:21 EST 2011

To answer your question about 2.302585092994046


print (log 10.0) 
"= 2.302585092994046" 
{/code
}     So the intension of

(exp ((realOf iDesiredPrecision) * 2.302585092994046))
{/code}

is to compute 10 to the power of iDesiredPrecision.

Re: Real number formatting in DXL
SystemAdmin - Tue Feb 15 03:26:17 EST 2011

Thanks for pointing out the buggy behaviour. Frankly speaking I had written this from the top of my head in a few minutes. Here is a fix:


Regexp greSOR_1 = regexp2 
"^([0-9]+)?(\\.([0-9]+))?$" Regexp greSOR_2 = regexp2 
"^0*$"   string fStringOfReal(real rInput, 

int iDesiredPrecision, bool bTruncateZeros)
{ real rPrecision = (exp ((realOf iDesiredPrecision) * 2.302585092994046)) real rToBeRounded = rInput * rPrecision 

if rToBeRounded < 2147483646.5 and rToBeRounded > -2147483647.5 then rInput = (realOf (intOf rToBeRounded))/rPrecision string sInput = rInput 
""     

if(greSOR_1 sInput)
{ 

int iEndFactionalPart = start 2 

if(iEndFactionalPart >= 0)
{ iEndFactionalPart += iDesiredPrecision+1 

if((length sInput) > iEndFactionalPart)
{ 

if bTruncateZeros and (greSOR_2 sInput[iEndFactionalPart:]) then
{ 

int i = (length sInput) -1 

while (sInput[i : i] == 
"0") --i; sInput = sInput[0 : i]   
} 

else sInput = sInput[0 : (iEndFactionalPart-1)] 
} 

else 

if(bTruncateZeros)
{ 

int i = (length sInput) -1 

while (sInput[i : i] == 
"0") --i; sInput = sInput[0 : i] 
} 

else 
{ 

int i = (length sInput) - iEndFactionalPart 

if i >= 1 then
{ Buffer oBuff = create oBuff = sInput 

for i in i : 1 by -1 

do oBuff += 
"0" sInput = stringOf oBuff 
} 
} 
} 

else 

if(bTruncateZeros)
{ 

int i = (length sInput) -1 

while (sInput[i : i] == 
"0") --i; sInput = sInput[0 : i] 
} 

else 
{ Buffer oBuff = create oBuff = sInput oBuff += 
"." 

int i 

for i in iDesiredPrecision : 1 by -1 

do oBuff += 
"0" sInput = stringOf oBuff 
} 
}   

return sInput 
}


I have tried the following

print fStringOfReal(1234.456789, 4, 

true) 
"\n" print fStringOfReal(12344567.89, 4, 

true) 
"\n" print fStringOfReal(123456789000000000.0, 6, 

true) 
"\n" print fStringOfReal(1234.456789, 4, 

false) 
"\n" print fStringOfReal(12344567.89, 4, 

false) 
"\n" print fStringOfReal(123456789000000000.0, 6, 

false) 
"\n"


and it seems to work better, as I get the following output:

1234.4568 12344567.89 123456789000000000. 1234.4568 12344567.8900 123456789000000000.000000

You will have rounding only for numbers with fixed point digits fitting in 32bits. I hope this is portable enough on most machines.

BTW, the terminology iDesiredPrecision is quite confusing, we are not speaking about precision which is the minimum number of significant digits, but about the number of digits in the fractional part.

BTW, are there any coding convention guide for DXL. I am a bit inspired by Visual Basic coding rules found here http://en.wikibooks.org/wiki/Visual_Basic/Coding_Standards.

Vincent.

Re: Real number formatting in DXL
SystemAdmin - Tue Feb 15 04:37:55 EST 2011

FYI I have posted an RFE http://www.ibm.com/developerworks/support/rational/rfe/execute?use_case=viewRfe&CR_ID=9524

Re: Real number formatting in DXL
jester76 - Tue Jun 11 14:45:20 EDT 2013

SystemAdmin - Mon Feb 14 12:42:59 EST 2011
I quickly sketched the following code to do the same thing. It uses regular expression and text manipulation.

I am quite surprised & disappointed that such a thing does not exist de-facto in DXL.

BTW, what markup do you use to post code with syntax highlight

string fStringOfReal(real rInput, int iDesiredPrecision, bool bTruncateZeros){
real rPrecision = (exp ((realOf iDesiredPrecision) * 2.302585092994046))
string sInput = ((realOf (intOf (rInput * rPrecision)))/rPrecision) ""
Regexp re = regexp2 "^(0-9+)?(\\.(0-9+))?$"

if(re sInput){
int iEndFactionalPart = start 2
if(iEndFactionalPart >= 0){
iEndFactionalPart = 1+iDesiredPrecision
if((length sInput) > iEndFactionalPart+1){
if bTruncateZeros and matches("^0*$",sInput(iEndFactionalPart+1):) then{
re = regexp2 "0+$"
if re sInput then{
sInput = sInput0 : ((start 0)-1)
}
}
else
sInput = sInput0 : iEndFactionalPart
}
else if(!bTruncateZeros){
Buffer oBuff = create
oBuff = sInput
int i = (length sInput) - iEndFactionalPart
for i in i : 0 by -1 do oBuff += "0"
sInput = stringOf oBuff
}
}
else if(bTruncateZeros){
re = regexp2 "0+$"
if re sInput then{
sInput = sInput0 : ((start 0)-1)
}
}
else
{
Buffer oBuff = create
oBuff = sInput
oBuff += "."
int i
for i in iDesiredPrecision : 1 by -1 do oBuff += "0"
sInput = stringOf oBuff
}

}

return sInput

}

Does anyone have this workig in DOORS environment? I an getting errors

Re: Real number formatting in DXL
llandale - Tue Jun 11 17:40:53 EDT 2013

SystemAdmin - Tue Feb 15 03:26:17 EST 2011
Thanks for pointing out the buggy behaviour. Frankly speaking I had written this from the top of my head in a few minutes. Here is a fix:


Regexp greSOR_1 = regexp2 
"^([0-9]+)?(\\.([0-9]+))?$" Regexp greSOR_2 = regexp2 
"^0*$"   string fStringOfReal(real rInput, 

int iDesiredPrecision, bool bTruncateZeros)
{ real rPrecision = (exp ((realOf iDesiredPrecision) * 2.302585092994046)) real rToBeRounded = rInput * rPrecision 

if rToBeRounded < 2147483646.5 and rToBeRounded > -2147483647.5 then rInput = (realOf (intOf rToBeRounded))/rPrecision string sInput = rInput 
""     

if(greSOR_1 sInput)
{ 

int iEndFactionalPart = start 2 

if(iEndFactionalPart >= 0)
{ iEndFactionalPart += iDesiredPrecision+1 

if((length sInput) > iEndFactionalPart)
{ 

if bTruncateZeros and (greSOR_2 sInput[iEndFactionalPart:]) then
{ 

int i = (length sInput) -1 

while (sInput[i : i] == 
"0") --i; sInput = sInput[0 : i]   
} 

else sInput = sInput[0 : (iEndFactionalPart-1)] 
} 

else 

if(bTruncateZeros)
{ 

int i = (length sInput) -1 

while (sInput[i : i] == 
"0") --i; sInput = sInput[0 : i] 
} 

else 
{ 

int i = (length sInput) - iEndFactionalPart 

if i >= 1 then
{ Buffer oBuff = create oBuff = sInput 

for i in i : 1 by -1 

do oBuff += 
"0" sInput = stringOf oBuff 
} 
} 
} 

else 

if(bTruncateZeros)
{ 

int i = (length sInput) -1 

while (sInput[i : i] == 
"0") --i; sInput = sInput[0 : i] 
} 

else 
{ Buffer oBuff = create oBuff = sInput oBuff += 
"." 

int i 

for i in iDesiredPrecision : 1 by -1 

do oBuff += 
"0" sInput = stringOf oBuff 
} 
}   

return sInput 
}


I have tried the following

print fStringOfReal(1234.456789, 4, 

true) 
"\n" print fStringOfReal(12344567.89, 4, 

true) 
"\n" print fStringOfReal(123456789000000000.0, 6, 

true) 
"\n" print fStringOfReal(1234.456789, 4, 

false) 
"\n" print fStringOfReal(12344567.89, 4, 

false) 
"\n" print fStringOfReal(123456789000000000.0, 6, 

false) 
"\n"


and it seems to work better, as I get the following output:

1234.4568 12344567.89 123456789000000000. 1234.4568 12344567.8900 123456789000000000.000000

You will have rounding only for numbers with fixed point digits fitting in 32bits. I hope this is portable enough on most machines.

BTW, the terminology iDesiredPrecision is quite confusing, we are not speaking about precision which is the minimum number of significant digits, but about the number of digits in the fractional part.

BTW, are there any coding convention guide for DXL. I am a bit inspired by Visual Basic coding rules found here http://en.wikibooks.org/wiki/Visual_Basic/Coding_Standards.

Vincent.

Finally took a look at this:

  • I see this function fails when the input has a lot of fractional digiits; e.g. "1234.45678901234".  The culprit here is about line #10  [string sInput = rInput ""] which automatically chops fractional digits to 6; which is the original problem this function was supposed to resolve.I see that this function imposes 'iDesiredPrecesion" always, even when zero or even -1.  The original code suggested it should be ignored when zero or less. 
  • I also see this function may decline to have any fractional part at all, but I think it should have at least a zero.  e.g. "123." should be "123.0" at a minimum. 
  • I see when iDesiredPrecion is -1; there is no period.

I agree however:

  • "Decimal" should be "Fractional".
  • "Whole" should be "Integer"
  • My "PrecisionDesired" should be "FractionalDigits_Desired"

I have no clue about the constants and so cannot debug this function.  I imagine if I understood then I could get rid of my hokey "17 digits" observation.

I'm wondering if a real solution would involve repetitively dividing by 10 ..err.. 10.0 and taking the "remainder" digit; one at a time; and build the resulting string right to left.  Then apply desired digits and truncation as appropriate.

-Louie

Re: Real number formatting in DXL
Adamarla - Thu Jun 13 03:47:15 EDT 2013

llandale - Mon Feb 14 11:34:47 EST 2011
Let me guess what you are asking. Wrote this a couple years ago.



/*        ====================== 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<NumZeros; i++) 
{  gl_bufStringOfReal       += 
'0' 
} gl_bufStringOfReal      += DecimalPart DecimalPart      = stringOf(gl_bufStringOfReal) 
}           
// end prefixing Decimal Part with needed Zeros 
// [6]Truncate the Imprecise right-most parts of DecimalPart 
// When there is no Whole Number part Precision, then we need 
//    all the Precision Decimal Parts including leading zeros. 
// When there is indeed Whole Number part Precision, then we need 
//   only the first so many digits of the DecimalPart 

if (WholePrecision > 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<">\traw: <">\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<DesiredPrecision - DecimalLen; i++) 
{  gl_bufStringOfReal    += 
'0' 
} DecimalPart     = stringOf(gl_bufStringOfReal) 
}   
// end Suffixing Decimal Part with needed trailing Zeros   
// [8] Truncate Decimal Part to max DesiredPrecision DecimalLen   = length(DecimalPart) 

if(DesiredPrecision > 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))   print (fStringOfReal(3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803483, 3, 

true)) 
"\n" print (fStringOfReal(3141592653589793238462643383279502884197169399375105820974944592307816.40628620899862803483, 3, 

true)) 
"\n" print (fStringOfReal(314159265358.97, 5, 

false)) 
"\n"

prints this:
3.141
3141592653589793100000000000000000000000000000000000000000000000000000.0
314159265358.96997

I see it truncates, returning 3.141 not 3.142. I see there is some approximation issues in precision.

Someone want to have a go at truncating, please feel free.

  • Louie

I have a nagging feeling that this clumsy function can be made vastly simpler, and that I'll be embarrased when someone posts a much more elegant solution. And I think I know who can do so!

I have posted my real formatting library code HERE