/**
  * @param $value
  * @param $fromUnit
  * @param $toUnit
  * @return float
  * @throws UnknownUnit
  * @throws \Granam\Float\Tools\Exceptions\WrongParameterType
  * @throws \Granam\Float\Tools\Exceptions\ValueLostOnCast
  */
 private function getValueInDifferentUnit($value, $fromUnit, $toUnit)
 {
     if ($fromUnit === $toUnit) {
         return ToFloat::toFloat($value);
     }
     if ($fromUnit === self::M && $toUnit === self::KM) {
         return $value / 1000;
     }
     if ($fromUnit === self::KM && $toUnit === self::M) {
         return $value * 1000;
     }
     throw new UnknownUnit('Unknown one or both from ' . ValueDescriber::describe($fromUnit) . ' to ' . ValueDescriber::describe($toUnit) . ' unit(s)');
 }
 /**
  * @param number $number
  *
  * @return int
  */
 public static function ceiledThird($number)
 {
     return self::ceil(ToFloat::toFloat($number) / 3);
 }
 private function normalizeValueForType($value, $type)
 {
     $value = trim($value);
     switch ($type) {
         case self::BOOLEAN:
             return ToBoolean::toBoolean($value, false);
         case self::INTEGER:
             return $value === '' ? false : ToInteger::toInteger($this->normalizeMinus($value));
         case self::POSITIVE_INTEGER:
             return $value === '' ? false : ToInteger::toPositiveInteger($this->normalizeMinus($value));
         case self::NEGATIVE_INTEGER:
             return $value === '' ? false : ToInteger::toNegativeInteger($this->normalizeMinus($value));
         case self::FLOAT:
             return $value === '' ? false : ToFloat::toFloat($this->normalizeMinus($value));
         default:
             // string
             return $value;
     }
 }
 /**
  * @param mixed $value
  * @param bool $strict = true allows only explicit values, not null and empty string
  * @param bool $paranoid Throws exception if some value is lost on cast because of rounding
  * @throws \Granam\Float\Tools\Exceptions\WrongParameterType
  * @throws \Granam\Float\Tools\Exceptions\ValueLostOnCast
  */
 public function __construct($value, $strict = true, $paranoid = false)
 {
     parent::__construct(ToFloat::toFloat($value, $strict, $paranoid));
 }
 /**
  * @param mixed $value
  * @return number
  * @throws \Granam\Float\Tools\Exceptions\WrongParameterType
  * @throws \Granam\Float\Tools\Exceptions\ValueLostOnCast
  */
 protected function normalizeValue($value)
 {
     return ToFloat::toFloat($value);
 }
 /**
  * @param float $searchedValue
  * @param array $closestLower
  * @param array $closestHigher
  *
  * @return int
  */
 private function getBonusClosestTo($searchedValue, array $closestLower, array $closestHigher)
 {
     $searchedValue = ToFloat::toFloat($searchedValue);
     $closerValue = $this->getCloserValue($searchedValue, key($closestLower), key($closestHigher));
     if ($closerValue !== false) {
         if (array_key_exists("{$closerValue}", $closestLower)) {
             $bonuses = $closestLower["{$closerValue}"];
         } else {
             $bonuses = $closestHigher["{$closerValue}"];
         }
         // matched single table-value, maybe with more bonuses, the lowest bonus should be taken
         return min($bonuses);
         // PPH page 11, right column
     } else {
         // both table border-values are equally close to the value, we will choose from bonuses of both borders
         $bonuses = array_merge(count($closestLower) > 0 ? current($closestLower) : [], count($closestHigher) > 0 ? current($closestHigher) : []);
         // matched two table-values, more bonuses for sure, the highest bonus should be taken
         return max($bonuses);
         // PPH page 11, right column
     }
 }