public function matchBest($value, $pos = 7) { /* * Have to use this instead of scalar type hints. Because we are dealing with arbitrarily large numbers, we are * using the bc math extension. This means that all numbers are actually strings in order to retain precision. * * If we used the int or float type hints, PHP 7 would cast string values (which is good), but that would lose * precision in the cast (which is bad). * * is_numeric will achieve the desired effect without altering the $value */ if (!is_numeric($value)) { throw new \Exception('Cannot pass a non-numeric value.'); } // The number is more than three orders of magnitude from zero if ($value >= 1000 || $value <= -1000) { // Increase the exponent by three and try again return $this->matchBest(MathProvider::divide($value, 1000), $pos + 1); } // The number is a decimal less than one from zero if ($value < 1 && $value > -1 && $value != 0) { // Decrease the exponent by three and try again return $this->matchBest(MathProvider::multiply($value, 1000), $pos - 1); } // If no transformation is needed, we have a special case of the return if ($pos == 7) { return ['value' => $value, 'scale' => '1', 'prefix' => '']; } // Otherwise, we have our answer return ['value' => $value, 'scale' => $this->scale[$this->order[$pos]], 'prefix' => $this->order[$pos]]; }
/** * @param Time $time * @param Acceleration $acceleration * @param UnitComposition|null $unitComposition * * @return Length */ public static function distanceFromConstantAccel(Time $time, Acceleration $acceleration, UnitComposition $unitComposition = null) { if (is_null($unitComposition)) { $unitComposition = new UnitComposition(); } /** @var Length $distance */ $distance = $unitComposition->naiveMultiOpt([$time, $time, $acceleration], []); return $distance->preConvertedSubtract(MathProvider::divide($distance->getValue(), 2)); }
public function preConvertedDivide($value, $precision = 2) { $this->value = MathProvider::divide($this->value, $value, $precision); return $this; }
/** * @param Quantity[] $mults * @param Quantity[] $divides * @param int $precision */ public function naiveMultiOpt(array $mults, array $divides, $precision = 2) { $newUnit = $this->getMultiUnits($mults, $divides); $newVal = 1; foreach ($mults as $quantity) { $newVal = MathProvider::multiply($newVal, $quantity->toNative()->getValue()); } foreach ($divides as $quantity) { $newVal = MathProvider::divide($newVal, $quantity->toNative()->getValue(), $precision); } return $newUnit->preConvertedAdd($newVal); }