将float转换为纯字符串表示形式

时间:2014-02-20 09:00:09

标签: php floating-point format

SO,

问题

我的问题是关于微不足道的事情:如何将数字字符串转换为它的普通(“原生”)表示。这意味着:如果数字字符串已经处于普通视图中,请保持原样,但如果它是科学记数法,则转换它。样品:

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

数字字符串应该是有效的,如果不是 - 则不是转换的情况(例如,我们可以自由地返回null或空字符串)。

用例

bcmath需要这样做,因为它无法与科学浮点数一起使用。因此,他们需要转换为普通字符串(问here)。这个用例的重要结果是数字字符串可以是1E-3001E+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()格式?

重要提示:我不想处理精确度。我想要黑盒子。在那里传递数字 - 得到字符串作为输出。就这样。不想处理任何其他事情。事实上,我所有的正则表达式都是关于计算长度&amp;精确 - 所以,当然,如果明确地传递它们(例如参数) - 我们就会摆脱正则表达式。但是 - 不,那不是我想要的。

4 个答案:

答案 0 :(得分:9)

由于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

答案 1 :(得分: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是所需的小数位数。

答案 2 :(得分:2)

对于那些只想将float转换为string的人。

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;

答案 3 :(得分:0)

(更新为使用andufo建议使用的非折旧函数;我选择了explode,但如果您愿意,可以使用preg_split。如果有人仍然读到这一点,请注意,接受的答案失败了 - 试试我的第一个也是最后一个测试用例。)

我在2002年由benjcarson发布的PHP板上挖出了一个小宝石,他注意到了你对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