Skip to content

Fix #76093: Format strings w/o loss of precision w/ FORMAT_TYPE_DECIMAL #7782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

cscott
Copy link
Contributor

@cscott cscott commented Dec 15, 2021

Passing the argument to NumberFormat::format() as a number loses
precision if the value can not be represented precisely as a
double or long integer. The icu library provides a "decimal number"
type that avoids the loss of prevision when the value is passed
as a string. Add a new FORMAT_TYPE_DECIMAL to explicitly request
the argument be converted to a string and then passed to icu
that way.

@cscott
Copy link
Contributor Author

cscott commented Dec 15, 2021

This is discussed in https://bugs.php.net/bug.php?id=76093 and is also related to a MediaWiki bug described at https://phabricator.wikimedia.org/T268456 .

int64_t value = (Z_TYPE_P(number) == IS_DOUBLE)?(int64_t)Z_DVAL_P(number):Z_LVAL_P(number);
int64_t value;
if (Z_TYPE_P(number) == IS_DOUBLE) {
value = (int64_t)Z_DVAL_P(number);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAIK, this conversion is undefined behavior if the double value is outside the range of int64_t. However, we already do that prior to this patch, so it may be okay for all supported platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could be justified in broadening the expected test result if platforms really do crazy things with this conversion. But IMO if you've asked for TYPE_INT64 you are explicitly asking for the result of casting the double to the int64, however that's defined on your platform. And as you note, this isn't new behavior in this patch, this is exactly how it worked previously.

Copy link
Contributor Author

@cscott cscott Dec 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As it turns out, your comment was prescient: the s390 architecture got +9223372036854775808 instead of -9223372036854775808 as a result of that cast (https://app.travis-ci.com/github/php/php-src/jobs/552554610). So I've tweaked the test to allow any value for this case.

@cscott cscott force-pushed the bug76093 branch 3 times, most recently from 34d275c to d1afe1f Compare December 20, 2021 16:08
@cscott
Copy link
Contributor Author

cscott commented Dec 22, 2021

The failing tests seem to be failures of the CI infrastructure, not due to this patch.

@cmb69
Copy link
Member

cmb69 commented Dec 22, 2021

The failing tests seem to be failures of the CI infrastructure, not due to this patch.

Did you rebase onto current php:master?

Passing the argument to NumberFormat::format() as a number loses
precision if the value can not be represented precisely as a
double or long integer.  The icu library provides a "decimal number"
type that avoids the loss of prevision when the value is passed
as a string.  Add a new FORMAT_TYPE_DECIMAL to explicitly request
the argument be converted to a string and then passed to icu
that way.
This extends the argument types of NumberFormat::formatCurrency() to
include string, in the same way the earlier patch extended
NumberFormat::format() to allow a string.  This allows formatting
values which can't be represented precisely as a double or long
integer, using the "decimal number" type of the icu library.
@cscott
Copy link
Contributor Author

cscott commented Dec 22, 2021

The failing tests seem to be failures of the CI infrastructure, not due to this patch.
Did you rebase onto current php:master?

Ok, just did so.

@cmb69
Copy link
Member

cmb69 commented Dec 22, 2021

CI looks better now. :) (usually, only Travis is sometimes flaky)

@ramsey
Copy link
Member

ramsey commented Jun 1, 2022

This change looks good to me. Should this target PHP-8.0, since it's fixing a bug?

Comment on lines -38 to +52
if( zend_parse_method_parameters( ZEND_NUM_ARGS(), getThis(), "On|l",
&object, NumberFormatter_ce_ptr, &number, &type ) == FAILURE )
{
RETURN_THROWS();
if (object) {
ZEND_PARSE_PARAMETERS_START(1, 2)
Z_PARAM_STR_OR_NUMBER(number)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(type)
ZEND_PARSE_PARAMETERS_END();
} else {
ZEND_PARSE_PARAMETERS_START(2, 3)
Z_PARAM_OBJECT_OF_CLASS(object, NumberFormatter_ce_ptr)
Z_PARAM_STR_OR_NUMBER(number)
Z_PARAM_OPTIONAL
Z_PARAM_LONG(type)
ZEND_PARSE_PARAMETERS_END();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case it might make sense to stick with zend_parse_method_parameters(), but replace the n specifier with z, and to replace the EMPTY_SWITCH_DEFAULT_CASE() some lines below to throw a type error manually.

@cmb69
Copy link
Member

cmb69 commented Jun 1, 2022

Should this target PHP-8.0, since it's fixing a bug?

This is more like implementing a feature request, so targeting master seems appropriate.

Comment on lines +76 to +80
if(sizeof(zend_long) < 8) {
if (Z_TYPE_P(number) == IS_STRING && type == FORMAT_TYPE_INT64) {
type = FORMAT_TYPE_DECIMAL;
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should better be a compile directive:

Suggested change
if(sizeof(zend_long) < 8) {
if (Z_TYPE_P(number) == IS_STRING && type == FORMAT_TYPE_INT64) {
type = FORMAT_TYPE_DECIMAL;
}
}
#if SIZEOF_ZEND_LONG < 8
if (Z_TYPE_P(number) == IS_STRING && type == FORMAT_TYPE_INT64) {
type = FORMAT_TYPE_DECIMAL;
}
#endif

@cmb69
Copy link
Member

cmb69 commented Aug 31, 2022

What is the status here? In principle, this PR is fine, but some details should be reconsidered/improved as written above. Also it is not clear if this still can target PHP-8.2; if so, the PR should be rebased onto PHP-8.2 (instead of "master").

@github-actions
Copy link

There has not been any recent activity in this PR. It will automatically be closed in 7 days if no further action is taken.

@lucaswerkmeister
Copy link

Resubmitted as #9909.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants