mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-09-15 12:36:32 +00:00
New Algorithm for TRUNC, ROUNDUP, and ROUNDDOWN
Fix #4113. TRUNC isn't always producing the expected result. There was a promising algorithm at https://stackoverflow.com/questions/4668628/truncate-float-numbers-with-php from user Juan. It works through Php8.3, but failed in Php8.4 (more on this later). User Savageman on the same page has a solution that needs work, but, once the work had taken place, it works on Php8.1-8.4. The ROUNDUP and ROUNDDOWN functions were adversely affected by Php8.4, probably for the same reasons as Juan's TRUNC suggestion. I put a kludge in place for them some time ago, but I wasn't happy with it. The solution used for TRUNC here suggested a change to the ROUNDUP and ROUNDDOWN code that would no longer require the kludge. The change to those functions now works more cleanly on Php8.1-8.4.
This commit is contained in:
@@ -10,8 +10,6 @@ class Round
|
||||
{
|
||||
use ArrayEnabled;
|
||||
|
||||
private const ROUNDING_ADJUSTMENT = (PHP_VERSION_ID < 80400) ? 0 : 1e-14;
|
||||
|
||||
/**
|
||||
* ROUND.
|
||||
*
|
||||
@@ -69,11 +67,22 @@ class Round
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$digitsPlus1 = $digits + 1;
|
||||
if ($number < 0.0) {
|
||||
return round($number - 0.5 * 0.1 ** $digits + self::ROUNDING_ADJUSTMENT, $digits, PHP_ROUND_HALF_DOWN);
|
||||
if ($digitsPlus1 < 0) {
|
||||
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
|
||||
}
|
||||
$result = sprintf("%.{$digitsPlus1}f", $number - 0.5 * 0.1 ** $digits);
|
||||
|
||||
return round((float) $result, $digits, PHP_ROUND_HALF_DOWN);
|
||||
}
|
||||
|
||||
return round($number + 0.5 * 0.1 ** $digits - self::ROUNDING_ADJUSTMENT, $digits, PHP_ROUND_HALF_DOWN);
|
||||
if ($digitsPlus1 < 0) {
|
||||
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
|
||||
}
|
||||
$result = sprintf("%.{$digitsPlus1}f", $number + 0.5 * 0.1 ** $digits);
|
||||
|
||||
return round((float) $result, $digits, PHP_ROUND_HALF_DOWN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,11 +114,23 @@ class Round
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
$digitsPlus1 = $digits + 1;
|
||||
if ($number < 0.0) {
|
||||
return round($number + 0.5 * 0.1 ** $digits - self::ROUNDING_ADJUSTMENT, $digits, PHP_ROUND_HALF_UP);
|
||||
if ($digitsPlus1 < 0) {
|
||||
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
|
||||
}
|
||||
$result = sprintf("%.{$digitsPlus1}f", $number + 0.5 * 0.1 ** $digits);
|
||||
|
||||
return round((float) $result, $digits, PHP_ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
return round($number - 0.5 * 0.1 ** $digits + self::ROUNDING_ADJUSTMENT, $digits, PHP_ROUND_HALF_UP);
|
||||
if ($digitsPlus1 < 0) {
|
||||
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
$result = sprintf("%.{$digitsPlus1}f", $number - 0.5 * 0.1 ** $digits);
|
||||
|
||||
return round((float) $result, $digits, PHP_ROUND_HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,15 +34,26 @@ class Trunc
|
||||
return $e->getMessage();
|
||||
}
|
||||
|
||||
$digits = floor($digits);
|
||||
|
||||
// Truncate
|
||||
$adjust = 10 ** $digits;
|
||||
|
||||
if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
|
||||
if ($value == 0) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return ((int) ($value * $adjust)) / $adjust;
|
||||
if ($value >= 0) {
|
||||
$minusSign = '';
|
||||
} else {
|
||||
$minusSign = '-';
|
||||
$value = -$value;
|
||||
}
|
||||
$digits = (int) floor($digits);
|
||||
if ($digits < 0) {
|
||||
$power = (int) (10 ** -$digits);
|
||||
$result = intdiv((int) floor($value), $power) * $power;
|
||||
|
||||
return ($minusSign === '') ? $result : -$result;
|
||||
}
|
||||
$digitsPlus1 = $digits + 1;
|
||||
$result = substr($minusSign . sprintf("%.{$digitsPlus1}f", $value), 0, -1);
|
||||
|
||||
return (float) $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,4 +33,5 @@ return [
|
||||
[0, 'B1, 0'],
|
||||
['exception', ''],
|
||||
['exception', '35.51'],
|
||||
'negative number and precision' => [-31400, '-31415.92654, -2'],
|
||||
];
|
||||
|
||||
@@ -33,4 +33,5 @@ return [
|
||||
[0, 'B1, 0'],
|
||||
['exception', ''],
|
||||
['exception', '35.51'],
|
||||
'negative number and precision' => [-31500, '-31415.92654, -2'],
|
||||
];
|
||||
|
||||
@@ -11,6 +11,7 @@ return [
|
||||
[-31415.92654, '-31415.92654, 10'],
|
||||
[31415.92, '31415.92654, 2'],
|
||||
[31400, '31415.92654, -2'],
|
||||
'negative number and precision' => [-31400, '-31415.92654, -2'],
|
||||
[0, '31415.92654, -10'],
|
||||
[0, '-31415.92654, -10'],
|
||||
[12000, '12345.6789, -3'],
|
||||
@@ -32,4 +33,12 @@ return [
|
||||
[-3, 'A4'],
|
||||
[-5, 'A5'],
|
||||
[0, 'B1'],
|
||||
'issue4113' => [1.0, '1.01, 1'],
|
||||
'issue4113 negative' => [-1.0, '-1.01, 1'],
|
||||
'issue4113 additional' => [10.04, '10.04, 2'],
|
||||
'issue4113 additional negative' => [-10.04, '-10.04, 2'],
|
||||
'issue4113 small fraction keep all' => [0.04, '0.04, 2'],
|
||||
'issue4113 small negative fraction keep all' => [-0.04, '-0.04, 2'],
|
||||
'issue4113 small fraction lose some' => [0.0, '0.01, 1'],
|
||||
'issue4113 small negative fraction lose some' => [0.0, '-0.001, 1'],
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user