Skip to content

Commit

Permalink
Merge pull request #118 from DataValues/release0100
Browse files Browse the repository at this point in the history
Release 0.10.0 with refined float to string conversion
  • Loading branch information
mariushoch authored Apr 11, 2018
2 parents b86eb12 + 0fcde54 commit 8272ed7
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 49 deletions.
2 changes: 1 addition & 1 deletion Number.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
return 1;
}

define( 'DATAVALUES_NUMBER_VERSION', '0.9.1' );
define( 'DATAVALUES_NUMBER_VERSION', '0.10.0' );
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,16 @@ the [Wikidata project](https://www.wikidata.org/).

## Release notes

### 0.10.0 (2018-04-10)

* Changed the float to string conversion algorithm for `DecimalValue`, `QuantityValue`, and
`UnboundedQuantityValue`. Instead of a hundred mostly irrelevant decimal places it now uses PHP's
"serialize_precision" default of 17 significant digits.
* Drop compatibility with data-values/interfaces 0.1 and data-values/common 0.2

### 0.9.1 (2017-08-09)

* Allow use with ~0.4.0 of DataValues/Common
* Allow use with data-values/common 0.4

### 0.9.0 (2017-08-09)

Expand Down Expand Up @@ -103,7 +110,7 @@ the [Wikidata project](https://www.wikidata.org/).
#### Other changes
* Fixed `DecimalValue` and `QuantityValue` allowing values with a newline at the end.
* `DecimalValue` strings are trimmed now, allowing any number of leading and trailing whitespace.
* Added explicit compatibility with DataValues Common 0.2 and 0.3.
* Added explicit compatibility with data-values/common 0.2 and 0.3.

### 0.6.0 (2015-09-09)

Expand Down
48 changes: 25 additions & 23 deletions src/DataValues/DecimalValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,29 +91,31 @@ private function convertToDecimal( $number ) {
throw new InvalidArgumentException( '$number must not be NAN or INF.' );
}

$decimal = strval( abs( $number ) );
$decimal = preg_replace_callback(
'/(\d*)\.(\d*)E([-+]\d+)/i',
function ( $matches ) {
list( , $before, $after, $exponent ) = $matches;

// Fill with as many zeros as necessary, and move the decimal point
if ( $exponent < 0 ) {
$before = str_repeat( '0', -$exponent - strlen( $before ) + 1 ) . $before;
$before = substr_replace( $before, '.', $exponent, 0 );
} else {
$after .= str_repeat( '0', $exponent - strlen( $after ) );
$after = substr_replace( $after, '.', $exponent, 0 );
}

// Remove not needed ".0" or just "." from the end
return $before . rtrim( rtrim( $after, '0' ), '.' );
},
$decimal,
1
);

return ( $number < 0 ? '-' : '+' ) . $decimal;
/**
* The 16 digits after the decimal point are derived from PHP's "serialize_precision"
* default of 17 significant digits (including 1 digit before the decimal point). This
* ensures a full float-string-float roundtrip.
* @see http://php.net/manual/en/ini.core.php#ini.serialize-precision
*/
$decimal = sprintf( '%.16e', abs( $number ) );
list( $base, $exponent ) = explode( 'e', $decimal );
list( $before, $after ) = explode( '.', $base );

// Fill with as many zeros as necessary, and move the decimal point
if ( $exponent < 0 ) {
$before = str_repeat( '0', -$exponent - strlen( $before ) + 1 ) . $before;
$before = substr_replace( $before, '.', $exponent, 0 );
} else {
$pad = $exponent - strlen( $after );
if ( $pad > 0 ) {
$after .= str_repeat( '0', $pad );
}
// Always add the decimal point back, even if the exponent is 0
$after = substr_replace( $after, '.', $exponent, 0 );
}

// Remove not needed ".0" or just "." from the end
return ( $number < 0 ? '-' : '+' ) . $before . rtrim( rtrim( $after, '0' ), '.' );
}

/**
Expand Down
72 changes: 49 additions & 23 deletions tests/DataValues/DecimalValueTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ public function testTrailingNewlineRobustness() {
* @dataProvider provideFloats
*/
public function testFloatInputs( $float, $expectedPrefix ) {
$originalLocale = setlocale( LC_NUMERIC, '0' );
setlocale( LC_NUMERIC, 'de_DE.utf8' );
$value = DecimalValue::newFromArray( $float );
setlocale( LC_NUMERIC, $originalLocale );

$this->assertStringStartsWith( $expectedPrefix, $value->getValue(), 'getValue' );
}
Expand Down Expand Up @@ -179,38 +182,61 @@ public function getSignProvider() {
/**
* @dataProvider getValueProvider
*/
public function testGetValue( DecimalValue $value, $expected ) {
$actual = $value->getValue();
public function testGetValue( $value, $expected ) {
$precision = ini_set( 'serialize_precision', '2' );
$actual = ( new DecimalValue( $value ) )->getValue();
ini_set( 'serialize_precision', $precision );

$this->assertSame( $expected, $actual );
}

public function getValueProvider() {
$argLists = [];

$argLists[] = [ new DecimalValue( 42 ), '+42' ];
$argLists[] = [ new DecimalValue( -42 ), '-42' ];
$argLists[] = [ new DecimalValue( -42.0 ), '-42' ];
$argLists[] = [ new DecimalValue( '-42' ), '-42' ];
$argLists[] = [ new DecimalValue( 4.5 ), '+4.5' ];
$argLists[] = [ new DecimalValue( -4.5 ), '-4.5' ];
$argLists[] = [ new DecimalValue( '+4.2' ), '+4.2' ];
$argLists[] = [ new DecimalValue( 0 ), '+0' ];
$argLists[] = [ new DecimalValue( 0.0 ), '+0' ];
$argLists[] = [ new DecimalValue( 1.0 ), '+1' ];
$argLists[] = [ new DecimalValue( 0.5 ), '+0.5' ];
$argLists[] = [ new DecimalValue( '-0.42' ), '-0.42' ];
$argLists[] = [ new DecimalValue( '-0.0' ), '+0.0' ];
$argLists[] = [ new DecimalValue( '-0' ), '+0' ];
$argLists[] = [ new DecimalValue( '+0.0' ), '+0.0' ];
$argLists[] = [ new DecimalValue( '+0' ), '+0' ];
$argLists[] = [ new DecimalValue( 2147483649 ), '+2147483649' ];
$argLists[] = [ new DecimalValue( 1000000000000000 ), '+1000000000000000' ];
$argLists[] = [ 42, '+42' ];
$argLists[] = [ -42, '-42' ];
$argLists[] = [ -42.0, '-42' ];
$argLists[] = [ '-42', '-42' ];
$argLists[] = [ 4.5, '+4.5' ];
$argLists[] = [ -4.5, '-4.5' ];
$argLists[] = [ '+4.2', '+4.2' ];
$argLists[] = [ 0, '+0' ];
$argLists[] = [ 0.0, '+0' ];
$argLists[] = [ 1.0, '+1' ];
$argLists[] = [ 0.5, '+0.5' ];
$argLists[] = [ '-0.42', '-0.42' ];
$argLists[] = [ '-0.0', '+0.0' ];
$argLists[] = [ '-0', '+0' ];
$argLists[] = [ '+0.0', '+0.0' ];
$argLists[] = [ '+0', '+0' ];
$argLists[] = [ 2147483649, '+2147483649' ];
$argLists[] = [ 1000000000000000, '+1000000000000000' ];
$argLists[] = [
1 + 1e-12 / 3,
'+1.0000000000003333'
];
$argLists[] = [
1 + 1e-13 / 3,
'+1.0000000000000333'
];
$argLists[] = [
1 + 1e-14 / 3,
'+1.0000000000000033'
];
$argLists[] = [
1 + 1e-15 / 3,
'+1.0000000000000004'
];
$argLists[] = [
1 + 1e-16 / 3,
'+1'
];
$argLists[] = [
new DecimalValue( 1 + 1e-12 / 3 ),
'+1.0000000000003'
1 - 1e-16,
'+0.99999999999999989'
];
$argLists[] = [
new DecimalValue( 1 + 1e-14 / 3 ),
1 - 1e-17,
'+1'
];

Expand Down

0 comments on commit 8272ed7

Please sign in to comment.