/** * @param DecimalValue $margin * @param int $roundingExponent * * @return string|null Text */ private function formatMargin(DecimalValue $margin, $roundingExponent) { if ($this->options->getOption(self::OPT_SHOW_UNCERTAINTY_MARGIN)) { // TODO: never round to 0! See bug #56892 $roundedMargin = $this->decimalMath->roundToExponent($margin, $roundingExponent); if (!$roundedMargin->isZero()) { return $this->decimalFormatter->format($roundedMargin); } } return null; }
/** * @dataProvider shiftProvider */ public function testShift(DecimalValue $value, $exponent, $expected) { $math = new DecimalMath(); $actual = $math->shift($value, $exponent); $this->assertEquals($expected, $actual->getValue()); }
/** * Returns a QuantityValue representing the given amount, automatically assuming * a level of uncertainty based on the digits given. * * The upper and lower bounds are determined automatically from the given * digits by increasing resp. decreasing the least significant digit. * E.g. "+0.01" would have upperBound "+0.02" and lowerBound "+0.01", * while "-100" would have upperBound "-99" and lowerBound "-101". * * @param DecimalValue $amount The quantity * @param string $unit The quantity's unit (use "1" for unit-less quantities) * @param int $exponent Decimal exponent to apply * * @return QuantityValue */ private function newUncertainQuantityFromDigits(DecimalValue $amount, $unit = '1', $exponent = 0) { $math = new DecimalMath(); if ($amount->getSign() === '+') { $upperBound = $math->bump($amount); $lowerBound = $math->slump($amount); } else { $upperBound = $math->slump($amount); $lowerBound = $math->bump($amount); } $amount = $this->decimalParser->applyDecimalExponent($amount, $exponent); $lowerBound = $this->decimalParser->applyDecimalExponent($lowerBound, $exponent); $upperBound = $this->decimalParser->applyDecimalExponent($upperBound, $exponent); return new QuantityValue($amount, $unit, $upperBound, $lowerBound); }
/** * Returns a transformed value derived from this QuantityValue by applying * the given transformation to the amount and the upper and lower bounds. * The resulting amount and bounds are rounded to the significant number of * digits. Note that for exact quantities (with at least one bound equal to * the amount), no rounding is applied (since they are considered to have * infinite precision). * * The transformation is provided as a callback, which must implement a * monotonously increasing, fully differentiable function mapping a DecimalValue * to a DecimalValue. Typically, it will be a linear transformation applying a * factor and an offset. * * @param string $newUnit The unit of the transformed quantity. * * @param callable $transformation A callback that implements the desired transformation. * The transformation will be called three times, once for the amount, once * for the lower bound, and once for the upper bound. It must return a DecimalValue. * The first parameter passed to $transformation is the DecimalValue to transform * In addition, any extra parameters passed to transform() will be passed through * to the transformation callback. * * @param mixed ... Any extra parameters will be passed to the $transformation function. * * @throws InvalidArgumentException * @return QuantityValue */ public function transform($newUnit, $transformation) { if (!is_callable($transformation)) { throw new InvalidArgumentException('$transformation must be callable.'); } if (!is_string($newUnit)) { throw new InvalidArgumentException('$newUnit must be a string. Use "1" as the unit for unit-less quantities.'); } if ($newUnit === '') { throw new InvalidArgumentException('$newUnit must not be empty. Use "1" as the unit for unit-less quantities.'); } $oldUnit = $this->getUnit(); if ($newUnit === null) { $newUnit = $oldUnit; } // Apply transformation by calling the $transform callback. // The first argument for the callback is the DataValue to transform. In addition, // any extra arguments given for transform() are passed through. $args = func_get_args(); array_shift($args); $args[0] = $this->getAmount(); $amount = call_user_func_array($transformation, $args); $args[0] = $this->getUpperBound(); $upperBound = call_user_func_array($transformation, $args); $args[0] = $this->getLowerBound(); $lowerBound = call_user_func_array($transformation, $args); // use a preliminary QuantityValue to determine the significant number of digits $transformed = new self($amount, $newUnit, $upperBound, $lowerBound); $roundingExponent = $transformed->getOrderOfUncertainty(); // apply rounding to the significant digits $math = new DecimalMath(); $amount = $math->roundToExponent($amount, $roundingExponent); $upperBound = $math->roundToExponent($upperBound, $roundingExponent); $lowerBound = $math->roundToExponent($lowerBound, $roundingExponent); return new self($amount, $newUnit, $upperBound, $lowerBound); }