Skip to content
This repository was archived by the owner on Dec 27, 2023. It is now read-only.

Commit ccf88b1

Browse files
committed
Merge pull request #40 from shabbyrobe/float-precision
Fix for float precision errors using number_format() #37, by @shabbyrobe .
2 parents 59bc3c2 + d3ece68 commit ccf88b1

File tree

2 files changed

+66
-2
lines changed

2 files changed

+66
-2
lines changed

src/Decimal.php

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,24 @@ public static function fromFloat($fltValue, $scale = null, $removeZeros = false)
138138
);
139139
}
140140

141-
$scale = ($scale === null) ? 16 : $scale;
141+
$defaultScale = 16;
142+
143+
$strValue = (string) $fltValue;
144+
if (preg_match("/^ (?P<int> \d*) (?: \. (?P<dec> \d+) ) E (?P<sign>[\+\-]) (?P<exp>\d+) $/x", $strValue, $capture)) {
145+
if ($scale === null) {
146+
if ($capture['sign'] == '-') {
147+
$scale = $capture['exp'] + strlen($capture['dec']);
148+
} else {
149+
$scale = $defaultScale;
150+
}
151+
}
152+
$strValue = number_format($fltValue, $scale, '.', '');
153+
}
154+
155+
if ($scale === null) {
156+
$scale = $defaultScale;
157+
}
142158

143-
$strValue = number_format($fltValue, $scale, '.', '');
144159
if ($removeZeros) {
145160
$strValue = self::removeTrailingZeros($strValue, $scale);
146161
}

tests/Decimal/DecimalFromFloatTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,53 @@ public function testNoFloat()
3838
{
3939
Decimal::fromFloat(5);
4040
}
41+
42+
public function floatProvider()
43+
{
44+
$tests = [
45+
[1.1, "1.1"],
46+
[1234567890.0, "1234567890"],
47+
[1.1234567890, "1.123456789"],
48+
[-1.1234567890, "-1.123456789"],
49+
[0.000001, "0.0000010"],
50+
[0.000001, "0.00", 2],
51+
[0.000001, "0.000001", null, !!'removeZeroes'],
52+
[90.05, "90.05"],
53+
];
54+
55+
if (PHP_INT_SIZE >= 8) {
56+
// These tests probably won't work if you're not testing on x86-64.
57+
// It might also be better to mark the tests skipped. It is certainly
58+
// useful to cover this functionality off though as it hits the exponent
59+
// parsing in Decimal::fromFloat()
60+
$tests[] = [
61+
1230123074129038740129734907810923874017283094.1,
62+
"1230123074129038665578332283019326242900934656.0000000000000000"
63+
];
64+
$tests[] = [
65+
1230123074129038740129734907810923874017283094.1,
66+
"1230123074129038665578332283019326242900934656",
67+
0
68+
];
69+
$tests[] = [
70+
0.0000000000000000000000000000000000000000000000123412351234,
71+
"0.0000000000000000000000000000000000000000000000123412351234",
72+
];
73+
$tests[] = [
74+
0.0000000000000000000000000000000000000000000000123412351234,
75+
"0.00",
76+
2
77+
];
78+
}
79+
return $tests;
80+
}
81+
82+
/**
83+
* @dataProvider floatProvider
84+
*/
85+
public function testFromFloat($in, $str, $scale=null, $removeZeroes=false)
86+
{
87+
$v = Decimal::fromFloat($in, $scale, $removeZeroes);
88+
$this->assertSame($str, $v->innerValue());
89+
}
4190
}

0 commit comments

Comments
 (0)