@@ -1131,16 +1131,159 @@ PHPAPI zend_string *_php_math_number_format_ex(double d, int dec, const char *de
11311131 return res ;
11321132}
11331133
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+
11341277/* {{{ Formats a number with grouped thousands */
11351278PHP_FUNCTION (number_format )
11361279{
1137- double num ;
1280+ zval * num ;
11381281 zend_long dec = 0 ;
11391282 char * thousand_sep = NULL , * dec_point = NULL ;
11401283 size_t thousand_sep_len = 0 , dec_point_len = 0 ;
11411284
11421285 ZEND_PARSE_PARAMETERS_START (1 , 4 )
1143- Z_PARAM_DOUBLE (num )
1286+ Z_PARAM_NUMBER (num )
11441287 Z_PARAM_OPTIONAL
11451288 Z_PARAM_LONG (dec )
11461289 Z_PARAM_STRING_OR_NULL (dec_point , dec_point_len )
@@ -1156,7 +1299,17 @@ PHP_FUNCTION(number_format)
11561299 thousand_sep_len = 1 ;
11571300 }
11581301
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+ }
11601313}
11611314/* }}} */
11621315
0 commit comments