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

Posted

技术标签:

【中文标题】在 bash/perl 中将科学记数法转换为十进制(不是整数)【英文标题】:convert scientific notation to decimal (not integer) in bash/perl 【发布时间】:2014-04-10 01:01:29 【问题描述】:

我有一个制表符分隔的文件,其中包含几列(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 语句,它适用于该列中的所有数字。因此,我无法将其中的信息转换为我的具体案例。有人可以帮我解决这个问题吗?

谢谢!

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

以下演示了一个用于执行此操作的函数。它接受一个可能是科学计数法的数字,并返回十进制表示。适用于正指数和负指数:

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

【讨论】:

【参考方案3】:

如果您以简单的方式执行此操作,即解析为浮点数,然后使用 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)并将其写出。

【讨论】:

以上是关于在 bash/perl 中将科学记数法转换为十进制(不是整数)的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中将科学计数法转换为十进制

在python中将数组的元素从科学计数法转换为十进制计数法

如何在 Python 中将负指数数转换为十进制数?

在没有科学计数法的 SQL Server 中将 float 转换为 varchar

在 hive 中将 bigint 转换为科学数

在pandas python中将指数或科学数转换为整数