/** * 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); }