Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert float to plain string representation

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.

like image 311
Alma Do Avatar asked Feb 20 '14 09:02

Alma Do


People also ask

Can you convert a float to a string?

To convert float to string, use the toString() method. It represents a value in a string.

How do I convert a float to a string in C++?

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.

Can we convert float to char?

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.

How do I convert a float list to a string in Python?

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.


2 Answers

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
like image 144
Ja͢ck Avatar answered Oct 03 '22 03:10

Ja͢ck


(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
like image 34
Tim Avatar answered Oct 03 '22 04:10

Tim