如何有效地将小数点向左移动N位



我有一堆十进制数字(作为字符串),我从API接收。我需要"缩放"它们,即将它们除以10的某个幂。对于整数来说,这似乎是一个简单的任务,但是我有小数,没有保证范围。所以,基本上我需要一个这样的函数:

move_point "12.34" 1; # "1.234"
move_point "12.34" 5; # "0.0001234"

为了避免舍入错误,我宁愿不使用浮点数

这有点冗长,但应该能达到目的:

sub move_point {
    my ($n, $places) = @_;
    die 'negative number of places' if $places < 0;
    return $n if $places == 0;
    my ($i, $f) = split /./, $n;  # split to integer/fractional parts
    $places += length($f);
    $n = sprintf "%0*s", $places+1, $i.$f;  # left pad with enough zeroes
    substr($n, -$places, 0, '.');  # insert the decimal point
    return $n;
}
演示:

my $n = "12.34";
for my $p (0..5) {
    printf "%d  %sn", $p, move_point($n, $p);
} 
0  12.34
1  1.234
2  0.1234
3  0.01234
4  0.001234
5  0.0001234

除非您的数据包含比您显示的数字多得多的值,否则浮点值对于您的目的来说已经足够精确了。Perl可以可靠地再现最多16位的值

use strict;
use warnings 'all';
use feature 'say';
say move_point("12.34", 1); # "1.234"
say move_point("12.34", 5); # "0.0001234"
say move_point("1234", 12);
say move_point("123400", -9);
sub move_point {
    my ($v, $n) = @_;
    my $dp = $v =~ /.([^.]*)z/ ? length $1 : 0;
    $dp += $n;
    $v /= 10**$n;
    sprintf '%.*f', $dp < 0 ? 0 : $dp, $v;
}

输出
1.234
0.0001234
0.000000001234
123400000000000



更新

如果标准浮点数的限制实际上对你来说是不够的,那么核心Math::BigFloat将满足你的需要

这个程序显示一个16位精度的数字,乘以从10E-20到10E20的所有数

use strict;
use warnings 'all';
use feature 'say';
use Math::BigFloat;
for ( -20 .. 20 ) {
    say move_point('1234567890.1234567890', $_);
}
sub move_point {
    my ($v, $n) = @_;
    $v = Math::BigFloat->new($v);
    # Build 10**$n
    my $mul = Math::BigFloat->new(10)->bpow($n);
    # Count new decimal places
    my $dp = $v =~ /.([^.]*)z/ ? length $1 : 0;
    $dp += $n;
    $v->bdiv($mul);
    $v->bfround(-$dp) if $dp >= 0;
    $v->bstr;
}

输出
123456789012345678900000000000
12345678901234567890000000000
1234567890123456789000000000
123456789012345678900000000
12345678901234567890000000
1234567890123456789000000
123456789012345678900000
12345678901234567890000
1234567890123456789000
123456789012345678900
12345678901234567890
1234567890123456789
123456789012345678.9
12345678901234567.89
1234567890123456.789
123456789012345.6789
12345678901234.56789
1234567890123.456789
123456789012.3456789
12345678901.23456789
1234567890.123456789
123456789.0123456789
12345678.90123456789
1234567.890123456789
123456.7890123456789
12345.67890123456789
1234.567890123456789
123.4567890123456789
12.34567890123456789
1.234567890123456789
0.1234567890123456789
0.01234567890123456789
0.001234567890123456789
0.0001234567890123456789
0.00001234567890123456789
0.000001234567890123456789
0.0000001234567890123456789
0.00000001234567890123456789
0.000000001234567890123456789
0.0000000001234567890123456789
0.00000000001234567890123456789

相关内容

  • 没有找到相关文章

最新更新