1 (* StringConvert.mod provides functions to convert numbers to and from strings.
3 Copyright (C) 2001-2024 Free Software Foundation, Inc.
4 Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
6 This file is part of GNU Modula-2.
8 GNU Modula-2 is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
13 GNU Modula-2 is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. *)
27 IMPLEMENTATION MODULE StringConvert
;
29 FROM SYSTEM
IMPORT ADDRESS
, ADR
;
30 FROM libc
IMPORT free
, printf
;
31 FROM libm
IMPORT powl
;
32 FROM M2RTS
IMPORT ErrorMessage
;
34 IMPORT DynamicStrings
;
36 FROM DynamicStrings
IMPORT InitString
,
37 InitStringChar
, InitStringCharStar
,
38 Mark
, ConCat
, Dup
, string
,
39 Slice
, Index
, char
, Assign
, Length
, Mult
,
40 RemoveWhitePrefix
, ConCatChar
, KillString
,
41 InitStringDB
, InitStringCharStarDB
,
42 InitStringCharDB
, MultDB
, DupDB
, SliceDB
;
44 FROM ldtoa
IMPORT Mode
, strtold
, ldtoa
;
45 IMPORT dtoa
; (* this fixes linking - as the C ldtoa uses dtoa *)
49 #undef GM2_DEBUG_STRINGCONVERT
50 #if defined(GM2_DEBUG_STRINGCONVERT)
51 # define InitString(X) InitStringDB(X, __FILE__, __LINE__)
52 # define InitStringCharStar(X) InitStringCharStarDB(X, __FILE__, __LINE__)
53 # define InitStringChar(X) InitStringCharDB(X, __FILE__, __LINE__)
54 # define Mult(X,Y) MultDB(X, Y, __FILE__, __LINE__)
55 # define Dup(X) DupDB(X, __FILE__, __LINE__)
56 # define Slice(X,Y,Z) SliceDB(X, Y, Z, __FILE__, __LINE__)
61 Assert - implement a simple assert.
64 PROCEDURE Assert (b
: BOOLEAN; file
: ARRAY OF CHAR; line
: CARDINAL; func
: ARRAY OF CHAR) ;
68 ErrorMessage('assert failed', file
, line
, func
)
77 PROCEDURE Max (a
, b
: CARDINAL) : CARDINAL ;
92 PROCEDURE Min (a
, b
: CARDINAL) : CARDINAL ;
104 LongMin - returns the smallest LONGCARD
107 PROCEDURE LongMin (a
, b
: LONGCARD) : LONGCARD ;
119 IsDigit - returns TRUE if, ch, lies between '0'..'9'.
122 PROCEDURE IsDigit (ch
: CHAR) : BOOLEAN ;
124 RETURN (ch
>='0') AND (ch
<='9')
129 IsDecimalDigitValid - returns the TRUE if, ch, is a base legal decimal digit.
130 If legal then the value is appended numerically onto, c.
133 PROCEDURE IsDecimalDigitValid (ch
: CHAR; base
: CARDINAL; VAR c
: CARDINAL) : BOOLEAN ;
135 IF IsDigit(ch
) AND (ORD(ch
)-ORD('0')<base
)
137 c
:= c
*base
+ (ORD(ch
)-ORD('0')) ;
142 END IsDecimalDigitValid
;
146 IsHexidecimalDigitValid - returns the TRUE if, ch, is a base legal hexidecimal digit.
147 If legal then the value is appended numerically onto, c.
150 PROCEDURE IsHexidecimalDigitValid (ch
: CHAR; base
: CARDINAL; VAR c
: CARDINAL) : BOOLEAN ;
152 IF (ch
>='a') AND (ch
<='f') AND (ORD(ch
)-ORD('a')+10<base
)
154 c
:= c
*base
+ (ORD(ch
)-ORD('a')+10) ;
156 ELSIF (ch
>='A') AND (ch
<='F') AND (ORD(ch
)-ORD('F')+10<base
)
158 c
:= c
*base
+ (ORD(ch
)-ORD('A')+10) ;
163 END IsHexidecimalDigitValid
;
167 IsDecimalDigitValidLong - returns the TRUE if, ch, is a base legal decimal digit.
168 If legal then the value is appended numerically onto, c.
171 PROCEDURE IsDecimalDigitValidLong (ch
: CHAR; base
: CARDINAL;
172 VAR c
: LONGCARD) : BOOLEAN ;
174 IF IsDigit(ch
) AND (ORD(ch
)-ORD('0')<base
)
176 c
:= c
* VAL(LONGCARD, base
+ (ORD(ch
)-ORD('0'))) ;
181 END IsDecimalDigitValidLong
;
185 IsHexidecimalDigitValidLong - returns the TRUE if, ch, is a base legal hexidecimal digit.
186 If legal then the value is appended numerically onto, c.
189 PROCEDURE IsHexidecimalDigitValidLong (ch
: CHAR; base
: CARDINAL; VAR c
: LONGCARD) : BOOLEAN ;
191 IF (ch
>='a') AND (ch
<='f') AND (ORD(ch
)-ORD('a')+10<base
)
193 c
:= c
* VAL(LONGCARD, base
+ (ORD(ch
)-ORD('a')+10)) ;
195 ELSIF (ch
>='A') AND (ch
<='F') AND (ORD(ch
)-ORD('F')+10<base
)
197 c
:= c
* VAL(LONGCARD, base
+ (ORD(ch
)-ORD('A')+10)) ;
202 END IsHexidecimalDigitValidLong
;
206 IsDecimalDigitValidShort - returns the TRUE if, ch, is a base legal decimal digit.
207 If legal then the value is appended numerically onto, c.
210 PROCEDURE IsDecimalDigitValidShort (ch
: CHAR; base
: CARDINAL; VAR c
: SHORTCARD
) : BOOLEAN ;
212 IF IsDigit(ch
) AND (ORD(ch
)-ORD('0')<base
)
214 c
:= c
* VAL(SHORTCARD
, base
+ (ORD(ch
)-ORD('0'))) ;
219 END IsDecimalDigitValidShort
;
223 IsHexidecimalDigitValidShort - returns the TRUE if, ch, is a base legal hexidecimal digit.
224 If legal then the value is appended numerically onto, c.
227 PROCEDURE IsHexidecimalDigitValidShort (ch
: CHAR; base
: CARDINAL; VAR c
: SHORTCARD
) : BOOLEAN ;
229 IF (ch
>='a') AND (ch
<='f') AND (ORD(ch
)-ORD('a')+10<base
)
231 c
:= c
* VAL(SHORTCARD
, base
+ (ORD(ch
)-ORD('a')+10)) ;
233 ELSIF (ch
>='A') AND (ch
<='F') AND (ORD(ch
)-ORD('F')+10<base
)
235 c
:= c
* VAL(SHORTCARD
, base
+ (ORD(ch
)-ORD('A')+10)) ;
240 END IsHexidecimalDigitValidShort
;
244 IntegerToString - converts INTEGER, i, into a String. The field with can be specified
245 if non zero. Leading characters are defined by padding and this
246 function will prepend a + if sign is set to TRUE.
247 The base allows the caller to generate binary, octal, decimal, hexidecimal
248 numbers. The value of lower is only used when hexidecimal numbers are
249 generated and if TRUE then digits abcdef are used, and if FALSE then ABCDEF
253 PROCEDURE IntegerToString (i
: INTEGER; width
: CARDINAL; padding
: CHAR; sign
: BOOLEAN;
254 base
: CARDINAL; lower
: BOOLEAN) : String
;
263 (* remember that -15 MOD 4 = 1 in Modula-2 *)
264 c
:= VAL(CARDINAL, ABS(i
+1))+1 ;
267 RETURN( ConCat(IntegerToString(-VAL(INTEGER, c
DIV base
),
268 width
-1, padding
, sign
, base
, lower
),
269 Mark(IntegerToString(c
MOD base
, 0, ' ', FALSE, base
, lower
))) )
271 RETURN( ConCat(IntegerToString(-VAL(INTEGER, c
DIV base
),
272 0, padding
, sign
, base
, lower
),
273 Mark(IntegerToString(c
MOD base
, 0, ' ', FALSE, base
, lower
))) )
287 IF i
>VAL(INTEGER, base
)-1
289 s
:= ConCat(ConCat(s
, Mark(IntegerToString(VAL(CARDINAL, i
) DIV base
, 0, ' ', FALSE, base
, lower
))),
290 Mark(IntegerToString(VAL(CARDINAL, i
) MOD base
, 0, ' ', FALSE, base
, lower
)))
294 s
:= ConCat(s
, Mark(InitStringChar(CHR(VAL(CARDINAL, i
)+ORD('0')))))
298 s
:= ConCat(s
, Mark(InitStringChar(CHR(VAL(CARDINAL, i
)+ORD('a')-10))))
300 s
:= ConCat(s
, Mark(InitStringChar(CHR(VAL(CARDINAL, i
)+ORD('A')-10))))
304 IF width
>DynamicStrings.
Length(s
)
306 RETURN( ConCat(Mult(Mark(InitStringChar(padding
)), width
-DynamicStrings.
Length(s
)), Mark(s
)) )
309 END IntegerToString
;
313 CardinalToString - converts CARDINAL, c, into a String. The field with can be specified
314 if non zero. Leading characters are defined by padding.
315 The base allows the caller to generate binary, octal, decimal, hexidecimal
316 numbers. The value of lower is only used when hexidecimal numbers are
317 generated and if TRUE then digits abcdef are used, and if FALSE then ABCDEF
321 PROCEDURE CardinalToString (c
: CARDINAL; width
: CARDINAL; padding
: CHAR;
322 base
: CARDINAL; lower
: BOOLEAN) : String
;
326 s
:= InitString('') ;
329 s
:= ConCat(ConCat(s
, Mark(CardinalToString(c
DIV base
, 0, ' ', base
, lower
))),
330 Mark(CardinalToString(c
MOD base
, 0, ' ', base
, lower
)))
334 s
:= ConCat(s
, Mark(InitStringChar(CHR(c
+ORD('0')))))
338 s
:= ConCat(s
, Mark(InitStringChar(CHR(c
+ORD('a')-10))))
340 s
:= ConCat(s
, Mark(InitStringChar(CHR(c
+ORD('A')-10))))
344 IF width
>DynamicStrings.
Length(s
)
346 RETURN( ConCat(Mult(Mark(InitStringChar(padding
)), width
-DynamicStrings.
Length(s
)), s
) )
349 END CardinalToString
;
353 LongIntegerToString - converts LONGINT, i, into a String. The field with
354 can be specified if non zero. Leading characters
355 are defined by padding and this function will
356 prepend a + if sign is set to TRUE.
357 The base allows the caller to generate binary,
358 octal, decimal, hexidecimal numbers.
359 The value of lower is only used when hexidecimal
360 numbers are generated and if TRUE then digits
361 abcdef are used, and if FALSE then ABCDEF are used.
364 PROCEDURE LongIntegerToString (i
: LONGINT; width
: CARDINAL; padding
: CHAR;
365 sign
: BOOLEAN; base
: CARDINAL; lower
: BOOLEAN) : String
;
375 (* remember that -15 MOD 4 is 1 in Modula-2, and although ABS(MIN(LONGINT)+1)
376 is very likely MAX(LONGINT), it is safer not to assume this is the case *)
377 c
:= VAL(LONGCARD, ABS(i
+1))+1 ;
380 RETURN( ConCat(LongIntegerToString(-VAL(LONGINT, c
DIV VAL(LONGCARD, base
)),
381 width
-1, padding
, sign
, base
, lower
),
382 Mark(LongIntegerToString(c
MOD VAL(LONGCARD, base
), 0, ' ', FALSE, base
, lower
))) )
384 RETURN( ConCat(LongIntegerToString(-VAL(LONGINT, c
DIV VAL(LONGCARD, base
)),
385 0, padding
, sign
, base
, lower
),
386 Mark(LongIntegerToString(c
MOD VAL(LONGCARD, base
), 0, ' ', FALSE, base
, lower
))) )
400 IF i
>VAL(LONGINT, base
-1)
402 s
:= ConCat(ConCat(s
, Mark(LongIntegerToString(i
DIV VAL(LONGINT, base
), 0, ' ', FALSE, base
, lower
))),
403 Mark(LongIntegerToString(i
MOD VAL(LONGINT, base
), 0, ' ', FALSE, base
, lower
)))
407 s
:= ConCat(s
, Mark(InitStringChar(CHR(VAL(CARDINAL, i
)+ORD('0')))))
411 s
:= ConCat(s
, Mark(InitStringChar(CHR(VAL(CARDINAL, i
)+ORD('a')-10))))
413 s
:= ConCat(s
, Mark(InitStringChar(CHR(VAL(CARDINAL, i
)+ORD('A')-10))))
417 IF width
>DynamicStrings.
Length(s
)
419 RETURN( ConCat(Mult(Mark(InitStringChar(padding
)), width
-DynamicStrings.
Length(s
)), s
) )
422 END LongIntegerToString
;
426 StringToLongInteger - converts a string, s, of, base, into an LONGINT.
427 Leading white space is ignored. It stops converting
428 when either the string is exhausted or if an illegal
430 The parameter found is set TRUE if a number was found.
433 PROCEDURE StringToLongInteger (s
: String
; base
: CARDINAL; VAR found
: BOOLEAN) : LONGINT ;
439 s
:= RemoveWhitePrefix(s
) ; (* returns a new string, s *)
440 l
:= DynamicStrings.
Length(s
) ;
446 (* parse leading + and - *)
447 WHILE (char(s
, n
)='-') OR (char(s
, n
)='+') DO
450 negative
:= NOT negative
454 WHILE (n
<l
) AND (IsDecimalDigitValidLong(char(s
, n
), base
, c
) OR
455 IsHexidecimalDigitValidLong(char(s
, n
), base
, c
)) DO
463 RETURN( -VAL(LONGINT, LongMin(VAL(LONGCARD, MAX(LONGINT))+1, c
)) )
465 RETURN( VAL(LONGINT, LongMin(MAX(LONGINT), c
)) )
467 END StringToLongInteger
;
471 StringToInteger - converts a string, s, of, base, into an INTEGER.
472 Leading white space is ignored. It stops converting
473 when either the string is exhausted or if an illegal
475 The parameter found is set TRUE if a number was found.
478 PROCEDURE StringToInteger (s
: String
; base
: CARDINAL;
479 VAR found
: BOOLEAN) : INTEGER ;
485 s
:= RemoveWhitePrefix(s
) ; (* returns a new string, s *)
486 l
:= DynamicStrings.
Length(s
) ;
492 (* parse leading + and - *)
493 WHILE (char(s
, n
)='-') OR (char(s
, n
)='+') DO
496 negative
:= NOT negative
500 WHILE (n
<l
) AND (IsDecimalDigitValid(char(s
, n
), base
, c
) OR
501 IsHexidecimalDigitValid(char(s
, n
), base
, c
)) DO
509 RETURN( -VAL(INTEGER, Min(VAL(CARDINAL, MAX(INTEGER))+1, c
)) )
511 RETURN( VAL(INTEGER, Min(MAX(INTEGER), c
)) )
513 END StringToInteger
;
517 StringToCardinal - converts a string, s, of, base, into a CARDINAL.
518 Leading white space is ignored. It stops converting
519 when either the string is exhausted or if an illegal
521 The parameter found is set TRUE if a number was found.
524 PROCEDURE StringToCardinal (s
: String
; base
: CARDINAL;
525 VAR found
: BOOLEAN) : CARDINAL ;
530 s
:= RemoveWhitePrefix(s
) ; (* returns a new string, s *)
531 l
:= DynamicStrings.
Length(s
) ;
536 (* parse leading + *)
537 WHILE (char(s
, n
)='+') DO
540 WHILE (n
<l
) AND (IsDecimalDigitValid(char(s
, n
), base
, c
) OR
541 IsHexidecimalDigitValid(char(s
, n
), base
, c
)) DO
548 END StringToCardinal
;
552 stoi - decimal string to INTEGER
555 PROCEDURE stoi (s
: String
) : INTEGER ;
559 RETURN( StringToInteger(s
, 10, found
) )
564 itos - integer to decimal string.
567 PROCEDURE itos (i
: INTEGER; width
: CARDINAL; padding
: CHAR; sign
: BOOLEAN) : String
;
569 RETURN( IntegerToString(i
, width
, padding
, sign
, 10, FALSE) )
574 ctos - cardinal to decimal string.
577 PROCEDURE ctos (c
: CARDINAL; width
: CARDINAL; padding
: CHAR) : String
;
579 RETURN( CardinalToString(c
, width
, padding
, 10, FALSE) )
584 stoc - decimal string to CARDINAL
587 PROCEDURE stoc (s
: String
) : CARDINAL ;
591 RETURN( StringToCardinal(s
, 10, found
) )
596 hstoi - hexidecimal string to INTEGER
599 PROCEDURE hstoi (s
: String
) : INTEGER ;
603 RETURN( StringToInteger(s
, 16, found
) )
608 ostoi - octal string to INTEGER
611 PROCEDURE ostoi (s
: String
) : INTEGER ;
615 RETURN( StringToInteger(s
, 8, found
) )
620 bstoi - binary string to INTEGER
623 PROCEDURE bstoi (s
: String
) : INTEGER ;
627 RETURN( StringToInteger(s
, 2, found
) )
632 hstoc - hexidecimal string to CARDINAL
635 PROCEDURE hstoc (s
: String
) : CARDINAL ;
639 RETURN( StringToCardinal(s
, 16, found
) )
644 ostoc - octal string to CARDINAL
647 PROCEDURE ostoc (s
: String
) : CARDINAL ;
651 RETURN( StringToCardinal(s
, 8, found
) )
656 bstoc - binary string to CARDINAL
659 PROCEDURE bstoc (s
: String
) : CARDINAL ;
663 RETURN( StringToCardinal(s
, 2, found
) )
667 (* **********************************************************************
668 R e a l a n d L o n g R e a l c o n v e r s i o n
669 ********************************************************************** *)
673 ToThePower10 - returns a LONGREAL containing the value of v * 10^power.
676 PROCEDURE ToThePower10 (v
: LONGREAL; power
: INTEGER) : LONGREAL;
698 DetermineSafeTruncation - we wish to use TRUNC when converting REAL/LONGREAL
699 into a string for the non fractional component.
700 However we need a simple method to
701 determine the maximum safe truncation value.
704 PROCEDURE DetermineSafeTruncation () : CARDINAL ;
706 MaxPowerOfTen
: REAL ;
707 LogPower
: CARDINAL ;
709 MaxPowerOfTen
:= 1.0 ;
711 WHILE MaxPowerOfTen
*10.0<FLOAT(MAX(INTEGER) DIV 10) DO
712 MaxPowerOfTen
:= MaxPowerOfTen
* 10.0 ;
716 END DetermineSafeTruncation
;
720 LongrealToString - converts a LONGREAL number, Real, which has,
721 TotalWidth, and FractionWidth into a string.
722 It uses decimal notation.
726 LongrealToString(1.0, 4, 2) -> '1.00'
727 LongrealToString(12.3, 5, 2) -> '12.30'
728 LongrealToString(12.3, 6, 2) -> ' 12.30'
729 LongrealToString(12.3, 6, 3) -> '12.300'
731 if total width is too small then the fraction
734 LongrealToString(12.3, 5, 3) -> '12.30'
736 Positive numbers do not have a '+' prepended.
737 Negative numbers will have a '-' prepended and
738 the TotalWidth will need to be large enough
739 to contain the sign, whole number, '.' and
740 fractional components.
743 PROCEDURE LongrealToString (x
: LONGREAL;
744 TotalWidth
, FractionWidth
: CARDINAL) : String
;
746 maxprecision
: BOOLEAN ;
755 maxprecision
:= TRUE ;
756 r
:= ldtoa(x
, decimaldigits
, 100, point
, sign
)
758 r
:= ldtoa(x
, decimaldigits
, 100, point
, sign
)
760 s
:= InitStringCharStar(r
) ;
762 l
:= DynamicStrings.
Length(s
) ;
765 s
:= ConCat(s
, Mark(Mult(Mark(InitStringChar('0')), point
-l
))) ;
766 s
:= ConCat(s
, Mark(InitString('.0'))) ;
767 IF (NOT maxprecision
) AND (FractionWidth
>0)
770 IF VAL(INTEGER, FractionWidth
)>point
-l
772 s
:= ConCat(s
, Mark(Mult(Mark(InitString('0')), FractionWidth
)))
777 s
:= ConCat(Mult(Mark(InitStringChar('0')), -point
), Mark(s
)) ;
778 l
:= DynamicStrings.
Length(s
) ;
779 s
:= ConCat(InitString('0.'), Mark(s
)) ;
780 IF (NOT maxprecision
) AND (l
<VAL(INTEGER, FractionWidth
))
782 s
:= ConCat(s
, Mark(Mult(Mark(InitString('0')), VAL(INTEGER, FractionWidth
)-l
)))
787 s
:= ConCat(InitString('0.'), Mark(Slice(Mark(s
), point
, 0)))
789 s
:= ConCat(ConCatChar(Slice(Mark(s
), 0, point
), '.'),
790 Mark(Slice(Mark(s
), point
, 0)))
792 IF (NOT maxprecision
) AND (l
-point
<VAL(INTEGER, FractionWidth
))
794 s
:= ConCat(s
, Mark(Mult(Mark(InitString('0')), VAL(INTEGER, FractionWidth
)-(l
-point
))))
797 IF DynamicStrings.
Length(s
)>TotalWidth
803 s
:= Slice(Mark(ToDecimalPlaces(s
, FractionWidth
)), 0, TotalWidth
-1) ;
804 s
:= ConCat(InitStringChar('-'), Mark(s
)) ;
807 (* minus 1 because all results will include a '.' *)
808 s
:= Slice(Mark(ToDecimalPlaces(s
, FractionWidth
)), 0, TotalWidth
) ;
813 s
:= ToDecimalPlaces(s
, FractionWidth
) ;
814 s
:= ConCat(InitStringChar('-'), Mark(s
)) ;
817 (* minus 1 because all results will include a '.' *)
818 s
:= ToDecimalPlaces(s
, FractionWidth
)
822 IF DynamicStrings.
Length(s
)<TotalWidth
824 s
:= ConCat(Mult(Mark(InitStringChar(' ')), TotalWidth
-DynamicStrings.
Length(s
)), Mark(s
))
827 END LongrealToString
;
831 StringToLongreal - returns a LONGREAL and sets found to TRUE if a legal number is seen.
834 PROCEDURE StringToLongreal (s
: String
; VAR found
: BOOLEAN) : LONGREAL ;
839 s
:= RemoveWhitePrefix(s
) ; (* new string is created *)
840 value
:= strtold(string(s
), error
) ;
844 END StringToLongreal
;
851 PROCEDURE rtos (r
: REAL; TotalWidth
, FractionWidth
: CARDINAL) : String
;
859 stor - returns a REAL given a string.
862 PROCEDURE stor (s
: String
) : REAL ;
866 RETURN( VAL(REAL, StringToLongreal(s
, found
)) )
874 PROCEDURE lrtos (r
: LONGREAL; TotalWidth
, FractionWidth
: CARDINAL) : String
;
882 stolr - returns a LONGREAL given a string.
885 PROCEDURE stolr (s
: String
) : LONGREAL ;
889 RETURN( StringToLongreal(s
, found
) )
894 LongCardinalToString - converts LONGCARD, c, into a String. The field
895 width can be specified if non zero. Leading
896 characters are defined by padding.
897 The base allows the caller to generate binary,
898 octal, decimal, hexidecimal numbers.
899 The value of lower is only used when hexidecimal
900 numbers are generated and if TRUE then digits
901 abcdef are used, and if FALSE then ABCDEF are used.
904 PROCEDURE LongCardinalToString (c
: LONGCARD; width
: CARDINAL; padding
: CHAR;
905 base
: CARDINAL; lower
: BOOLEAN) : String
;
909 s
:= InitString('') ;
910 IF c
>VAL(LONGCARD, base
-1)
912 s
:= ConCat(ConCat(s
, LongCardinalToString(c
DIV VAL(LONGCARD, base
), 0, ' ', base
, lower
)),
913 LongCardinalToString(c
MOD VAL(LONGCARD, base
), 0, ' ', base
, lower
))
917 s
:= ConCat(s
, InitStringChar(CHR(VAL(CARDINAL, c
)+ORD('0'))))
921 s
:= ConCat(s
, InitStringChar(CHR(VAL(CARDINAL, c
)+ORD('a')-10)))
923 s
:= ConCat(s
, InitStringChar(CHR(VAL(CARDINAL, c
)+ORD('A')-10)))
927 IF width
>DynamicStrings.
Length(s
)
929 RETURN( ConCat(Mult(Mark(InitStringChar(padding
)), width
-DynamicStrings.
Length(s
)), s
) )
932 END LongCardinalToString
;
936 StringToLongCardinal - converts a string, s, of, base, into a LONGCARD.
937 Leading white space is ignored. It stops converting
938 when either the string is exhausted or if an illegal
940 The parameter found is set TRUE if a number was found.
943 PROCEDURE StringToLongCardinal (s
: String
; base
: CARDINAL; VAR found
: BOOLEAN) : LONGCARD ;
948 s
:= RemoveWhitePrefix(s
) ; (* returns a new string, s *)
949 l
:= DynamicStrings.
Length(s
) ;
954 (* parse leading + *)
955 WHILE (char(s
, n
)='+') DO
958 WHILE (n
<l
) AND (IsDecimalDigitValidLong(char(s
, n
), base
, c
) OR
959 IsHexidecimalDigitValidLong(char(s
, n
), base
, c
)) DO
966 END StringToLongCardinal
;
970 ShortCardinalToString - converts SHORTCARD, c, into a String. The field
971 width can be specified if non zero. Leading
972 characters are defined by padding.
973 The base allows the caller to generate binary,
974 octal, decimal, hexidecimal numbers.
975 The value of lower is only used when hexidecimal
976 numbers are generated and if TRUE then digits
977 abcdef are used, and if FALSE then ABCDEF are used.
980 PROCEDURE ShortCardinalToString (c
: SHORTCARD
; width
: CARDINAL; padding
: CHAR;
981 base
: CARDINAL; lower
: BOOLEAN) : String
;
985 s
:= InitString('') ;
986 IF VAL(CARDINAL, c
)>base
-1
988 s
:= ConCat(ConCat(s
, ShortCardinalToString(c
DIV VAL(SHORTCARD
, base
), 0, ' ', base
, lower
)),
989 ShortCardinalToString(c
MOD VAL(SHORTCARD
, base
), 0, ' ', base
, lower
))
993 s
:= ConCat(s
, InitStringChar(CHR(VAL(CARDINAL, c
)+ORD('0'))))
997 s
:= ConCat(s
, InitStringChar(CHR(VAL(CARDINAL, c
)+ORD('a')-10)))
999 s
:= ConCat(s
, InitStringChar(CHR(VAL(CARDINAL, c
)+ORD('A')-10)))
1003 IF width
>DynamicStrings.
Length(s
)
1005 RETURN( ConCat(Mult(Mark(InitStringChar(padding
)), width
-DynamicStrings.
Length(s
)), s
) )
1008 END ShortCardinalToString
;
1012 StringToShortCardinal - converts a string, s, of, base, into a SHORTCARD.
1013 Leading white space is ignored. It stops converting
1014 when either the string is exhausted or if an illegal
1016 The parameter found is set TRUE if a number was found.
1019 PROCEDURE StringToShortCardinal (s
: String
; base
: CARDINAL;
1020 VAR found
: BOOLEAN) : SHORTCARD
;
1025 s
:= RemoveWhitePrefix(s
) ; (* returns a new string, s *)
1026 l
:= DynamicStrings.
Length(s
) ;
1031 (* parse leading + *)
1032 WHILE (char(s
, n
)='+') DO
1035 WHILE (n
<l
) AND (IsDecimalDigitValidShort(char(s
, n
), base
, c
) OR
1036 IsHexidecimalDigitValidShort(char(s
, n
), base
, c
)) DO
1041 s
:= KillString(s
) ;
1043 END StringToShortCardinal
;
1047 ToDecimalPlaces - returns a floating point or base 10 integer
1048 string which is accurate to, n, decimal
1049 places. It will return a new String
1050 and, s, will be destroyed.
1051 Decimal places yields, n, digits after
1056 rounded to the following decimal places yields
1065 PROCEDURE ToDecimalPlaces (s
: String
; n
: CARDINAL) : String
;
1069 Assert(IsDigit(char(s
, 0)) OR (char(s
, 0)='.'), __FILE__
, __LINE__
, __FUNCTION__
) ;
1070 point
:= Index(s
, '.', 0) ;
1075 RETURN( ConCat(ConCat(s
, Mark(InitStringChar('.'))), Mult(Mark(InitStringChar('0')), n
)) )
1080 s
:= doDecimalPlaces(s
, n
) ;
1081 (* if the last character is '.' remove it *)
1082 IF (DynamicStrings.
Length(s
)>0) AND (char(s
, -1)='.')
1084 RETURN( Slice(Mark(s
), 0, -1) )
1088 END ToDecimalPlaces
;
1092 doDecimalPlaces - returns a string which is accurate to
1093 n decimal places. It returns a new String
1094 and, s, will be destroyed.
1097 PROCEDURE doDecimalPlaces (s
: String
; n
: CARDINAL) : String
;
1105 l
:= DynamicStrings.
Length(s
) ;
1108 point
:= Index(s
, '.', 0) ;
1111 s
:= Slice(Mark(s
), 1, 0)
1114 s
:= ConCat(Slice(Mark(s
), 0, point
),
1115 Mark(Slice(Mark(s
), point
+1, 0)))
1117 s
:= Slice(Mark(s
), 0, point
)
1119 l
:= DynamicStrings.
Length(s
) ;
1123 (* skip over leading zeros *)
1124 WHILE (i
<l
) AND (char(s
, i
)='0') DO
1127 (* was the string full of zeros? *)
1128 IF (i
=l
) AND (char(s
, i
-1)='0')
1130 s
:= KillString(s
) ;
1131 s
:= ConCat(InitString('0.'), Mark(Mult(Mark(InitStringChar('0')), n
))) ;
1135 (* add a leading zero in case we need to overflow the carry *)
1136 (* insert leading zero *)
1137 s
:= ConCat(InitStringChar('0'), Mark(s
)) ;
1138 INC(point
) ; (* and move point position to correct place *)
1139 l
:= DynamicStrings.
Length(s
) ; (* update new length *)
1141 WHILE (n
>1) AND (i
<l
) DO
1148 hundreths
:= Slice(Mark(s
), i
+1, i
+3) ;
1150 IF stoc(hundreths
)>=50
1152 s
:= carryOne(Mark(s
), i
)
1154 hundreths
:= KillString(hundreths
)
1158 tenths
:= Slice(Mark(s
), i
+1, i
+2) ;
1162 s
:= carryOne(Mark(s
), i
)
1164 tenths
:= KillString(tenths
)
1166 (* check whether we need to remove the leading zero *)
1169 s
:= Slice(Mark(s
), 1, 0) ;
1175 s
:= Slice(Mark(s
), 0, i
) ;
1176 l
:= DynamicStrings.
Length(s
) ;
1179 s
:= ConCat(s
, Mult(Mark(InitStringChar('0')), point
-l
))
1182 (* re-insert the point *)
1187 s
:= ConCat(InitStringChar('.'), Mark(s
))
1189 s
:= ConCat(ConCatChar(Slice(Mark(s
), 0, point
), '.'),
1190 Mark(Slice(Mark(s
), point
, 0)))
1194 END doDecimalPlaces
;
1198 ToSigFig - returns a floating point or base 10 integer
1199 string which is accurate to, n, significant
1200 figures. It will return a new String
1201 and, s, will be destroyed.
1206 rounded to the following significant figures yields
1215 PROCEDURE ToSigFig (s
: String
; n
: CARDINAL) : String
;
1220 Assert(IsDigit(char(s
, 0)) OR (char(s
, 0)='.'), __FILE__
, __LINE__
, __FUNCTION__
) ;
1221 point
:= Index(s
, '.', 0) ;
1224 poTen
:= DynamicStrings.
Length(s
)
1228 s
:= doSigFig(s
, n
) ;
1229 (* if the last character is '.' remove it *)
1230 IF (DynamicStrings.
Length(s
)>0) AND (char(s
, -1)='.')
1232 RETURN( Slice(Mark(s
), 0, -1) )
1234 IF poTen
>DynamicStrings.
Length(s
)
1236 s
:= ConCat(s
, Mark(Mult(Mark(InitStringChar('0')), poTen
-DynamicStrings.
Length(s
))))
1244 doSigFig - returns a string which is accurate to
1245 n decimal places. It returns a new String
1246 and, s, will be destroyed.
1249 PROCEDURE doSigFig (s
: String
; n
: CARDINAL) : String
;
1257 l
:= DynamicStrings.
Length(s
) ;
1260 point
:= Index(s
, '.', 0) ;
1265 s
:= Slice(Mark(s
), 1, 0)
1268 s
:= ConCat(Slice(Mark(s
), 0, point
),
1269 Mark(Slice(Mark(s
), point
+1, 0)))
1271 s
:= Slice(Mark(s
), 0, point
)
1276 l
:= DynamicStrings.
Length(s
) ;
1280 (* skip over leading zeros *)
1281 WHILE (i
<l
) AND (char(s
, i
)='0') DO
1284 (* was the string full of zeros? *)
1285 IF (i
=l
) AND (char(s
, i
-1)='0')
1287 (* truncate string *)
1288 s
:= Slice(Mark(s
), 0, n
) ;
1292 (* add a leading zero in case we need to overflow the carry *)
1293 z
:= i
; (* remember where we inserted zero *)
1296 s
:= ConCat(InitStringChar('0'), Mark(s
))
1298 s
:= ConCat(ConCatChar(Slice(Mark(s
), 0, i
), '0'),
1299 Mark(Slice(Mark(s
), i
, 0)))
1301 INC(n
) ; (* and increase the number of sig figs needed *)
1302 l
:= DynamicStrings.
Length(s
) ;
1303 WHILE (n
>1) AND (i
<l
) DO
1310 hundreths
:= Slice(Mark(s
), i
+1, i
+3) ;
1312 IF stoc(hundreths
)>=50
1314 s
:= carryOne(Mark(s
), i
)
1316 hundreths
:= KillString(hundreths
)
1320 tenths
:= Slice(Mark(s
), i
+1, i
+2) ;
1324 s
:= carryOne(Mark(s
), i
)
1326 tenths
:= KillString(tenths
)
1328 (* check whether we need to remove the leading zero *)
1333 s
:= Slice(Mark(s
), z
+1, 0)
1335 s
:= ConCat(Slice(Mark(s
), 0, z
),
1336 Mark(Slice(Mark(s
), z
+1, 0)))
1338 l
:= DynamicStrings.
Length(s
)
1344 s
:= Slice(Mark(s
), 0, i
) ;
1345 l
:= DynamicStrings.
Length(s
) ;
1348 s
:= ConCat(s
, Mult(Mark(InitStringChar('0')), point
-l
))
1351 (* re-insert the point *)
1356 s
:= ConCat(InitStringChar('.'), Mark(s
))
1358 s
:= ConCat(ConCatChar(Slice(Mark(s
), 0, point
), '.'),
1359 Mark(Slice(Mark(s
), point
, 0)))
1367 carryOne - add a carry at position, i.
1370 PROCEDURE carryOne (s
: String
; i
: CARDINAL) : String
;
1374 IF IsDigit(char(s
, i
))
1380 s
:= ConCat(InitStringChar('1'), Mark(s
)) ;
1383 s
:= ConCat(ConCatChar(Slice(Mark(s
), 0, i
), '0'),
1384 Mark(Slice(Mark(s
), i
+1, 0))) ;
1385 RETURN carryOne(s
, i
-1)
1390 s
:= ConCat(InitStringChar(CHR(ORD(char(s
, i
))+1)),
1391 Mark(Slice(Mark(s
), i
+1, 0)))
1393 s
:= ConCat(ConCatChar(Slice(Mark(s
), 0, i
),
1394 CHR(ORD(char(s
, i
))+1)),
1395 Mark(Slice(Mark(s
), i
+1, 0)))