Issues

ZF-9319: Locale does not carry rounding up to units

Description

I found this while analysing a bug in Magento - currency values were not displaying correctly. Values were occasionally being display one unit (one Pound) lower than they should be. The following code fragment demonstrates the problem:

require_once("Zend/Loader.php"); require_once("Zend/Locale/Format.php"); foreach(array(3.99, 3.994, 3.995, 3.999, 4.00) as $value) { $rounded = round($value, 2); $formatted = Zend_Locale_Format::toNumber($value, array('precision' => 2)); echo "Value=$value Rounded=$rounded Formatted=$formatted "; if ((float)$rounded == (float)$formatted) { echo "PASS
"; } else { echo "** FAIL **
"; } }

This displays the following output:

Value=3.99 Rounded=3.99 Formatted=3.99 PASS Value=3.994 Rounded=3.99 Formatted=3.99 PASS Value=3.995 Rounded=4 Formatted=3.00 ** FAIL ** Value=3.999 Rounded=4 Formatted=3.00 ** FAIL ** Value=4 Rounded=4 Formatted=4.00 PASS

Note that when 0.995 is rounded up, the unit is not carried forward beyond the decimal point. Looking at the code, the number part above and below the decimal point is formatted separately, with the integer part done first, and the decimal part done second (losing the unit if it overflows).

Magento uses Zend 8.something (8.5?) but this error is still present in Zend 10.2 (which is where the above results came from).

Comments

This is no failure. From the API doc:

" * The 'precision' option of a value is used to truncate or stretch extra digits. -1 means not to touch the extra digits."

For rounding you need to use Zend_Locale_Math::round()

Could you explain that specification? I'm not sure how "truncate or stretch extra digits" can be interpreted to mean "3.995 should be formatted as 3.00", and even if it did, exactly what use such a transform is. At a stretch I would accept "truncate to a precision of 2" to mean 3.995 is formatted as 3.99. But 3.00 - surely not.

This method is actually called in a long chain from Magento to format monetary values, and goes via the Zend_Currency methods, so whether Zend_Format or Zend_Math is used to format that currency, is pretty much out of the hands of the application.

I would also be interested to know what tests are performed for this boundary condition. If any, is it accepted that 3.994 be formatted as "3.99" and 3.995 be formatted as "3.00"? If no tests are run, then can some be set up so people are very clear that this is the expected functionality?

Implemented using precision without format with r21493