diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ad6d0ddb6..7c930f6d62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a ### Fixed +- Additional floating-point precision changes. [Issue #1324](https://github.com/PHPOffice/PhpSpreadsheet/issues/1324) [PR #4575](https://github.com/PHPOffice/PhpSpreadsheet/pull/4575) - Header/Footer images expand location. [Issue #484](https://github.com/PHPOffice/PhpSpreadsheet/issues/484) [Issue #1318](https://github.com/PHPOffice/PhpSpreadsheet/issues/1318) [PR #4572](https://github.com/PHPOffice/PhpSpreadsheet/pull/4572) - Create uninitialized cell if used in calculation. [Issue #4558](https://github.com/PHPOffice/PhpSpreadsheet/issues/4558) [Issue #4530](https://github.com/PHPOffice/PhpSpreadsheet/issues/4530) [PR #4565](https://github.com/PHPOffice/PhpSpreadsheet/pull/4565) - Shared/Date::isDateTime handle cells which calculate as arrays. [Issue #4557](https://github.com/PHPOffice/PhpSpreadsheet/issues/4557) [PR #4562](https://github.com/PHPOffice/PhpSpreadsheet/pull/4562) diff --git a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php index 74ae4bd41d..86cedc38ae 100644 --- a/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php +++ b/src/PhpSpreadsheet/Style/NumberFormat/Formatter.php @@ -159,7 +159,9 @@ public static function toFormattedString($value, string $format, null|array|call // For 'General' format code, we just pass the value although this is not entirely the way Excel does it, // it seems to round numbers to a total of 10 digits. if (($format === NumberFormat::FORMAT_GENERAL) || ($format === NumberFormat::FORMAT_TEXT)) { - return self::adjustSeparators((string) $value); + return self::adjustSeparators( + StringHelper::convertToString($value) + ); } // Ignore square-$-brackets prefix in format string, like "[$-411]ge.m.d", "[$-010419]0%", etc diff --git a/src/PhpSpreadsheet/Writer/Csv.php b/src/PhpSpreadsheet/Writer/Csv.php index f8a65426b6..0ec8aa4724 100644 --- a/src/PhpSpreadsheet/Writer/Csv.php +++ b/src/PhpSpreadsheet/Writer/Csv.php @@ -2,10 +2,10 @@ namespace PhpOffice\PhpSpreadsheet\Writer; +use Composer\Pcre\Preg; use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Spreadsheet; -use Stringable; class Csv extends BaseWriter { @@ -133,6 +133,7 @@ public function save($filename, int $flags = 0): void } } } + /** @var string[] $cellsArray */ $this->writeLine($this->fileHandle, $cellsArray); } @@ -271,25 +272,11 @@ public function getEnclosureRequired(): bool return $this->enclosureRequired; } - /** - * Convert boolean to TRUE/FALSE; otherwise return element cast to string. - * - * @param null|bool|float|int|string|Stringable $element element to be converted - */ - private static function elementToString(mixed $element): string - { - if (is_bool($element)) { - return $element ? 'TRUE' : 'FALSE'; - } - - return (string) $element; - } - /** * Write line to CSV file. * * @param resource $fileHandle PHP filehandle - * @param mixed[] $values Array containing values in a row + * @param string[] $values Array containing values in a row */ private function writeLine($fileHandle, array $values): void { @@ -299,9 +286,23 @@ private function writeLine($fileHandle, array $values): void // Build the line $line = ''; - /** @var null|bool|float|int|string|Stringable $element */ foreach ($values as $element) { - $element = self::elementToString($element); + if (Preg::isMatch('/^([+-])?(\d+)[.](\d+)/', $element, $matches)) { + // Excel will "convert" file with pop-up + // if there are more than 15 digits precision. + $whole = $matches[2]; + if ($whole !== '0') { + $wholeLen = strlen($whole); + $frac = $matches[3]; + $maxFracLen = 15 - $wholeLen; + if ($maxFracLen >= 0 && strlen($frac) > $maxFracLen) { + $result = sprintf("%.{$maxFracLen}F", $element); + if (str_contains($result, '.')) { + $element = Preg::replace('/[.]?0+$/', '', $result); // strip trailing zeros + } + } + } + } // Add delimiter $line .= $delimiter; $delimiter = $this->delimiter; diff --git a/tests/PhpSpreadsheetTests/Shared/Issue1324Test.php b/tests/PhpSpreadsheetTests/Shared/Issue1324Test.php new file mode 100644 index 0000000000..f2797d2f1b --- /dev/null +++ b/tests/PhpSpreadsheetTests/Shared/Issue1324Test.php @@ -0,0 +1,48 @@ +getActiveSheet(); + $sheet->getCell('A1')->setValue($s1); + self::assertNotSame(753.15, $sheet->getCell('A1')->getValue()); + $formats = ['Csv', 'Xlsx', 'Xls', 'Ods', 'Html']; + foreach ($formats as $format) { + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $format); + $rsheet = $reloadedSpreadsheet->getActiveSheet(); + $s2 = $rsheet->getCell('A1')->getValue(); + self::assertSame($s1, $s2, "difference for $format"); + $reloadedSpreadsheet->disconnectWorksheets(); + } + $spreadsheet->disconnectWorksheets(); + } + + public function testCsv(): void + { + $string1 = '753.149999999999'; + $s1 = (float) $string1; + $spreadsheet = new Spreadsheet(); + $sheet = $spreadsheet->getActiveSheet(); + $sheet->getCell('A1')->setValue($s1); + $writer = new CsvWriter($spreadsheet); + ob_start(); + $writer->save('php://output'); + $output = (string) ob_get_clean(); + self::assertStringContainsString($string1, $output); + $spreadsheet->disconnectWorksheets(); + } +}