SO,
The problem
My question is about trivial thing: how to convert numeric string to it's plain ("native") representation. That means: if numeric string is already in plain view, leave it as it is, but if it is in scientific notation, convert it. Sample:
"3" --> "3" "1.5" --> "1.5" "-15.482E-2" --> "-0.15482"
Numeric string supposed to be valid, and if it's not - then it's not a case for conversion (we're free to return null or empty string, for example).
Use-case
That is needed for bcmath
because it can't work with scientific floats. Thus, they need to be converted to plain strings (asked here). So important consequence from this use-case is that numeric string can be something like 1E-300
or 1E+500
. Since we're working with bcmath
- it's intention is to handle such things.
My approach
For now, I've implemented that with regex-almighty, like:
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;
}
}
The question
I feel that there should be more simple and wise way to do that. How to achieve that in more simple way in PHP? May be some tricky sprintf()
format?
Important note: I don't want to deal with precision. I want black box. Pass something numeric there - got string as output. That's all. Don't want to deal with anything else. In fact, all my regex are about calculating length & precision - so, sure, if pass them explicitly (as parameters, for example) - we cat get rid of regex. But - no, that's not what I want.
To convert float to string, use the toString() method. It represents a value in a string.
We can convert float and double to string using the C++11 std::to_string() function. For the older C++ compilers, we can use std::stringstream objects.
This function is used to convert a floating point number to string. Syntax : gcvt (float value, int ndigits, char * buf); float value : It is the float or double value. int ndigits : It is number of digits.
The most Pythonic way to convert a list of floats fs to a list of strings is to use the one-liner fs = [str(x) for x in fs] . It iterates over all elements in the list fs using list comprehension and converts each list element x to a string value using the str(x) constructor.
Because sprintf()
with "%.f"
has trouble with expressions such as "1e-8"
, some text processing may be required:
function convertFloat($floatAsString)
{
$norm = strval(floatval($floatAsString));
if (($e = strrchr($norm, 'E')) === false) {
return $norm;
}
return number_format($norm, -intval(substr($e, 1)));
}
Tested with:
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
(Updated to use non-depreciated functions as suggested by andufo; I chose explode
, but you could use preg_split
if you wanted. As a side note if anyone still reads down this far, the accepted answer fails - try it with my first and last test case.)
I dug up a little gem from the PHP boards posted by benjcarson in 2002 who noted your exact problem with bcmath and scientific notation
It needed some adjustment (his function didn't set the right scale, and failed on regular decimals, and as pointed out it did not account for the length of decimal places in the scale)
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));
}
As a side note, your original code fails on any numbers smaller than 1E-40
(As do all the current answers using sprintf)
It would have been easier to debug if you posted more of your test cases, but this works for everything you've posted so far
Test cases:
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With