在bash / perl中将科学记数法转换为十进制(非整数)

时间:2014-04-10 01:01:30

标签: perl bash decimal scientific-notation

我有一个带有多个列(9列)的制表符分隔文件,如下所示:

1:21468 1   21468   2.8628817609765984  0.09640845515631684     0.05034710996552612     1.0     0.012377712911711025    54.0

然而,在第5栏中,我有时会有科学数字:

    8.159959468796783E-4
    8.465114165595303E-4
    8.703354859736187E-5
    9.05132870067004E-4

我需要在第5列中以十进制表示法包含所有数字。从上面的例子:

    0.0008159959468796783
    0.0008465114165595303
    0.00008703354859736187
    0.000905132870067004

我需要更改这些数字而不更改第5列中的其余数字或文件的其余部分。

我知道Convert scientific notation to decimal in multiple fields中有类似的帖子。但是在这种情况下,有一个if语句与字段中存在的数字类型无关,而且该字段适用于该列中的所有数字。因此,我无法将信息转换为我的具体案例。有人可以帮我解决这个问题吗?

谢谢!

3 个答案:

答案 0 :(得分:1)

将perl中的科学符号数转换为常规符号数最简单(也是最快)的方法:

my $num = '0.12345678E5';
$num *= 1;
print "$num\n";

答案 1 :(得分:0)

如果你这样做的方法很简单,通过解析为浮点然后使用printf强制它以小数形式打印,你可能会得到稍微不同的结果,因为你处于双精度可用的精度上限 - 精确格式。

你应该做的是将每一行拆分成字段,然后用这样的东西检查字段5.

($u,$d,$exp) = $field[5] =~ /(\d)\.(\d+)[Ee]([-+]\d+)/

如果字段[5]是科学记数法,这将给你

$u    the digit before the decimal
$d    the digits after the decimal
$exp  the exponent

(如果不是你会得到未定义的值,可以跳过重新格式化步骤)

使用该信息,您可以使用正确的前导零和小数点数重新组合数字。如果指数为正数,则必须重新组合数字,然后在正确的位置插入小数点。

按照您希望的方式重新格式化值后,重新组合整行(例如,使用join)并将其写出来。

答案 2 :(得分:0)

正如吉姆已经提出的,一种方法是简单地将数字视为字符串并自己进行翻译。这样您就可以完全保持有效数字。

以下演示了执行此操作的功能。它接受一个可能用科学记数法表示的数字,并返回十进制表示。与正指数和负指数一起使用:

use warnings;
use strict;

while (<DATA>) {
    my ($num, $expected) = split;
    my $dec = sn_to_dec($num);
    print $dec . ' - ' . ($dec eq $expected ? 'good' : 'bad') . "\n";
}

sub sn_to_dec {
    my $num = shift;

    if ($num =~ /^([+-]?)(\d*)(\.?)(\d*)[Ee]([-+]?\d+)$/) {
        my ($sign, $int, $period, $dec, $exp) = ($1, $2, $3, $4, $5);

        if ($exp < 0) {
            my $len = 1 - $exp;
            $int = ('0' x ($len - length $int)) . $int if $len > length $int;
            substr $int, $exp, 0, '.';
            return $sign.$int.$dec;

        } elsif ($exp > 0) {
            $dec .= '0' x ($exp - length $dec) if $exp > length $dec;
            substr $dec, $exp, 0, '.' if $exp < length $dec;
            return $sign.$int.$dec;

        } else {
            return $sign.$int.$period.$dec;
        }
    }

    return $num;
}


__DATA__
8.159959468796783E-4    0.0008159959468796783
8.465114165595303E-4    0.0008465114165595303
8.703354859736187E-5    0.00008703354859736187
9.05132870067004E-4     0.000905132870067004
9.05132870067004E+4     90513.2870067004
9.05132870067004E+16    90513287006700400
9.05132870067004E+0     9.05132870067004