Fix number formatting with different settings of precision in PHP

- fixed Util::formatNumber to avoid rounding issues to mess up display
- force precision = 14 to ensure we get sane behavior

With precision set to higher value many things do not behave as expected
producing strange numbers in various places.

For example with precision=19 you get:

php > echo round(1.2, 2);
1.199999999999999956

Fixes #12303

Signed-off-by: Michal Čihař <michal@cihar.com>
This commit is contained in:
Michal Čihař 2016-08-30 15:45:53 +02:00
parent f3babbb945
commit f3ca367efa
5 changed files with 60 additions and 16 deletions

View File

@ -19,6 +19,7 @@ phpMyAdmin - ChangeLog
- issue #12251 Automatically save SQL query in browser local storage rather than in cookie
- issue #12292 Unable to edit transformations
- issue #12502 Remove unused paramenter when connecting to MySQLi
- issue #12303 Fix number formatting with different settings of precision in PHP
4.6.4 (2016-08-16)
- issue [security] Weaknesses with cookie encryption, see PMASA-2016-29

View File

@ -1604,17 +1604,13 @@ class Util
$value = round($value / (self::pow(1000, $d, 'pow') / $dh)) /$dh;
$unit = $units[$d];
// If we don't want any zeros after the comma just add the thousand separator
if ($noTrailingZero) {
$localizedValue = self::localizeNumber(
preg_replace('/(?<=\d)(?=(\d{3})+(?!\d))/', ',', $value)
);
} else {
//number_format is not multibyte safe, str_replace is safe
$localizedValue = self::localizeNumber(
number_format($value, $digits_right)
);
// number_format is not multibyte safe, str_replace is safe
$formattedValue = number_format($value, $digits_right);
// If we don't want any zeros, remove them now
if ($noTrailingZero && strpos($formattedValue, '.') !== false) {
$formattedValue = preg_replace('/\.?0+$/', '', $formattedValue);
}
$localizedValue = self::localizeNumber($formattedValue);
if ($originalValue != 0 && floatval($value) == 0) {
return ' <' . self::localizeNumber((1 / self::pow(10, $digits_right)))

View File

@ -107,6 +107,13 @@ PMA_checkExtensions();
ini_set('default_charset', 'utf-8');
mb_internal_encoding('utf-8');
/**
* Set precision to sane value, with higher values
* things behave slightly unexpectedly, for example
* round(1.2, 2) returns 1.199999999999999956.
*/
ini_set('precision', 14);
/**
* the relation lib, tracker needs it
*/

View File

@ -6,6 +6,13 @@
* @package PhpMyAdmin-test
*/
/**
* Set precision to sane value, with higher values
* things behave slightly unexpectedly, for example
* round(1.2, 2) returns 1.199999999999999956.
*/
ini_set('precision', 14);
// Let PHP complain about all errors
error_reporting(E_ALL);

View File

@ -70,6 +70,30 @@ class PMA_FormatNumberByteDown_Test extends PHPUnit_Framework_TestCase
array(0.003, 3, 3, '3 m'),
array(-0.003, 6, 0, '-3,000 &micro;'),
array(100.98, 0, 2, '100.98'),
array(21010101, 0, 2, '21,010,101.00'),
array(20000, 2, 2, '20 k'),
array(20011, 2, 2, '20.01 k'),
);
}
/**
* Core test for formatNumber
*
*
* @param float $a Value to format
* @param int $b Sensitiveness
* @param int $c Number of decimals to retain
* @param array $d Expected value
*
* @return void
*/
private function assertFormatNumber($a, $b, $c, $d)
{
$this->assertEquals(
$d,
(string) PMA\libraries\Util::formatNumber(
$a, $b, $c, false
)
);
}
@ -87,12 +111,21 @@ class PMA_FormatNumberByteDown_Test extends PHPUnit_Framework_TestCase
*/
public function testFormatNumber($a, $b, $c, $d)
{
$this->assertEquals(
$d,
(string) PMA\libraries\Util::formatNumber(
$a, $b, $c, false
)
);
$this->assertFormatNumber($a, $b, $c, $d);
// Test with various precisions
$old_precision = ini_get('precision');
ini_set('precision', 20);
$this->assertFormatNumber($a, $b, $c, $d);
ini_set('precision', 14);
$this->assertFormatNumber($a, $b, $c, $d);
ini_set('precision', 10);
$this->assertFormatNumber($a, $b, $c, $d);
ini_set('precision', 5);
$this->assertFormatNumber($a, $b, $c, $d);
ini_set('precision', -1);
$this->assertFormatNumber($a, $b, $c, $d);
ini_set('precision', $old_precision);
}
/**