Skip to content

Commit

Permalink
Merge pull request #1359 from re2bit/precision-float
Browse files Browse the repository at this point in the history
Precision float
  • Loading branch information
goetas authored Oct 30, 2021
2 parents eb87fa6 + 9c3e9f2 commit 0c83e1f
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 2 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ phpstan.xml
.phpcs-cache
/doc/_build/*
/.phpunit.result.cache
**/.DS_STORE
6 changes: 6 additions & 0 deletions doc/reference/annotations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,12 @@ Available Types:
+------------------------------------------------------------+--------------------------------------------------+
| double or float | Primitive double |
+------------------------------------------------------------+--------------------------------------------------+
| double<2> or float<2> | Primitive double with percision |
+------------------------------------------------------------+--------------------------------------------------+
| double<2, 'HALF_DOWN'> or float<2, 'HALF_DOWN'> | Primitive double with percision and |
| | Rounding Mode. |
| | (HALF_UP, HALF_DOWN, HALF_EVEN HALF_ODD) |
+------------------------------------------------------------+--------------------------------------------------+
| string | Primitive string |
+------------------------------------------------------------+--------------------------------------------------+
| array | An array with arbitrary keys, and values. |
Expand Down
1 change: 0 additions & 1 deletion phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ parameters:
- '~Class Doctrine\\Common\\Persistence\\Proxy not found~'
- '~Class Doctrine\\ODM\\MongoDB\\PersistentCollection not found~'
- '~Class Symfony\\Component\\Translation\\TranslatorInterface not found~'
- '#Instantiated class Doctrine\\Common\\Cache\\FilesystemCache not found\.#'
- '#Constructor of class JMS\\Serializer\\Annotation\\.*? has an unused parameter#'

paths:
Expand Down
20 changes: 20 additions & 0 deletions src/AbstractVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,24 @@ protected function assertValueCanCastToFloat($value): void
throw new NonFloatCastableTypeException($value);
}
}

protected function mapRoundMode(?string $roundMode = null): int
{
switch ($roundMode) {
case 'HALF_DOWN':
$roundMode = PHP_ROUND_HALF_DOWN;
break;
case 'HALF_EVEN':
$roundMode = PHP_ROUND_HALF_EVEN;
break;
case 'HALF_ODD':
$roundMode = PHP_ROUND_HALF_ODD;
break;
case 'HALF_UP':
default:
$roundMode = PHP_ROUND_HALF_UP;
}

return $roundMode;
}
}
10 changes: 9 additions & 1 deletion src/JsonSerializationVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,15 @@ public function visitInteger(int $data, array $type)
*/
public function visitDouble(float $data, array $type)
{
return $data;
$percision = $type['params'][0] ?? null;
if (!is_int($percision)) {
return $data;
}

$roundMode = $type['params'][1] ?? null;
$roundMode = $this->mapRoundMode($roundMode);

return round($data, $percision, $roundMode);
}

/**
Expand Down
7 changes: 7 additions & 0 deletions src/XmlSerializationVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,13 @@ public function visitInteger(int $data, array $type)
*/
public function visitDouble(float $data, array $type)
{
$percision = $type['params'][0] ?? null;
if (is_int($percision)) {
$roundMode = $type['params'][1] ?? null;
$roundMode = $this->mapRoundMode($roundMode);
$data = round($data, $percision, $roundMode);
}

return $this->document->createTextNode(var_export((float) $data, true));
}

Expand Down
84 changes: 84 additions & 0 deletions tests/Fixtures/ObjectWithFloatProperty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace JMS\Serializer\Tests\Fixtures;

use JMS\Serializer\Annotation\Type;

class ObjectWithFloatProperty
{
/**
* @Type("float")
* @var float
*/
#[Type(name: 'float')]
private $floatingPointUnchanged;

/**
* @Type("float<2,'HALF_DOWN'>")
* @var float
*/
#[Type(name: 'float<2, "HALF_DOWN">')]
private $floatingPointHalfDown;

/**
* @Type("double<1, 'HALF_EVEN'>")
* @var float
*/
#[Type(name: 'double<1, "HALF_EVEN">')]
private $floatingPointHalfEven;

/**
* @Type("float<1, 'HALF_ODD'>")
* @var float
*/
#[Type(name: 'float<1, "HALF_ODD">')]
private $floatingPointHalfOdd;

/**
* @Type("double<2>")
* @var float
*/
#[Type(name: 'double<2, "HALF_UP">')]
private $floatingPointHalfUp;

public function __construct(
float $floatingPointUnchanged,
float $floatingPointHalfDown,
float $floatingPointHalfEven,
float $floatingPointHalfOdd,
float $floatingPointHalfUp
) {
$this->floatingPointUnchanged = $floatingPointUnchanged;
$this->floatingPointHalfDown = $floatingPointHalfDown;
$this->floatingPointHalfEven = $floatingPointHalfEven;
$this->floatingPointHalfOdd = $floatingPointHalfOdd;
$this->floatingPointHalfUp = $floatingPointHalfUp;
}

public function getFloatingPointUnchanged(): float
{
return $this->floatingPointUnchanged;
}

public function getFloatingPointHalfDown(): float
{
return $this->floatingPointHalfDown;
}

public function getFloatingPointHalfEven(): float
{
return $this->floatingPointHalfEven;
}

public function getFloatingPointHalfOdd(): float
{
return $this->floatingPointHalfOdd;
}

public function getFloatingPointHalfUp(): float
{
return $this->floatingPointHalfUp;
}
}
28 changes: 28 additions & 0 deletions tests/Serializer/JsonSerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use JMS\Serializer\Tests\Fixtures\AuthorList;
use JMS\Serializer\Tests\Fixtures\FirstClassMapCollection;
use JMS\Serializer\Tests\Fixtures\ObjectWithEmptyArrayAndHash;
use JMS\Serializer\Tests\Fixtures\ObjectWithFloatProperty;
use JMS\Serializer\Tests\Fixtures\ObjectWithInlineArray;
use JMS\Serializer\Tests\Fixtures\Tag;
use JMS\Serializer\Visitor\Factory\JsonSerializationVisitorFactory;
Expand Down Expand Up @@ -373,6 +374,9 @@ public function getTypeHintedArrays()

[['a', 'b'], '{"0":"a","1":"b"}', SerializationContext::create()->setInitialType('array<integer,string>')],
[['a' => 'a', 'b' => 'b'], '{"a":"a","b":"b"}', SerializationContext::create()->setInitialType('array<string,string>')],

[[15.6, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array<float>')],
[[5.2 * 3, 2], '[15.6,2.0]', SerializationContext::create()->setInitialType('array<float>')],
];
}

Expand Down Expand Up @@ -429,6 +433,30 @@ public function testTypeHintedArrayAndStdClassSerialization(array $array, $expec
self::assertEquals($expected, $this->serialize($array, $context));
}

public function testSerialisationWithPercisionForFloat(): void
{
$objectWithFloat = new ObjectWithFloatProperty(
1.555555555,
1.555,
1.15,
1.15,
1.555
);

$result = $this->serialize($objectWithFloat, SerializationContext::create());

static::assertEquals(
'{'
. '"floating_point_unchanged":1.555555555,'
. '"floating_point_half_down":1.55,'
. '"floating_point_half_even":1.2,'
. '"floating_point_half_odd":1.1,'
. '"floating_point_half_up":1.56'
. '}',
$result
);
}

protected function getFormat()
{
return 'json';
Expand Down
26 changes: 26 additions & 0 deletions tests/Serializer/XmlSerializationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use JMS\Serializer\Tests\Fixtures\Discriminator\ObjectWithXmlNotCDataDiscriminatorParent;
use JMS\Serializer\Tests\Fixtures\Input;
use JMS\Serializer\Tests\Fixtures\InvalidUsageOfXmlValue;
use JMS\Serializer\Tests\Fixtures\ObjectWithFloatProperty;
use JMS\Serializer\Tests\Fixtures\ObjectWithNamespacesAndList;
use JMS\Serializer\Tests\Fixtures\ObjectWithNamespacesAndNestedList;
use JMS\Serializer\Tests\Fixtures\ObjectWithVirtualXmlProperties;
Expand Down Expand Up @@ -569,6 +570,31 @@ public function testDoubleEncoding()
setlocale(LC_ALL, $locale);
}

public function testSerialisationWithPercisionForFloat(): void
{
$objectWithFloat = new ObjectWithFloatProperty(
1.555555555,
1.555,
1.15,
1.15,
1.555
);

$result = $this->serialize($objectWithFloat, SerializationContext::create());

static::assertXmlStringEqualsXmlString(
'<?xml version="1.0" encoding="UTF-8"?>
<result>
<floating_point_unchanged>1.555555555</floating_point_unchanged>
<floating_point_half_down>1.55</floating_point_half_down>
<floating_point_half_even>1.2</floating_point_half_even>
<floating_point_half_odd>1.1</floating_point_half_odd>
<floating_point_half_up>1.56</floating_point_half_up>
</result>',
$result
);
}

private function xpathFirstToString(\SimpleXMLElement $xml, $xpath)
{
$nodes = $xml->xpath($xpath);
Expand Down

0 comments on commit 0c83e1f

Please sign in to comment.