Skip to content

Commit 886a8c3

Browse files
authored
Merge pull request #4575 from oleibman/issue1324
Additional Floating Point Precision Changes
2 parents 8d8bc23 + 0c8db6e commit 886a8c3

File tree

4 files changed

+71
-19
lines changed

4 files changed

+71
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). Thia is a
3636

3737
### Fixed
3838

39+
- Additional floating-point precision changes. [Issue #1324](https://github.com/PHPOffice/PhpSpreadsheet/issues/1324) [PR #4575](https://github.com/PHPOffice/PhpSpreadsheet/pull/4575)
3940
- 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)
4041
- 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)
4142
- 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)

src/PhpSpreadsheet/Style/NumberFormat/Formatter.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ public static function toFormattedString($value, string $format, null|array|call
159159
// For 'General' format code, we just pass the value although this is not entirely the way Excel does it,
160160
// it seems to round numbers to a total of 10 digits.
161161
if (($format === NumberFormat::FORMAT_GENERAL) || ($format === NumberFormat::FORMAT_TEXT)) {
162-
return self::adjustSeparators((string) $value);
162+
return self::adjustSeparators(
163+
StringHelper::convertToString($value)
164+
);
163165
}
164166

165167
// Ignore square-$-brackets prefix in format string, like "[$-411]ge.m.d", "[$-010419]0%", etc

src/PhpSpreadsheet/Writer/Csv.php

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace PhpOffice\PhpSpreadsheet\Writer;
44

5+
use Composer\Pcre\Preg;
56
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
67
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
78
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8-
use Stringable;
99

1010
class Csv extends BaseWriter
1111
{
@@ -133,6 +133,7 @@ public function save($filename, int $flags = 0): void
133133
}
134134
}
135135
}
136+
/** @var string[] $cellsArray */
136137
$this->writeLine($this->fileHandle, $cellsArray);
137138
}
138139

@@ -271,25 +272,11 @@ public function getEnclosureRequired(): bool
271272
return $this->enclosureRequired;
272273
}
273274

274-
/**
275-
* Convert boolean to TRUE/FALSE; otherwise return element cast to string.
276-
*
277-
* @param null|bool|float|int|string|Stringable $element element to be converted
278-
*/
279-
private static function elementToString(mixed $element): string
280-
{
281-
if (is_bool($element)) {
282-
return $element ? 'TRUE' : 'FALSE';
283-
}
284-
285-
return (string) $element;
286-
}
287-
288275
/**
289276
* Write line to CSV file.
290277
*
291278
* @param resource $fileHandle PHP filehandle
292-
* @param mixed[] $values Array containing values in a row
279+
* @param string[] $values Array containing values in a row
293280
*/
294281
private function writeLine($fileHandle, array $values): void
295282
{
@@ -299,9 +286,23 @@ private function writeLine($fileHandle, array $values): void
299286
// Build the line
300287
$line = '';
301288

302-
/** @var null|bool|float|int|string|Stringable $element */
303289
foreach ($values as $element) {
304-
$element = self::elementToString($element);
290+
if (Preg::isMatch('/^([+-])?(\d+)[.](\d+)/', $element, $matches)) {
291+
// Excel will "convert" file with pop-up
292+
// if there are more than 15 digits precision.
293+
$whole = $matches[2];
294+
if ($whole !== '0') {
295+
$wholeLen = strlen($whole);
296+
$frac = $matches[3];
297+
$maxFracLen = 15 - $wholeLen;
298+
if ($maxFracLen >= 0 && strlen($frac) > $maxFracLen) {
299+
$result = sprintf("%.{$maxFracLen}F", $element);
300+
if (str_contains($result, '.')) {
301+
$element = Preg::replace('/[.]?0+$/', '', $result); // strip trailing zeros
302+
}
303+
}
304+
}
305+
}
305306
// Add delimiter
306307
$line .= $delimiter;
307308
$delimiter = $this->delimiter;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Shared;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Writer\Csv as CsvWriter;
9+
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
10+
11+
class Issue1324Test extends AbstractFunctional
12+
{
13+
protected static int $version = 80400;
14+
15+
public function testPrecision(): void
16+
{
17+
$string1 = '753.149999999999';
18+
$s1 = (float) $string1;
19+
$spreadsheet = new Spreadsheet();
20+
$sheet = $spreadsheet->getActiveSheet();
21+
$sheet->getCell('A1')->setValue($s1);
22+
self::assertNotSame(753.15, $sheet->getCell('A1')->getValue());
23+
$formats = ['Csv', 'Xlsx', 'Xls', 'Ods', 'Html'];
24+
foreach ($formats as $format) {
25+
$reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $format);
26+
$rsheet = $reloadedSpreadsheet->getActiveSheet();
27+
$s2 = $rsheet->getCell('A1')->getValue();
28+
self::assertSame($s1, $s2, "difference for $format");
29+
$reloadedSpreadsheet->disconnectWorksheets();
30+
}
31+
$spreadsheet->disconnectWorksheets();
32+
}
33+
34+
public function testCsv(): void
35+
{
36+
$string1 = '753.149999999999';
37+
$s1 = (float) $string1;
38+
$spreadsheet = new Spreadsheet();
39+
$sheet = $spreadsheet->getActiveSheet();
40+
$sheet->getCell('A1')->setValue($s1);
41+
$writer = new CsvWriter($spreadsheet);
42+
ob_start();
43+
$writer->save('php://output');
44+
$output = (string) ob_get_clean();
45+
self::assertStringContainsString($string1, $output);
46+
$spreadsheet->disconnectWorksheets();
47+
}
48+
}

0 commit comments

Comments
 (0)