aboutsummaryrefslogtreecommitdiff
path: root/sys/fmtio/ctod.x
blob: 06b1d3510233d13673cb8c847779bd3695a49813 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.

include <mach.h>
include <ctype.h>

define	DECIMAL		10

.help
.nf _________________________________________________________________________
Attempt to convert a string to a number:  nchar = ctod (str, ip, dval)
The index IP must be set to the first character to be scanned upon entry
to CTOD, and will be left pointing at the first untranslated character.

If the string is successfully converted, the number of characters used
is returned as the function argument.  If the string (or the first few
characters of the string) cannot be interpreted as a number, zero will be
returned.  Note that even if no numeric characters are encountered, the
index IP may be incremented, if leading whitespace is encountered (but the
return value N will still be zero).

The upper case string "INDEF" is a legal real number, as is "." (. == 0.0).
Sexagesimal numbers are permitted.  Excess digits of precision are ignored.
Out of range exponents are detected, and result in the value INDEF being
returned (this is not considered an ERROR condition).  Any number with an
exponent greater than or equal to MAX_EXPONENT is interpreted as INDEF,
regardless of the mantissa.  The number need not contain a decimal point.

Lexical form of a sexagesimal number:

	D :==	[0-9]		numeric digit
	E :==	[eEdD]		exponent symbol

	({D}*:)+{D}*(".")?{D}*({E}("+"|"-")?{D}+)?

The format for sexagesimal numbers is fairly permissive.  Any number of
colon fields are permitted, with any number of digits (including zero) in
each field.  An exponent may occur at the end of a sexagesimal number.
Leading zeros may be omitted in the fields.
.endhelp ____________________________________________________________________


# CTOD -- Convert a string to double precision real.

int procedure ctod (str, ip, dval)

char	str[ARB]		# string to be converted
int	ip			# pointer into str
double	dval			# receives binary value

bool	neg
char	dig[MAX_DIGITS]
int	j, e, vexp, ip_start
long	expon
double	value, scalar
int	strncmp(), gctol(), stridx()

begin
	while (IS_WHITE (str[ip]))			# skip whitespace
	    ip = ip + 1
	ip_start = ip
	dval = INDEFD
				
	if (strncmp (str[ip], "INDEF", 5) == 0) {	# check for "INDEF"
	    for (ip=ip+5;  IS_ALPHA (str[ip]) || str[ip] == '_';  ip=ip+1)
		;
	    return (ip - ip_start)
	}

	neg = (str[ip] == '-')				# check for sign
	if (neg || str[ip] == '+')
	    ip = ip + 1

	while (str[ip] == '0')				# ignore leading zeros
	    ip = ip + 1

	dval = 0.0
	scalar = 60.0

	repeat {					# accumulate digits
	    for (j=1;  j <= MAX_DIGITS && IS_DIGIT(str[ip]);  j=j+1) {
		dig[j] = str[ip]
		ip = ip + 1
	    }

	    for (e=0;  IS_DIGIT(str[ip]);  e=e+1)	# ignore the rest
		ip = ip + 1

	    scalar = scalar / 60.0
	    if (ip > 1 && stridx(str[ip], "'\":dDhHmMsS")>0) {  # sexagesimal?
		ip = ip + 1
		dig[j] = EOS
		value = 0.0				# convert digits
		for (j=1;  dig[j] != EOS;  j=j+1)
		    value = value * 10.0D0 + TO_INTEG (dig[j])
		dval = dval + value * scalar * (10.0 ** e)

		while (str[ip] != EOS && 		# multiple spaces etc
		    stridx(str[ip]," '\":dDhHmMsS")>0)
		        ip = ip + 1
	    } else
		break
	}

	if (str[ip] == '.') {				# check for a fraction
	    ip = ip + 1
	    if (j == 1)					# skip leading zeros
		while (str[ip] == '0') {		# if str = "0.00ddd"
		    ip = ip + 1
		    e = e - 1
		}
	    for (;  j <= MAX_DIGITS && IS_DIGIT(str[ip]);  j=j+1) {
		dig[j] = str[ip]
		e = e - 1				# adjust scale factor
		ip = ip + 1
	    }						# discard insignificant
	    while (IS_DIGIT (str[ip]))			# fractional digits
		ip = ip + 1
	}

	dig[j] = EOS					# no more digits
	vexp = e + j - 1				# save for ovfl check
	if (ip == ip_start)				# not a number?
	    return (0)

	value = 0.0					# convert the mantissa
	for (j=1;  dig[j] != EOS;  j=j+1)
	    value = value * 10.0D0 + TO_INTEG (dig[j])
	if (e != 0)
	    value = value * (10.0D0 ** e)		# scale by e


	# Check for exponent.

	j = ip
	expon = 0
	if (stridx (str[ip], "eEdD") > 0) {		# exponent?
	    ip = ip + 1
	    if (gctol (str, ip, expon, DECIMAL) <= 0) {
		ip = j					# return chars
		expon = 0
	    }
	}

	if (abs(vexp+expon) > MAX_EXPONENTD)		# check for overflow
	    return (ip - ip_start)

	dval = dval + value * scalar
	if (expon != 0)
	    dval = dval * (10.0D0 ** expon)		# apply exponent

	if (neg)
	    dval = -dval
	return (ip - ip_start)
end