ТАК,
Проблема
Мой вопрос о тривиальной вещи: как преобразовать числовую строку в простое («родное») представление. Это означает: если числовая строка уже находится в открытом виде, оставьте ее как есть, но если она находится в научной нотации, преобразуйте ее. Образец:
"3" -> "3" "1,5" -> "1,5" "-15.482E-2" -> "-0.15482"
Числовая строка должна быть действительной, а если нет – тогда это не случай преобразования (мы можем, например, возвращать пустую или пустую строку).
Использование регистра
Это необходимо для bcmath
потому что он не может работать с научными поплавками. Таким образом, они должны быть преобразованы в простые строки (заданные здесь ). Поэтому важным следствием этого варианта использования является то, что числовая строка может быть чем-то вроде 1E-300
или 1E+500
. Поскольку мы работаем с bcmath
– это намерение обрабатывать такие вещи.
Мой подход
На данный момент я реализовал это с помощью regex-almighty , например:
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()
?
Важное замечание : я не хочу иметь дело с точностью. Я хочу черный ящик. Передайте что-то числовое – получите строку в качестве вывода. Это все. Не хочу иметь дело ни с чем другим. На самом деле, все мое регулярное выражение – это вычисление длины и точности – так что, конечно, если передать их явно (как параметры, например), мы избавимся от регулярного выражения. Но – нет, это не то, что я хочу.
Поскольку 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
# 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 – желаемое количество дробных цифр.
Для тех, кто просто хочет конвертировать 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;
(Обновлено для использования non-depreciated функций, предложенных 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's 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