将浮点数转换为普通字符串表示形式

17

所以,

问题

我的问题是关于一个琐碎的事情:如何将数字字符串转换为它的纯文本表示。也就是说:如果数字字符串已经是纯文本视图,则保持不变,但如果它是科学计数法,则进行转换。示例:

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

数字字符串应该是有效的,如果不是,则不能进行转换(例如,我们可以返回null或空字符串)。

用例

这对于这里提问)。因此,这个用例的重要结论是数字字符串可能是诸如1E-3001E+500之类的东西。由于我们正在使用

我的方法

目前,我使用了实现了这个方法,如下所示:

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()格式吗?

重要提示:我不想处理精度,我需要的是黑盒子。传递一个数值 - 得到字符串输出。就这样,不想处理其他任何事情。实际上,我的所有正则表达式都是关于计算长度和精度的 - 所以,如果将它们显式地传递(例如作为参数),我们可以摆脱正则表达式。但是 - 不,那不是我想要的。


这个:http://juliankessel.de/2012/07/11/php-bcmath-and-the-scientific-notation-issue/? - user1299518
我的回答应该能解决你的问题。 - hd1
@vlzvl 在 "1E-8" 上失败了,就像其他任何东西一样。 - Alma Do
(我正在努力工作,发现它很具有挑战性) - user1299518
事实是 - 我已经有了一个工作解决方案(或多或少,没有经过适当的测试),我想要更简单的解决方案。但如果那将是困难或复杂的代码,我更喜欢现在的解决方案。 - Alma Do
显示剩余4条评论
4个回答

13

由于 sprintf() 在处理表达式例如 "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

不适用于表示无法使用PHP浮点数表示的数字字符串。例如:"1E-500","0.100000000000009"。 - Oktokolo
@Ali 我认为所有的sprintf答案都有这个问题,在底部看到我的答案,虽然我没有在理论上测试超过1E-300,但-500应该可以工作。 - Tim

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'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

将失败:echo (float)"1E-8";//1.0E-8 - Alma Do
例如在 -4.66E-2 上失败(你的结果将是 -0.04 - Alma Do
只需要调整小数位的长度。 - Tim
split和spliti已经被弃用。 - Andres SK
@andufo 谢谢,已更新答案。虽然我永远不会知道为什么这不是被接受的答案 - 它是唯一完全可行的解决方案(包括对 OP 自己的解决方案)。 - Tim
@Tim同意,楼主应该收到你的回答更新。感谢你的回答! - Andres SK

3

对于那些只想将浮点数转换为字符串的人。

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;

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 是所需小数位数。


1
不,我不想处理精度。我想要黑盒子。在那里传递一些数字 - 得到字符串作为输出。就这样。不想处理其他任何东西。 - Alma Do
函数存在问题,会截断类似于0.0000123456的浮点数。 - pablorsk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接