959 } |
960 } |
960 size_t start = 0; |
961 size_t start = 0; |
961 |
962 |
962 // if base is 2 or 16, some leading stuff may appear |
963 // if base is 2 or 16, some leading stuff may appear |
963 if (base == 2) { |
964 if (base == 2) { |
964 if (str.ptr[0] == 'b' || str.ptr[0] == 'B') { |
965 if ((str.ptr[0] | 32) == 'b') { |
965 start = 1; |
966 start = 1; |
966 } else if (str.ptr[0] == '0' && str.length > 1) { |
967 } else if (str.ptr[0] == '0' && str.length > 1) { |
967 if (str.ptr[1] == 'b' || str.ptr[1] == 'B') { |
968 if ((str.ptr[1] | 32) == 'b') { |
968 start = 2; |
969 start = 2; |
969 } |
970 } |
970 } |
971 } |
971 } else if (base == 16) { |
972 } else if (base == 16) { |
972 if (str.ptr[0] == 'x' || str.ptr[0] == 'X' || str.ptr[0] == '#') { |
973 if ((str.ptr[0] | 32) == 'x' || str.ptr[0] == '#') { |
973 start = 1; |
974 start = 1; |
974 } else if (str.ptr[0] == '0' && str.length > 1) { |
975 } else if (str.ptr[0] == '0' && str.length > 1) { |
975 if (str.ptr[1] == 'x' || str.ptr[1] == 'X') { |
976 if ((str.ptr[1] | 32) == 'x') { |
976 start = 2; |
977 start = 2; |
977 } |
978 } |
978 } |
979 } |
979 } |
980 } |
980 |
981 |
1041 #error "unsupported size_t size" |
1042 #error "unsupported size_t size" |
1042 #endif |
1043 #endif |
1043 } |
1044 } |
1044 |
1045 |
1045 int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) { |
1046 int cx_strtof_lc(cxstring str, float *output, char decsep, const char *groupsep) { |
1046 // TODO: replace temporary implementation |
1047 // use string to double and add a range check |
1047 (void) groupsep; // unused in temp impl |
1048 double d; |
1048 (void) decsep; // unused in temp impl |
1049 int ret = cx_strtod_lc(str, &d, decsep, groupsep); |
1049 char *s = malloc(str.length + 1); |
1050 if (ret != 0) return ret; |
1050 memcpy(s, str.ptr, str.length); |
1051 // note: FLT_MIN is the smallest POSITIVE number that can be represented |
1051 s[str.length] = '\0'; |
1052 double test = d < 0 ? -d : d; |
1052 char *e; |
1053 if (test < FLT_MIN || test > FLT_MAX) { |
1053 *output = strtof(s, &e); |
1054 errno = ERANGE; |
1054 int r = !(e && *e == '\0'); |
1055 return -1; |
1055 free(s); |
1056 } |
1056 return r; |
1057 *output = (float) d; |
|
1058 return 0; |
1057 } |
1059 } |
1058 |
1060 |
1059 int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) { |
1061 int cx_strtod_lc(cxstring str, double *output, char decsep, const char *groupsep) { |
1060 // TODO: replace temporary implementation |
1062 // TODO: overflow check |
1061 (void) groupsep; // unused in temp impl |
1063 // TODO: increase precision |
1062 (void) decsep; // unused in temp impl |
1064 |
1063 char *s = malloc(str.length + 1); |
1065 // trim and check |
1064 memcpy(s, str.ptr, str.length); |
1066 str = cx_strtrim(str); |
1065 s[str.length] = '\0'; |
1067 if (str.length == 0) { |
1066 char *e; |
1068 errno = EINVAL; |
1067 *output = strtod(s, &e); |
1069 return -1; |
1068 int r = !(e && *e == '\0'); |
1070 } |
1069 free(s); |
1071 |
1070 return r; |
1072 double result = 0.; |
1071 } |
1073 int sign = 1; |
|
1074 |
|
1075 // check if there is a sign |
|
1076 if (str.ptr[0] == '-') { |
|
1077 sign = -1; |
|
1078 str.ptr++; |
|
1079 str.length--; |
|
1080 } else if (str.ptr[0] == '+') { |
|
1081 str.ptr++; |
|
1082 str.length--; |
|
1083 } |
|
1084 |
|
1085 // there must be at least one char to parse |
|
1086 if (str.length == 0) { |
|
1087 errno = EINVAL; |
|
1088 return -1; |
|
1089 } |
|
1090 |
|
1091 // parse all digits until we find the decsep |
|
1092 size_t pos = 0; |
|
1093 do { |
|
1094 if (isdigit(str.ptr[pos])) { |
|
1095 result = result * 10 + (str.ptr[pos] - '0'); |
|
1096 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
|
1097 break; |
|
1098 } |
|
1099 } while (++pos < str.length); |
|
1100 |
|
1101 // already done? |
|
1102 if (pos == str.length) { |
|
1103 *output = result * sign; |
|
1104 return 0; |
|
1105 } |
|
1106 |
|
1107 // is the next char the decsep? |
|
1108 if (str.ptr[pos] == decsep) { |
|
1109 pos++; |
|
1110 // it may end with the decsep, if it did not start with it |
|
1111 if (pos == str.length) { |
|
1112 if (str.length == 1) { |
|
1113 errno = EINVAL; |
|
1114 return -1; |
|
1115 } else { |
|
1116 *output = result * sign; |
|
1117 return 0; |
|
1118 } |
|
1119 } |
|
1120 // parse everything until exponent or end |
|
1121 double factor = 1.; |
|
1122 do { |
|
1123 if (isdigit(str.ptr[pos])) { |
|
1124 factor *= 0.1; |
|
1125 result = result + factor * (str.ptr[pos] - '0'); |
|
1126 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
|
1127 break; |
|
1128 } |
|
1129 } while (++pos < str.length); |
|
1130 } |
|
1131 |
|
1132 // no exponent? |
|
1133 if (pos == str.length) { |
|
1134 *output = result * sign; |
|
1135 return 0; |
|
1136 } |
|
1137 |
|
1138 // now the next separator MUST be the exponent separator |
|
1139 // and at least one char must follow |
|
1140 if ((str.ptr[pos] | 32) != 'e' || str.length <= pos + 1) { |
|
1141 errno = EINVAL; |
|
1142 return -1; |
|
1143 } |
|
1144 pos++; |
|
1145 |
|
1146 // check if we have a sign for the exponent |
|
1147 double factor = 10.; |
|
1148 if (str.ptr[pos] == '-') { |
|
1149 factor = .1; |
|
1150 pos++; |
|
1151 } else if (str.ptr[pos] == '+') { |
|
1152 pos++; |
|
1153 } |
|
1154 |
|
1155 // at least one digit must follow |
|
1156 if (pos == str.length) { |
|
1157 errno = EINVAL; |
|
1158 return -1; |
|
1159 } |
|
1160 |
|
1161 // parse the exponent |
|
1162 unsigned int exp = 0; |
|
1163 do { |
|
1164 if (isdigit(str.ptr[pos])) { |
|
1165 exp = 10 * exp + (str.ptr[pos] - '0'); |
|
1166 } else if (strchr(groupsep, str.ptr[pos]) == NULL) { |
|
1167 errno = EINVAL; |
|
1168 return -1; |
|
1169 } |
|
1170 } while (++pos < str.length); |
|
1171 |
|
1172 // apply the exponent by fast exponentiation |
|
1173 do { |
|
1174 if (exp & 1) { |
|
1175 result *= factor; |
|
1176 } |
|
1177 factor *= factor; |
|
1178 } while ((exp >>= 1) > 0); |
|
1179 |
|
1180 // store the result and exit |
|
1181 *output = result * sign; |
|
1182 return 0; |
|
1183 } |