@@ -47,7 +47,9 @@
log('Non-numeric input');
+ } elseif ($_POST['A'] == 0) {
$helper->log('The equation is not quadratic');
} else {
// Calculate and Display the results
diff --git a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
index e56fabe913..b53829b767 100644
--- a/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
+++ b/src/PhpSpreadsheet/Calculation/Financial/Amortization.php
@@ -9,8 +9,6 @@
class Amortization
{
- private const ROUNDING_ADJUSTMENT = (PHP_VERSION_ID < 80400) ? 0 : 1e-14;
-
/**
* AMORDEGRC.
*
@@ -82,7 +80,7 @@ public static function AMORDEGRC(
$amortiseCoeff = self::getAmortizationCoefficient($rate);
$rate *= $amortiseCoeff;
- $rate += self::ROUNDING_ADJUSTMENT;
+ $rate = (float) (string) $rate; // ugly way to avoid rounding problem
$fNRate = round($yearFrac * $rate * $cost, 0);
$cost -= $fNRate;
$fRest = $cost - $salvage;
diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Round.php b/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
index a573f2afdf..d2aa1c0b13 100644
--- a/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
+++ b/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
@@ -10,8 +10,6 @@ class Round
{
use ArrayEnabled;
- private const ROUNDING_ADJUSTMENT = (PHP_VERSION_ID < 80400) ? 0 : 1e-14;
-
/**
* ROUND.
*
@@ -69,11 +67,22 @@ public static function up($number, $digits): array|string|float
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);
+ }
+
+ 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($number + 0.5 * 0.1 ** $digits - self::ROUNDING_ADJUSTMENT, $digits, PHP_ROUND_HALF_DOWN);
+ return round((float) $result, $digits, PHP_ROUND_HALF_DOWN);
}
/**
@@ -105,11 +114,23 @@ public static function down($number, $digits): array|string|float
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);
+ }
+
+ if ($digitsPlus1 < 0) {
+ return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
}
- return round($number - 0.5 * 0.1 ** $digits + self::ROUNDING_ADJUSTMENT, $digits, PHP_ROUND_HALF_UP);
+ $result = sprintf("%.{$digitsPlus1}F", $number - 0.5 * 0.1 ** $digits);
+
+ return round((float) $result, $digits, PHP_ROUND_HALF_UP);
}
/**
diff --git a/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php b/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
index 44aedd2ca0..f36f14d704 100644
--- a/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
+++ b/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
@@ -13,6 +13,11 @@ class Trunc
* TRUNC.
*
* Truncates value to the number of fractional digits by number_digits.
+ * This will probably not be the precise result in the unlikely
+ * event that the number of digits to the left of the decimal
+ * plus the number of digits to the right exceeds PHP_FLOAT_DIG
+ * (or possibly that value minus 1).
+ * Excel is unlikely to do any better.
*
* @param array|float $value Or can be an array of values
* @param array|int $digits Or can be an array of values
@@ -34,15 +39,27 @@ public static function evaluate(array|float|string|null $value = 0, array|int|st
return $e->getMessage();
}
- $digits = floor($digits);
+ if ($value == 0) {
+ return $value;
+ }
- // Truncate
- $adjust = 10 ** $digits;
+ if ($value >= 0) {
+ $minusSign = '';
+ } else {
+ $minusSign = '-';
+ $value = -$value;
+ }
+ $digits = (int) floor($digits);
+ if ($digits < 0) {
+ $result = (float) (substr(sprintf('%.0F', $value), 0, $digits) . str_repeat('0', -$digits));
- if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
- return $value;
+ return ($minusSign === '') ? $result : -$result;
}
+ $decimals = (floor($value) == (int) $value) ? (PHP_FLOAT_DIG - strlen((string) (int) $value)) : $digits;
+ $resultString = ($decimals < 0) ? sprintf('%F', $value) : sprintf('%.' . $decimals . 'F', $value);
+ $regExp = '/([.]\\d{' . $digits . '})\\d+$/';
+ $result = $minusSign . (preg_replace($regExp, '$1', $resultString) ?? $resultString);
- return ((int) ($value * $adjust)) / $adjust;
+ return (float) $result;
}
}
diff --git a/src/PhpSpreadsheet/Helper/Sample.php b/src/PhpSpreadsheet/Helper/Sample.php
index c772aface1..e0d2cfee24 100644
--- a/src/PhpSpreadsheet/Helper/Sample.php
+++ b/src/PhpSpreadsheet/Helper/Sample.php
@@ -9,6 +9,7 @@
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
+use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
@@ -129,7 +130,11 @@ public function write(Spreadsheet $spreadsheet, string $filename, array $writers
$writerCallback($writer);
}
$callStartTime = microtime(true);
- $writer->save($path);
+ if (PHP_VERSION_ID >= 80400 && $writer instanceof Dompdf) {
+ @$writer->save($path);
+ } else {
+ $writer->save($path);
+ }
$this->logWrite($writer, $path, $callStartTime);
if ($this->isCli() === false) {
// @codeCoverageIgnoreStart
diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/TruncTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/TruncTest.php
index beaf0a8313..e82d7ccd9b 100644
--- a/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/TruncTest.php
+++ b/tests/PhpSpreadsheetTests/Calculation/Functions/MathTrig/TruncTest.php
@@ -47,4 +47,36 @@ public static function providerTruncArray(): array
'matrix' => [[[3.14, 3.141], [3.14159, 3.14159265]], '3.1415926536', '{2, 3; 5, 8}'],
];
}
+
+ /**
+ * @dataProvider providerTooMuchPrecision
+ */
+ public function testTooMuchPrecision(mixed $expectedResult, float|int|string $value, int $digits = 1): void
+ {
+ // This test is pretty screwy. Possibly shouldn't even attempt it.
+ // At any rate, these results seem to indicate that PHP
+ // maximum precision is PHP_FLOAT_DIG - 1 digits, not PHP_FLOAT_DIG.
+ // If that changes, at least one of these tests will have to change.
+ $sheet = $this->getSheet();
+ $sheet->getCell('E1')->setValue($value);
+ $sheet->getCell('E2')->setValue("=TRUNC(E1,$digits)");
+ $result = $sheet->getCell('E2')->getCalculatedValue();
+ self::assertSame($expectedResult, (string) $result);
+ }
+
+ public static function providerTooMuchPrecision(): array
+ {
+ $max64Plus1 = 9223372036854775808;
+ $stringMax = (string) $max64Plus1;
+
+ return [
+ '2 digits less than PHP_FLOAT_DIG' => ['1' . str_repeat('0', PHP_FLOAT_DIG - 4) . '1.2', 10.0 ** (PHP_FLOAT_DIG - 3) + 1.2, 1],
+ '1 digit less than PHP_FLOAT_DIG' => ['1' . str_repeat('0', PHP_FLOAT_DIG - 3) . '1', 10.0 ** (PHP_FLOAT_DIG - 2) + 1.2, 1],
+ 'PHP_FLOAT_DIG' => ['1.0E+' . (PHP_FLOAT_DIG - 1), 10.0 ** (PHP_FLOAT_DIG - 1) + 1.2, 1],
+ '1 digit more than PHP_FLOAT_DIG' => ['1.0E+' . PHP_FLOAT_DIG, 10.0 ** PHP_FLOAT_DIG + 1.2, 1],
+ '32bit exceed int max' => ['3123456780', 3123456789, -1],
+ '64bit exceed int max neg decimals' => [$stringMax, $max64Plus1, -1],
+ '64bit exceed int max pos decimals' => [$stringMax, $max64Plus1, 1],
+ ];
+ }
}
diff --git a/tests/PhpSpreadsheetTests/Functional/StreamTest.php b/tests/PhpSpreadsheetTests/Functional/StreamTest.php
index 3430b7a31b..c67c1ab22c 100644
--- a/tests/PhpSpreadsheetTests/Functional/StreamTest.php
+++ b/tests/PhpSpreadsheetTests/Functional/StreamTest.php
@@ -8,6 +8,12 @@
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PHPUnit\Framework\TestCase;
+/**
+ * Not clear that Dompdf will be Php8.4 compatible in time.
+ * Run in separate process and add version test till it is ready.
+ *
+ * @runTestsInSeparateProcesses
+ */
class StreamTest extends TestCase
{
public static function providerFormats(): array
@@ -42,7 +48,11 @@ public function testAllWritersCanWriteToStream(string $format): void
} else {
self::assertSame(0, $stat['size']);
- $writer->save($stream);
+ if ($format === 'Dompdf' && PHP_VERSION_ID >= 80400) {
+ @$writer->save($stream);
+ } else {
+ $writer->save($stream);
+ }
self::assertIsResource($stream, 'should not close the stream for further usage out of PhpSpreadsheet');
$stat = fstat($stream);
diff --git a/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php b/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php
index 4c0b86b637..ccd0e84f05 100644
--- a/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php
+++ b/tests/PhpSpreadsheetTests/Writer/Dompdf/PaperSizeArrayTest.php
@@ -10,6 +10,12 @@
use PhpOffice\PhpSpreadsheet\Writer\Pdf\Dompdf;
use PHPUnit\Framework\TestCase;
+/**
+ * Not clear that Dompdf will be Php8.4 compatible in time.
+ * Run in separate process and add version test till it is ready.
+ *
+ * @runTestsInSeparateProcesses
+ */
class PaperSizeArrayTest extends TestCase
{
private string $outfile = '';
@@ -36,7 +42,11 @@ public function testPaperSizeArray(): void
$sheet->setCellValue('A7', 'Lorem Ipsum');
$writer = new Dompdf($spreadsheet);
$this->outfile = File::temporaryFilename();
- $writer->save($this->outfile);
+ if (PHP_VERSION_ID >= 80400) { // hopefully temporary
+ @$writer->save($this->outfile);
+ } else {
+ $writer->save($this->outfile);
+ }
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
$contents = file_get_contents($this->outfile);
@@ -56,7 +66,11 @@ public function testPaperSizeNotArray(): void
$sheet->setCellValue('A7', 'Lorem Ipsum');
$writer = new Dompdf($spreadsheet);
$this->outfile = File::temporaryFilename();
- $writer->save($this->outfile);
+ if (PHP_VERSION_ID >= 80400) { // hopefully temporary
+ @$writer->save($this->outfile);
+ } else {
+ $writer->save($this->outfile);
+ }
$spreadsheet->disconnectWorksheets();
unset($spreadsheet);
$contents = file_get_contents($this->outfile);
diff --git a/tests/data/Calculation/MathTrig/ROUNDDOWN.php b/tests/data/Calculation/MathTrig/ROUNDDOWN.php
index 3a65c13f24..fed45cd449 100644
--- a/tests/data/Calculation/MathTrig/ROUNDDOWN.php
+++ b/tests/data/Calculation/MathTrig/ROUNDDOWN.php
@@ -33,4 +33,5 @@
[0, 'B1, 0'],
['exception', ''],
['exception', '35.51'],
+ 'negative number and precision' => [-31400, '-31415.92654, -2'],
];
diff --git a/tests/data/Calculation/MathTrig/ROUNDUP.php b/tests/data/Calculation/MathTrig/ROUNDUP.php
index 9014a9e3c0..9683d54d4a 100644
--- a/tests/data/Calculation/MathTrig/ROUNDUP.php
+++ b/tests/data/Calculation/MathTrig/ROUNDUP.php
@@ -33,4 +33,5 @@
[0, 'B1, 0'],
['exception', ''],
['exception', '35.51'],
+ 'negative number and precision' => [-31500, '-31415.92654, -2'],
];
diff --git a/tests/data/Calculation/MathTrig/TRUNC.php b/tests/data/Calculation/MathTrig/TRUNC.php
index 40fa521603..a92d502b26 100644
--- a/tests/data/Calculation/MathTrig/TRUNC.php
+++ b/tests/data/Calculation/MathTrig/TRUNC.php
@@ -11,6 +11,7 @@
[-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,23 @@
[-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'],
+ 'issue4113 example 3' => [-43747, '-43747.99122596, 0'],
+ 'issue4113 example 3 positive' => [43747, '43747.99122596, 0'],
+ 'issue4113 example 4' => [-9.11, '-9.1196419, 2'],
+ 'issue4113 example 5' => [-42300.65, '-42300.65099338, 3'],
+ 'issue4113 example 6 variant 1' => [0.000012, '0.0000123, 6'],
+ 'issue4113 example 6 variant 2' => [0.0000123, '0.0000123, 8'],
+ 'issue4113 example 6 variant 3' => [-0.000012, '-0.0000123, 6'],
+ 'issue4113 example 6 variant 4' => [-0.0000123, '-0.0000123, 8'],
+ 'issue4113 example 6 variant 5' => [0.000012, '1.23E-5, 6'],
+ 'issue4113 example 6 variant 6' => [-0.0000123, '-1.23E-5, 8'],
+ 'exceed 32-bit int max' => [3123456780, '3123456789, -1'],
];
|