我有一堆十进制数字(作为字符串),我从API收到。我需要' unscale'它们,即将它们除以10的幂。对于整数来说,这似乎是一个简单的task,但是我的小数没有保证范围。所以,基本上我需要一个像这样工作的函数:
move_point "12.34" 1; # "1.234"
move_point "12.34" 5; # "0.0001234"
我宁愿不使用浮动来避免任何舍入错误。
答案 0 :(得分:7)
这有点冗长,但应该这样做:
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 %s\n", $p, move_point($n, $p);
}
0 12.34
1 1.234
2 0.1234
3 0.01234
4 0.001234
5 0.0001234
答案 1 :(得分:1)
除非您的数据包含的数字明显多于您显示的数字,否则浮点值的精确度足以达到您的目的。 Perl可以可靠地再现最多16位数值
cusparseDestroyMatDescr
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;
}
如果标准浮点数的限制对您来说实际上是不充分的,那么核心Math::BigFloat
将满足您的需求
此程序显示精确度为16位的数字,乘以10E-20到10E20的所有内容
1.234
0.0001234
0.000000001234
123400000000000
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;
}