3535
3636/* Convert strings to bc numbers. Base 10 only.*/
3737
38- bool bc_str2num (bc_num * num , char * str , size_t scale )
38+ bool bc_str2num (bc_num * num , char * str , size_t scale , bool auto_scale )
3939{
4040 size_t digits = 0 ;
41- size_t strscale = 0 ;
42- char * ptr , * nptr ;
43- size_t trailing_zeros = 0 ;
41+ size_t str_scale = 0 ;
42+ char * ptr = str ;
43+ char * fractional_ptr = NULL ;
44+ char * fractional_end = NULL ;
4445 bool zero_int = false;
4546
4647 /* Prepare num. */
4748 bc_free_num (num );
4849
4950 /* Check for valid number and count digits. */
50- ptr = str ;
51-
5251 if ((* ptr == '+' ) || (* ptr == '-' )) {
5352 /* Skip Sign */
5453 ptr ++ ;
@@ -57,77 +56,102 @@ bool bc_str2num(bc_num *num, char *str, size_t scale)
5756 while (* ptr == '0' ) {
5857 ptr ++ ;
5958 }
59+ const char * integer_ptr = ptr ;
6060 /* digits before the decimal point */
6161 while (* ptr >= '0' && * ptr <= '9' ) {
6262 ptr ++ ;
6363 digits ++ ;
6464 }
6565 /* decimal point */
66- if (* ptr == '.' ) {
67- ptr ++ ;
66+ char * decimal_point = (* ptr == '.' ) ? ptr : NULL ;
67+
68+ /* If a non-digit and non-decimal-point indicator is in the string, i.e. an invalid character */
69+ if (!decimal_point && * ptr != '\0' ) {
70+ goto fail ;
6871 }
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 ;
72+
73+ /* search and validate fractional end if exists */
74+ if (decimal_point ) {
75+ /* search */
76+ fractional_ptr = fractional_end = decimal_point + 1 ;
77+ if (* fractional_ptr == '\0' ) {
78+ goto after_fractional ;
7579 }
76- ptr ++ ;
77- strscale ++ ;
78- }
7980
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 ;
81+ /* validate */
82+ while (* fractional_ptr >= '0' && * fractional_ptr <= '9' ) {
83+ fractional_end = * fractional_ptr != '0' ? fractional_ptr + 1 : fractional_end ;
84+ fractional_ptr ++ ;
85+ }
86+ if (* fractional_ptr != '\0' ) {
87+ /* invalid num */
88+ goto fail ;
89+ }
90+ /* Move the pointer to the beginning of the fraction. */
91+ fractional_ptr = decimal_point + 1 ;
92+
93+ /* Calculate the length of the fraction excluding trailing zero. */
94+ str_scale = fractional_end - fractional_ptr ;
95+
96+ /*
97+ * If set the scale manually and it is smaller than the automatically calculated scale,
98+ * adjust it to match the manual setting.
99+ */
100+ if (str_scale > scale && !auto_scale ) {
101+ fractional_end -= str_scale - scale ;
102+ str_scale = scale ;
103+ }
83104 }
84- if ((* ptr != '\0' ) || (digits + strscale == 0 )) {
85- * num = bc_copy_num (BCG (_zero_ ));
86- return * ptr == '\0' ;
105+
106+ after_fractional :
107+
108+ if (digits + str_scale == 0 ) {
109+ goto zero ;
87110 }
88111
89112 /* Adjust numbers and allocate storage and initialize fields. */
90- strscale = MIN (strscale , scale );
91113 if (digits == 0 ) {
92114 zero_int = true;
93115 digits = 1 ;
94116 }
95- * num = bc_new_num (digits , strscale );
117+ * num = bc_new_num (digits , str_scale );
118+ (* num )-> n_sign = * str == '-' ? MINUS : PLUS ;
119+ char * nptr = (* num )-> n_value ;
96120
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 ;
111121 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 ++ );
122+ nptr ++ ;
123+ /*
124+ * If zero_int is true and the str_scale is 0, there is an early return,
125+ * so here str_scale is always greater than 0.
126+ */
127+ while (fractional_ptr < fractional_end ) {
128+ * nptr = CH_VAL (* fractional_ptr );
129+ nptr ++ ;
130+ fractional_ptr ++ ;
131+ }
132+ } else {
133+ const char * integer_end = integer_ptr + digits ;
134+ while (integer_ptr < integer_end ) {
135+ * nptr = CH_VAL (* integer_ptr );
136+ nptr ++ ;
137+ integer_ptr ++ ;
138+ }
139+ if (str_scale > 0 ) {
140+ while (fractional_ptr < fractional_end ) {
141+ * nptr = CH_VAL (* fractional_ptr );
142+ nptr ++ ;
143+ fractional_ptr ++ ;
144+ }
125145 }
126146 }
127147
128- if (bc_is_zero (* num )) {
129- (* num )-> n_sign = PLUS ;
130- }
148+ return true;
131149
150+ zero :
151+ * num = bc_copy_num (BCG (_zero_ ));
132152 return true;
153+
154+ fail :
155+ * num = bc_copy_num (BCG (_zero_ ));
156+ return false;
133157}
0 commit comments