Daniel Kinzler has uploaded a new change for review.
https://gerrit.wikimedia.org/r/97907
Change subject: (bug 57588) Scientific notation for quantities.
......................................................................
(bug 57588) Scientific notation for quantities.
This adds support scientific (exponent) notation to
DecimalParser and QuantityParser.
Change-Id: Iff70e26f46b10eb18b79861cc60dac8e4e15fa25
---
M DataValuesCommon/src/DataValues/DecimalMath.php
M DataValuesCommon/src/ValueParsers/DecimalParser.php
M DataValuesCommon/src/ValueParsers/QuantityParser.php
M DataValuesCommon/tests/DataValues/DecimalMathTest.php
M DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
M DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
6 files changed, 156 insertions(+), 5 deletions(-)
git pull ssh://gerrit.wikimedia.org:29418/mediawiki/extensions/DataValues
refs/changes/07/97907/1
diff --git a/DataValuesCommon/src/DataValues/DecimalMath.php
b/DataValuesCommon/src/DataValues/DecimalMath.php
index 9930146..4ec0c6c 100644
--- a/DataValuesCommon/src/DataValues/DecimalMath.php
+++ b/DataValuesCommon/src/DataValues/DecimalMath.php
@@ -482,7 +482,7 @@
$slumped = substr( $value, 0, $i ) . $slumped;
// strip leading zeros
- $slumped = preg_replace( '/^([-+])(0+)([0-9]+(\.|$))/', '\1\3',
$slumped );
+ $slumped = $this->stripLeadingZeros( $slumped );
if ( $slumped === '-0' ) {
$slumped = '+0';
@@ -490,4 +490,60 @@
return $slumped;
}
+
+ /**
+ * @param string $digits
+ *
+ * @return string
+ */
+ protected function stripLeadingZeros( $digits ) {
+ $digits = preg_replace( '/^([-+])(0+)([0-9]+(\.|$))/', '\1\3',
$digits );
+ return $digits;
+ }
+
+ /**
+ * Shift the decimal point according to the given exponent.
+ *
+ * @param DecimalValue $decimal
+ * @param int $exponent The exponent to apply (digits to shift by). A
Positive exponent
+ * shifts the decimal point to the right, a negative exponent shifts to
the left.
+ *
+ * @throws \InvalidArgumentException
+ * @return DecimalValue
+ */
+ public function shift( DecimalValue $decimal, $exponent ) {
+ if ( !is_int( $exponent ) ) {
+ throw new \InvalidArgumentException( '$exponent must be
an integer' );
+ }
+
+ if ( $exponent == 0 ) {
+ return $decimal;
+ }
+
+ $sign = $decimal->getSign();
+ $intPart = $decimal->getIntegerPart();
+ $fractPart = $decimal->getFractionalPart();
+
+ if ( $exponent < 0 ) {
+ // shift left
+ if ( -$exponent < strlen( $intPart ) ) {
+ $intPart = substr( $intPart, 0, -$exponent ) .
'.' . substr( $intPart, -$exponent );
+ } else {
+ $intPart = '0.' . str_pad( $intPart,
-$exponent, '0', STR_PAD_LEFT );
+ }
+ } else {
+ // shift right
+ if ( $exponent < strlen( $fractPart ) ) {
+ $fractPart = substr( $fractPart, 0, $exponent )
. '.' . substr( $fractPart, $exponent );
+ } else {
+ $fractPart = str_pad( $fractPart, $exponent,
'0', STR_PAD_RIGHT );
+ }
+ }
+
+ // assemble result
+ $digits = $sign . $intPart . $fractPart;
+ $digits = $this->stripLeadingZeros( $digits );
+
+ return new DecimalValue( $digits );
+ }
}
diff --git a/DataValuesCommon/src/ValueParsers/DecimalParser.php
b/DataValuesCommon/src/ValueParsers/DecimalParser.php
index 5d8e548..bc72ed8 100644
--- a/DataValuesCommon/src/ValueParsers/DecimalParser.php
+++ b/DataValuesCommon/src/ValueParsers/DecimalParser.php
@@ -2,6 +2,7 @@
namespace ValueParsers;
+use DataValues\DecimalMath;
use DataValues\DecimalValue;
use DataValues\IllegalValueException;
@@ -16,13 +17,29 @@
class DecimalParser extends StringValueParser {
/**
+ * @var DecimalMath
+ */
+ private $math;
+
+ /**
+ * @return DecimalMath
+ */
+ private function getMath() {
+ if ( $this->math === null ) {
+ $this->math = new DecimalMath();
+ }
+
+ return $this->math;
+ }
+
+ /**
* Creates a DecimalValue from a given string.
*
* The decimal notation for the value is based on ISO 31-0, with some
modifications:
* - the decimal separator is '.' (period). Comma is not used anywhere.
* - leading and trailing as well as any internal whitespace is ignored
* - the following characters are ignored: comma (","), apostrophe
("'").
- * - scientific (exponential) notation is not used.
+ * - scientific (exponential) notation is supported using the pattern
/e[-+][0-9]+/
* - the number may start (or end) with a decimal point.
* - leading zeroes are stripped, except directly before the decimal
point
* - trailing zeroes are stripped, except directly after the decimal
point
@@ -38,6 +55,17 @@
* @throws ParseException
*/
protected function stringParse( $value ) {
+
+ //handle scientific notation
+ if ( preg_match( '/^(.*)([eE]|x10\^)([-+]?[,\d]+)$/', $value,
$matches ) ) {
+ $exponent = $this->normalizeDecimal( $matches[3] );
+ $exponent = intval( $exponent );
+
+ $value = $matches[1];
+ } else {
+ $exponent = 0;
+ }
+
$value = $this->normalizeDecimal( $value );
if ( $value === '' ) {
@@ -46,6 +74,12 @@
try {
$decimal = new DecimalValue( $value );
+
+ if ( $exponent ) {
+ $math = $this->getMath();
+ $decimal = $math->shift( $decimal, $exponent );
+ }
+
return $decimal;
} catch ( IllegalValueException $ex ) {
throw new ParseException( $ex->getMessage() );
@@ -80,5 +114,4 @@
return $number;
}
-
}
diff --git a/DataValuesCommon/src/ValueParsers/QuantityParser.php
b/DataValuesCommon/src/ValueParsers/QuantityParser.php
index 3d1d1b5..611ddfb 100644
--- a/DataValuesCommon/src/ValueParsers/QuantityParser.php
+++ b/DataValuesCommon/src/ValueParsers/QuantityParser.php
@@ -17,7 +17,7 @@
*/
class QuantityParser extends StringValueParser {
- const NUMBER_PATTERN =
'(?:[-+]\s*)?(?:[0-9,\'`]+\.[0-9,\'`]*|\.?[0-9,\'`]+)?';
+ const NUMBER_PATTERN =
'(?:[-+]\s*)?(?:[0-9,\'`]+\.[0-9,\'`]*|\.?[0-9,\'`]+)(?:[eE][-+]?[0-9,\'`]+)?';
const UNIT_PATTERN = '[a-zA-ZµåÅöÖ°%][-.a-zA-Z0-9åÅöÖ°%²³^]*';
@@ -105,7 +105,7 @@
$pattern = '@^'
. '\s*(' . self::NUMBER_PATTERN . ')' // $1: amount
. '\s*(?:'
- . '([!~])' // $2: '!' for "exact", '~' for
"approx", or nothing
+ . '([~!])' // $2: '!' for "exact", '~' for
"approx", or nothing
. '|(?:\+/?-|±)\s*(' . self::NUMBER_PATTERN .
')' // $3: plus/minus offset (uncertainty margin)
. '|' // or nothing
. ')'
diff --git a/DataValuesCommon/tests/DataValues/DecimalMathTest.php
b/DataValuesCommon/tests/DataValues/DecimalMathTest.php
index 4ca9c9c..f9078e8 100644
--- a/DataValuesCommon/tests/DataValues/DecimalMathTest.php
+++ b/DataValuesCommon/tests/DataValues/DecimalMathTest.php
@@ -387,4 +387,55 @@
return $argLists;
}
+
+ /**
+ * @dataProvider shiftProvider
+ *
+ * @param DecimalValue $value
+ * @param $exponent
+ * @param $expected
+ */
+ public function testShift( DecimalValue $value, $exponent, $expected ) {
+ $math = new DecimalMath();
+
+ $actual = $math->shift( $value, $exponent );
+ $this->assertEquals( $expected, $actual->getValue() );
+ }
+
+ public function shiftProvider() {
+ $argLists = array();
+
+ $argLists[] = array( new DecimalValue( '+0' ), 0, '+0' );
+ $argLists[] = array( new DecimalValue( '+0' ), 1, '+0' );
+ $argLists[] = array( new DecimalValue( '+0' ), 2, '+0' );
+ $argLists[] = array( new DecimalValue( '+0' ), -1, '+0.0' );
+ $argLists[] = array( new DecimalValue( '+0' ), -2, '+0.00' );
+
+ $argLists[] = array( new DecimalValue( '+0.0' ), 0, '+0.0' );
+ $argLists[] = array( new DecimalValue( '+0.0' ), 1, '+0' );
+ $argLists[] = array( new DecimalValue( '+0.0' ), 2, '+0' );
+ $argLists[] = array( new DecimalValue( '+0.0' ), -1, '+0.00' );
+ $argLists[] = array( new DecimalValue( '+0.0' ), -2, '+0.000' );
+
+ $argLists[] = array( new DecimalValue( '-25' ), 0, '-25' );
+ $argLists[] = array( new DecimalValue( '-25' ), 1, '-250' );
+ $argLists[] = array( new DecimalValue( '-25' ), 2, '-2500' );
+ $argLists[] = array( new DecimalValue( '-25' ), -1, '-2.5' );
+ $argLists[] = array( new DecimalValue( '-25' ), -2, '-0.25' );
+ $argLists[] = array( new DecimalValue( '-25' ), -3, '-0.025' );
+ $argLists[] = array( new DecimalValue( '-25' ), -4, '-0.0025' );
+
+ $argLists[] = array( new DecimalValue( '-2.5' ), 0, '-2.5' );
+ $argLists[] = array( new DecimalValue( '-2.5' ), 1, '-25' );
+ $argLists[] = array( new DecimalValue( '-2.5' ), 2, '-250' );
+ $argLists[] = array( new DecimalValue( '-2.5' ), -1, '-0.25' );
+ $argLists[] = array( new DecimalValue( '-2.5' ), -2, '-0.025' );
+ $argLists[] = array( new DecimalValue( '-2.5' ), -3, '-0.0025'
);
+
+ $argLists[] = array( new DecimalValue( '+5' ), -4, '+0.0005' );
+ $argLists[] = array( new DecimalValue( '+5.0' ), -4, '+0.00050'
);
+ $argLists[] = array( new DecimalValue( '+5.00' ), -4,
'+0.000500' );
+
+ return $argLists;
+ }
}
diff --git a/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
b/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
index bcca87d..5ce83e1 100644
--- a/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
+++ b/DataValuesCommon/tests/ValueParsers/DecimalParserTest.php
@@ -45,6 +45,10 @@
',3,' => 3,
'2.125' => 2.125,
'2.1250' => '+2.1250',
+ '2.1250e0' => '+2.1250',
+ '2.1250e3' => '+2125.0',
+ '2.1250e+3' => '+2125.0',
+ '2.1250e-2' => '+0.021250',
' 5 ' => 5,
'100,000' => 100000,
'100 000' => 100000,
diff --git a/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
b/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
index 3b2082a..fcda9e4 100644
--- a/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
+++ b/DataValuesCommon/tests/ValueParsers/QuantityParserTest.php
@@ -50,6 +50,13 @@
'100,003' => QuantityValue::newFromNumber( 100003, '1',
100004, 100002 ),
'100\'003' => QuantityValue::newFromNumber( 100003,
'1', 100004, 100002 ),
+ '1.4e-2' => QuantityValue::newFromNumber( '+0.014',
'1', '+0.015', '+0.013' ),
+ '1.4e3' => QuantityValue::newFromNumber( '+1400', '1',
'+1401', '+1399' ),
+ '1.4e3!m' => QuantityValue::newFromNumber( '+1400',
'm', '+1400', '+1400' ),
+ '1.4e3m2' => QuantityValue::newFromNumber( '+1400',
'm2', '+1401', '+1399' ),
+ '1.4ev' => QuantityValue::newFromNumber( '+1.4', 'ev',
'+1.5', '+1.3' ),
+ '1.4e' => QuantityValue::newFromNumber( '+1.4', 'e',
'+1.5', '+1.3' ),
+
// precision
'0!' => QuantityValue::newFromNumber( 0, '1', 0, 0 ),
'10.003!' => QuantityValue::newFromNumber( '+10.003',
'1', '+10.003', '+10.003' ),
--
To view, visit https://gerrit.wikimedia.org/r/97907
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Iff70e26f46b10eb18b79861cc60dac8e4e15fa25
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/extensions/DataValues
Gerrit-Branch: master
Gerrit-Owner: Daniel Kinzler <[email protected]>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits