@@ -1131,16 +1131,159 @@ PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *de
1131
1131
return res ;
1132
1132
}
1133
1133
1134
+ PHPAPI zend_string * _php_math_number_format_long (zend_long num , int dec , const char * dec_point ,
1135
+ size_t dec_point_len , const char * thousand_sep , size_t thousand_sep_len )
1136
+ {
1137
+ int neg = num < 0 ;
1138
+ zend_ulong num_abs = neg ? ((zend_ulong )- (num + 1 )) + 1 : (zend_ulong )num ;
1139
+ zend_string * res ;
1140
+ zend_string * res2 ;
1141
+ zend_string * tmpbuf ;
1142
+ char * s , * t , * t2 ; /* source, target */
1143
+ size_t integral ;
1144
+ size_t reslen = 0 ;
1145
+ int count = 0 ;
1146
+ size_t topad ;
1147
+ char cur_char ;
1148
+ int roundup = 0 ;
1149
+
1150
+ tmpbuf = strpprintf (0 , "%lu" , num_abs );
1151
+
1152
+ /* rounding more places than length of num
1153
+ * or same places of length of num with rounding down
1154
+ * will result in zero */
1155
+ if (dec < 0 ) {
1156
+ if (- dec > ZSTR_LEN (tmpbuf ) || (- dec == ZSTR_LEN (tmpbuf ) && ZSTR_VAL (tmpbuf )[0 ] < '5' )) {
1157
+ reslen = 1 ;
1158
+ res = zend_string_alloc (reslen , 0 );
1159
+ t = ZSTR_VAL (res );
1160
+ * t = '0' ;
1161
+
1162
+ ZSTR_LEN (res ) = reslen ;
1163
+ zend_string_release_ex (tmpbuf , 0 );
1164
+ return res ;
1165
+ }
1166
+ }
1167
+
1168
+ integral = ZSTR_LEN (tmpbuf );
1169
+
1170
+ /* allow for thousand separators */
1171
+ if (thousand_sep ) {
1172
+ integral = zend_safe_addmult ((integral - 1 )/3 , thousand_sep_len , integral , "number formatting" );
1173
+ }
1174
+
1175
+ reslen = integral + neg ;
1176
+
1177
+ if (dec > 0 ) {
1178
+ reslen += dec ;
1179
+
1180
+ if (dec_point ) {
1181
+ reslen = zend_safe_addmult (reslen , 1 , dec_point_len , "number formatting" );
1182
+ }
1183
+ }
1184
+
1185
+ res = zend_string_alloc (reslen , 0 );
1186
+
1187
+ s = ZSTR_VAL (tmpbuf ) + ZSTR_LEN (tmpbuf ) - 1 ;
1188
+ t = ZSTR_VAL (res ) + reslen ;
1189
+ * t -- = '\0' ;
1190
+
1191
+ /* copy the decimal places. */
1192
+ if (dec > 0 ) {
1193
+ topad = (size_t )dec ;
1194
+
1195
+ /* pad with '0's */
1196
+ while (topad -- ) {
1197
+ * t -- = '0' ;
1198
+ }
1199
+
1200
+ /* add decimal point */
1201
+ if (dec_point ) {
1202
+ t -= dec_point_len ;
1203
+ memcpy (t + 1 , dec_point , dec_point_len );
1204
+ }
1205
+ }
1206
+
1207
+ /* copy the numbers before the decimal point,
1208
+ * adding thousand separator every three digits,
1209
+ * round negative decimal places if needed */
1210
+ topad = 0 ;
1211
+ cur_char = * s ;
1212
+ while (s >= ZSTR_VAL (tmpbuf )) {
1213
+ cur_char = * s -- ;
1214
+ if (roundup && cur_char != '-' ) {
1215
+ if (cur_char < '9' ) {
1216
+ cur_char ++ ;
1217
+ roundup = 0 ;
1218
+ } else {
1219
+ cur_char = '0' ;
1220
+ }
1221
+ }
1222
+
1223
+ if (dec < 0 && - dec > topad ) {
1224
+ if (- dec == topad + 1 && cur_char >= '5' ) {
1225
+ roundup = 1 ;
1226
+ }
1227
+
1228
+ topad ++ ;
1229
+ * t -- = '0' ;
1230
+ } else {
1231
+ * t -- = cur_char ;
1232
+ }
1233
+
1234
+ if (thousand_sep && (++ count %3 )== 0 && s >= ZSTR_VAL (tmpbuf )) {
1235
+ t -= thousand_sep_len ;
1236
+ memcpy (t + 1 , thousand_sep , thousand_sep_len );
1237
+ }
1238
+ }
1239
+
1240
+ if (neg ) {
1241
+ * t = '-' ;
1242
+ }
1243
+
1244
+ /* Allocate more bytes in case we have to round up to more places than initially thought
1245
+ * E.g. 999 with negative decimals between -1 and -3 needs to end up as 1.000 */
1246
+ if (roundup ) {
1247
+ reslen += 1 + (thousand_sep && (count %3 )== 0 ? thousand_sep_len : 0 );
1248
+ res2 = zend_string_alloc (reslen , 0 );
1249
+
1250
+ t2 = ZSTR_VAL (res2 );
1251
+ if (neg ) {
1252
+ * t2 ++ = '-' ;
1253
+ * t2 ++ = '1' ;
1254
+ if (thousand_sep && (count %3 ) == 0 ) {
1255
+ memcpy (t2 , thousand_sep , thousand_sep_len );
1256
+ t2 += thousand_sep_len ;
1257
+ }
1258
+ memcpy (t2 , ZSTR_VAL (res )+ 1 , reslen - 2 );
1259
+ } else {
1260
+ * t2 ++ = '1' ;
1261
+ if (thousand_sep && (count %3 ) == 0 ) {
1262
+ memcpy (t2 , thousand_sep , thousand_sep_len );
1263
+ t2 += thousand_sep_len ;
1264
+ }
1265
+ memcpy (t2 , ZSTR_VAL (res ), reslen - 1 );
1266
+ }
1267
+
1268
+ zend_string_release_ex (res , 0 );
1269
+ res = res2 ;
1270
+ }
1271
+
1272
+ ZSTR_LEN (res ) = reslen ;
1273
+ zend_string_release_ex (tmpbuf , 0 );
1274
+ return res ;
1275
+ }
1276
+
1134
1277
/* {{{ Formats a number with grouped thousands */
1135
1278
PHP_FUNCTION (number_format )
1136
1279
{
1137
- double num ;
1280
+ zval * num ;
1138
1281
zend_long dec = 0 ;
1139
1282
char * thousand_sep = NULL , * dec_point = NULL ;
1140
1283
size_t thousand_sep_len = 0 , dec_point_len = 0 ;
1141
1284
1142
1285
ZEND_PARSE_PARAMETERS_START (1 , 4 )
1143
- Z_PARAM_DOUBLE (num )
1286
+ Z_PARAM_NUMBER (num )
1144
1287
Z_PARAM_OPTIONAL
1145
1288
Z_PARAM_LONG (dec )
1146
1289
Z_PARAM_STRING_OR_NULL (dec_point , dec_point_len )
@@ -1156,7 +1299,17 @@ PHP_FUNCTION(number_format)
1156
1299
thousand_sep_len = 1 ;
1157
1300
}
1158
1301
1159
- RETURN_STR (_php_math_number_format_ex (num , (int )dec , dec_point , dec_point_len , thousand_sep , thousand_sep_len ));
1302
+ switch (Z_TYPE_P (num )) {
1303
+ case IS_LONG :
1304
+ RETURN_STR (_php_math_number_format_long (Z_LVAL_P (num ), (int )dec , dec_point , dec_point_len , thousand_sep , thousand_sep_len ));
1305
+ break ;
1306
+
1307
+ case IS_DOUBLE :
1308
+ RETURN_STR (_php_math_number_format_ex (Z_DVAL_P (num ), (int )dec , dec_point , dec_point_len , thousand_sep , thousand_sep_len ));
1309
+ break ;
1310
+
1311
+ EMPTY_SWITCH_DEFAULT_CASE ()
1312
+ }
1160
1313
}
1161
1314
/* }}} */
1162
1315
0 commit comments