Skip to content

Commit d2263d4

Browse files
committed
Implement FR #53457 (number_format must support more than one character for
thousands separator).
1 parent 18ec6da commit d2263d4

File tree

5 files changed

+105
-18
lines changed

5 files changed

+105
-18
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ PHP NEWS
112112
getallheaders(), apache_request_headers() and apache_response_headers()
113113
. Improved performance of FastCGI request parsing.
114114

115+
- Improved core functions:
116+
. number_format() no longer truncates multibyte decimal points and thousand
117+
separators to the first byte. FR #53457. (Adam)
118+
115119
- Improved CURL extension:
116120
. Added support for CURLOPT_MAX_RECV_SPEED_LARGE and
117121
CURLOPT_MAX_SEND_SPEED_LARGE. FR #51815. (Pierrick)

UPGRADING

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,8 @@ UPGRADE NOTES - PHP X.Y
133133
behavior follows the recommendations of Unicode Technical Report #36.
134134
- htmlspecialchars_decode/html_entity_decode now decode ' if the document
135135
type is ENT_XML1, ENT_XHTML, or ENT_HTML5.
136+
- number_format() no longer truncates multibyte decimal points and thousand
137+
separators to the first byte.
136138
- The third parameter ($matches) to preg_match_all() is now optional. If
137139
omitted, the function will simply return the number of times the pattern was
138140
matched in the subject and will have no other side effects.

ext/standard/math.c

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,6 +1081,11 @@ PHP_FUNCTION(base_convert)
10811081
/* {{{ _php_math_number_format
10821082
*/
10831083
PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char thousand_sep)
1084+
{
1085+
return _php_math_number_format_ex(d, dec, &dec_point, 1, &thousand_sep, 1);
1086+
}
1087+
1088+
PHPAPI char *_php_math_number_format_ex(double d, int dec, char *dec_point, size_t dec_point_len, char *thousand_sep, size_t thousand_sep_len)
10841089
{
10851090
char *tmpbuf = NULL, *resbuf;
10861091
char *s, *t; /* source, target */
@@ -1121,7 +1126,7 @@ PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char tho
11211126

11221127
/* allow for thousand separators */
11231128
if (thousand_sep) {
1124-
integral += (integral-1) / 3;
1129+
integral += thousand_sep_len * ((integral-1) / 3);
11251130
}
11261131

11271132
reslen = integral;
@@ -1130,7 +1135,7 @@ PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char tho
11301135
reslen += dec;
11311136

11321137
if (dec_point) {
1133-
reslen++;
1138+
reslen += dec_point_len;
11341139
}
11351140
}
11361141

@@ -1166,7 +1171,8 @@ PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char tho
11661171

11671172
/* add decimal point */
11681173
if (dec_point) {
1169-
*t-- = dec_point;
1174+
t -= dec_point_len;
1175+
memcpy(t + 1, dec_point, dec_point_len);
11701176
}
11711177
}
11721178

@@ -1175,7 +1181,8 @@ PHPAPI char *_php_math_number_format(double d, int dec, char dec_point, char tho
11751181
while(s >= tmpbuf) {
11761182
*t-- = *s--;
11771183
if (thousand_sep && (++count%3)==0 && s>=tmpbuf) {
1178-
*t-- = thousand_sep;
1184+
t -= thousand_sep_len;
1185+
memcpy(t + 1, thousand_sep, thousand_sep_len);
11791186
}
11801187
}
11811188

@@ -1212,21 +1219,17 @@ PHP_FUNCTION(number_format)
12121219
RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
12131220
break;
12141221
case 4:
1215-
if (dec_point != NULL) {
1216-
if (dec_point_len) {
1217-
dec_point_chr = dec_point[0];
1218-
} else {
1219-
dec_point_chr = 0;
1220-
}
1222+
if (dec_point == NULL) {
1223+
dec_point = &dec_point_chr;
1224+
dec_point_len = 1;
12211225
}
1222-
if (thousand_sep != NULL) {
1223-
if (thousand_sep_len) {
1224-
thousand_sep_chr = thousand_sep[0];
1225-
} else {
1226-
thousand_sep_chr = 0;
1227-
}
1226+
1227+
if (thousand_sep == NULL) {
1228+
thousand_sep = &thousand_sep_chr;
1229+
thousand_sep_len = 1;
12281230
}
1229-
RETURN_STRING(_php_math_number_format(num, dec, dec_point_chr, thousand_sep_chr), 0);
1231+
1232+
RETURN_STRING(_php_math_number_format_ex(num, dec, dec_point, dec_point_len, thousand_sep, thousand_sep_len), 0);
12301233
break;
12311234
default:
12321235
WRONG_PARAM_COUNT;

ext/standard/php_math.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
2222
#ifndef PHP_MATH_H
2323
#define PHP_MATH_H
2424

25-
PHPAPI char *_php_math_number_format(double, int, char , char);
25+
PHPAPI char *_php_math_number_format(double, int, char, char);
26+
PHPAPI char *_php_math_number_format_ex(double, int, char *, size_t, char *, size_t);
2627
PHPAPI char * _php_math_longtobase(zval *arg, int base);
2728
PHPAPI long _php_math_basetolong(zval *arg, int base);
2829
PHPAPI int _php_math_basetozval(zval *arg, int base, zval *ret);
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
--TEST--
2+
Test number_format() - multiple character separator support
3+
--FILE--
4+
<?php
5+
$values = array(1234.5678,
6+
-1234.5678,
7+
1234.6578e4,
8+
-1234.56789e4,
9+
0x1234CDEF,
10+
02777777777,
11+
"123456789",
12+
"123.456789",
13+
"12.3456789e1",
14+
null,
15+
true,
16+
false);
17+
18+
echo " number_format tests.....multiple character decimal point\n";
19+
for ($i = 0; $i < count($values); $i++) {
20+
$res = number_format($values[$i], 2, '&#183;', ' ');
21+
var_dump($res);
22+
}
23+
24+
echo "\n number_format tests.....multiple character thousand separator\n";
25+
for ($i = 0; $i < count($values); $i++) {
26+
$res = number_format($values[$i], 2, '.' , '&thinsp;');
27+
var_dump($res);
28+
}
29+
30+
echo "\n number_format tests.....multiple character decimal and thousep\n";
31+
for ($i = 0; $i < count($values); $i++) {
32+
$res = number_format($values[$i], 2, '&#183;' , '&thinsp;');
33+
var_dump($res);
34+
}
35+
?>
36+
--EXPECTF--
37+
number_format tests.....multiple character decimal point
38+
string(13) "1 234&#183;57"
39+
string(14) "-1 234&#183;57"
40+
string(18) "12 346 578&#183;00"
41+
string(19) "-12 345 678&#183;90"
42+
string(19) "305 450 479&#183;00"
43+
string(19) "402 653 183&#183;00"
44+
string(19) "123 456 789&#183;00"
45+
string(11) "123&#183;46"
46+
string(11) "123&#183;46"
47+
string(9) "0&#183;00"
48+
string(9) "1&#183;00"
49+
string(9) "0&#183;00"
50+
51+
number_format tests.....multiple character thousand separator
52+
string(15) "1&thinsp;234.57"
53+
string(16) "-1&thinsp;234.57"
54+
string(27) "12&thinsp;346&thinsp;578.00"
55+
string(28) "-12&thinsp;345&thinsp;678.90"
56+
string(28) "305&thinsp;450&thinsp;479.00"
57+
string(28) "402&thinsp;653&thinsp;183.00"
58+
string(28) "123&thinsp;456&thinsp;789.00"
59+
string(6) "123.46"
60+
string(6) "123.46"
61+
string(4) "0.00"
62+
string(4) "1.00"
63+
string(4) "0.00"
64+
65+
number_format tests.....multiple character decimal and thousep
66+
string(20) "1&thinsp;234&#183;57"
67+
string(21) "-1&thinsp;234&#183;57"
68+
string(32) "12&thinsp;346&thinsp;578&#183;00"
69+
string(33) "-12&thinsp;345&thinsp;678&#183;90"
70+
string(33) "305&thinsp;450&thinsp;479&#183;00"
71+
string(33) "402&thinsp;653&thinsp;183&#183;00"
72+
string(33) "123&thinsp;456&thinsp;789&#183;00"
73+
string(11) "123&#183;46"
74+
string(11) "123&#183;46"
75+
string(9) "0&#183;00"
76+
string(9) "1&#183;00"
77+
string(9) "0&#183;00"

0 commit comments

Comments
 (0)