Преобразование float в представление простой строки

SO,

Проблема

Мой вопрос о тривиальной вещи: как преобразовать числовую строку в это простое ( "родное" ) представление. Это означает: если числовая строка уже находится в открытом виде, оставьте ее как есть, но если она находится в научной нотации, преобразуйте ее. Пример:

"3"          -->  "3"
"1.5"        -->  "1.5"
"-15.482E-2" -->  "-0.15482"

Числовая строка должна быть действительной, а если нет - тогда это не случай преобразования (мы можем, например, возвращать пустую или пустую строку).

Использование случая

Это необходимо для bcmath, потому что он не может работать с научными поплавками. Таким образом, они должны быть преобразованы в простые строки (задано здесь). Поэтому важным следствием этого варианта использования является то, что числовая строка может быть чем-то вроде 1E-300 или 1E+500. Поскольку мы работаем с bcmath, он намерен обрабатывать такие вещи.

Мой подход

В настоящее время я реализовал это с помощью , например:

function parseFloat($string)
{
   $string = (string)$string;
   if(preg_match('/^[+-]?(\d+|\d+\.\d*)[Ee]([+-]?)(\d+)$/', $string, $matches))
   {
      $precision = false!==($dot=strpos($matches[1], '.'))
                   ?strlen($matches[1])-$dot-1
                   :0;
      $precision = $matches[2]=='-'
                   ?$precision + (int)$matches[3]
                   :$precision - (int)$matches[3];
      return number_format($string, $precision<0?0:$precision, '', '');
   }
   if(preg_match('/^[+-]?(\d+|\d+\.\d+)$/', $string))
   {
      return $string;
   }
}

Вопрос

Я чувствую, что должен быть более простой и мудрый способ сделать это. Как добиться этого более простым способом в PHP? Может быть какой-то сложный sprintf() формат?

Важное замечание: я не хочу иметь дело с точностью. Я хочу черный ящик. Передайте что-то числовое - получите строку в качестве вывода. Все это. Не хочу иметь дело ни с чем другим. На самом деле, все мое регулярное выражение - это вычисление длины и точности - так что, конечно, если передать их явно (как параметры, например), мы избавимся от регулярного выражения. Но - нет, это не то, что я хочу.

Ответ 1

Поскольку sprintf() с "%.f" имеет проблемы с выражениями, такими как "1e-8", может потребоваться некоторая обработка текста:

function convertFloat($floatAsString)
{
    $norm = strval(floatval($floatAsString));

    if (($e = strrchr($norm, 'E')) === false) {
        return $norm;
    }

    return number_format($norm, -intval(substr($e, 1)));
}

Протестировано с помощью

3          3
1.5        1.5
-15.482e-2 -0.15482
1e-8       0.00000001
1e+3       1000
-4.66E-2   -0.0466
3e-3       0.003

Ответ 2

# convert output of used php-math functions like sin in scientific notation to decimal notation
function xpnd($scientific, $precision){ # expand from scientific notation
  if(is_int($scientific)){ #don't convert integers
    return $scientific; 
  }
  return sprintf("%.".$precision."F", $scientific);
}

Где $precision - желаемое количество дробных цифр.

Ответ 3

Для тех, кто просто хочет преобразовать float в строку.

function float_to_string($float)
{
    $string = (string)$float;

    if (preg_match('~\.(\d+)E([+-])?(\d+)~', $string, $matches)) {
        $decimals = $matches[2] === '-' ? strlen($matches[1]) + $matches[3] : 0;
        $string = number_format($float, $decimals,'.','');
    }

    return $string;
}

$float = 0.00000000020001;

echo $float; // 2.0001E-10
echo PHP_EOL;
echo float_to_string($float); // 0.00000000020001
echo PHP_EOL;

$float = 10000000000000000000000;

echo $float; // 1.0E+22
echo PHP_EOL;
echo float_to_string($float); // 10000000000000000000000
echo PHP_EOL;

Ответ 4

(Обновлено для использования неисчерпаемых функций, предложенных andufo, я выбрал explode, но вы могли бы использовать preg_split, если хотите. В качестве побочного примечания, если кто-то все еще читает это до конца, принятый ответ терпит неудачу - попробуйте его с моим первым и последним тестовым случаем.)

Я выкопал немного драгоценного камня из досок PHP, опубликованных benjcarson в 2002 году, который отметил вашу точную проблему с bcmath и научной нотацией

Он нуждался в некоторой корректировке (его функция не установила правильную шкалу и не сработала с правильными десятичными знаками, и, как указано, она не учитывала длину десятичных знаков в шкале)

function exp2int($exp) {
  list($mantissa, $exponent) = explode("e", strtolower($exp));
  if($exponent=='') return $exp;
  list($int, $dec) = explode(".", $mantissa);
  bcscale (abs($exponent-strlen($dec)));
  return bcmul($mantissa, bcpow("10", $exponent));
}

В качестве побочного примечания ваш исходный код выходит из строя на любых номерах меньше 1E-40

(Как и все текущие ответы, используя sprintf)

Было бы легче отладить, если бы вы разместили больше своих тестовых примеров, но это работает для всего, что вы уже опубликовали

Тестовые случаи:

echo exp2int("-1.82235135978667123456789E5"); \\-182235.135978667123456789
echo exp2int("1.1350865232E-60"); \\0.0000000000000000000000000000000000000000000000000000000000011350865232
echo exp2int("-15.482E-2"); \\-0.15482
echo exp2int("1.5"); \\1.5
echo exp2int("3"); \\3
echo exp2int("123.123e10"); \\1231230000000.000 - you mentioned trailing 0 aren't a problem
echo exp2int("123.123e-10"); \\0.0000000123123
echo exp2int("123456789E-9"); \\0.123456789
echo exp2int("12345.6789E-5"); \\0.123456789
echo exp2int("1E-300"); \\0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001