xref: /php-src/ext/bcmath/libbcmath/src/str2num.c (revision e56ed6e1)
1 /* str2num.c: bcmath library file. */
2 /*
3     Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
4     Copyright (C) 2000 Philip A. Nelson
5 
6     This library is free software; you can redistribute it and/or
7     modify it under the terms of the GNU Lesser General Public
8     License as published by the Free Software Foundation; either
9     version 2 of the License, or (at your option) any later version.
10 
11     This library is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14     Lesser General Public License for more details.  (LICENSE)
15 
16     You should have received a copy of the GNU Lesser General Public
17     License along with this library; if not, write to:
18 
19       The Free Software Foundation, Inc.
20       59 Temple Place, Suite 330
21       Boston, MA 02111-1307 USA.
22 
23     You may contact the author by:
24        e-mail:  philnelson@acm.org
25       us-mail:  Philip A. Nelson
26                 Computer Science Department, 9062
27                 Western Washington University
28                 Bellingham, WA 98226-9062
29 
30 *************************************************************************/
31 
32 #include "bcmath.h"
33 #include <stdbool.h>
34 #include <stddef.h>
35 
36 /* Convert strings to bc numbers.  Base 10 only.*/
37 
bc_str2num(bc_num * num,char * str,size_t scale)38 bool bc_str2num(bc_num *num, char *str, size_t scale)
39 {
40 	size_t digits = 0;
41 	size_t strscale = 0;
42 	char *ptr, *nptr;
43 	size_t trailing_zeros = 0;
44 	bool zero_int = false;
45 
46 	/* Prepare num. */
47 	bc_free_num (num);
48 
49 	/* Check for valid number and count digits. */
50 	ptr = str;
51 
52 	if ((*ptr == '+') || (*ptr == '-')) {
53 		/* Skip Sign */
54 		ptr++;
55 	}
56 	/* Skip leading zeros. */
57 	while (*ptr == '0') {
58 		ptr++;
59 	}
60 	/* digits before the decimal point */
61 	while (*ptr >= '0' && *ptr <= '9') {
62 		ptr++;
63 		digits++;
64 	}
65 	/* decimal point */
66 	if (*ptr == '.') {
67 		ptr++;
68 	}
69 	/* digits after the decimal point */
70 	while (*ptr >= '0' && *ptr <= '9') {
71 		if (*ptr == '0') {
72 			trailing_zeros++;
73 		} else {
74 			trailing_zeros = 0;
75 		}
76 		ptr++;
77 		strscale++;
78 	}
79 
80 	if (trailing_zeros > 0) {
81 		/* Trailing zeros should not take part in the computation of the overall scale, as it is pointless. */
82 		strscale = strscale - trailing_zeros;
83 	}
84 	if ((*ptr != '\0') || (digits + strscale == 0)) {
85 		*num = bc_copy_num(BCG(_zero_));
86 		return *ptr == '\0';
87 	}
88 
89 	/* Adjust numbers and allocate storage and initialize fields. */
90 	strscale = MIN(strscale, scale);
91 	if (digits == 0) {
92 		zero_int = true;
93 		digits = 1;
94 	}
95 	*num = bc_new_num (digits, strscale);
96 
97 	/* Build the whole number. */
98 	ptr = str;
99 	if (*ptr == '-') {
100 		(*num)->n_sign = MINUS;
101 		ptr++;
102 	} else {
103 		(*num)->n_sign = PLUS;
104 		if (*ptr == '+') ptr++;
105 	}
106 	/* Skip leading zeros. */
107 	while (*ptr == '0') {
108 		ptr++;
109 	}
110 	nptr = (*num)->n_value;
111 	if (zero_int) {
112 		*nptr++ = 0;
113 		digits = 0;
114 	}
115 	for (; digits > 0; digits--) {
116 		*nptr++ = CH_VAL(*ptr++);
117 	}
118 
119 	/* Build the fractional part. */
120 	if (strscale > 0) {
121 		/* skip the decimal point! */
122 		ptr++;
123 		for (; strscale > 0; strscale--) {
124 			*nptr++ = CH_VAL(*ptr++);
125 		}
126 	}
127 
128 	if (bc_is_zero(*num)) {
129 		(*num)->n_sign = PLUS;
130 	}
131 
132 	return true;
133 }
134