Почему perl имеет ошибки с плавающей запятой? [Дубликат]

У меня есть этот код:

sub range {
        my ($start, $end, $step) = @_;

        if($step == 0) {
                die("Step size cannot be 0!")
        }
        if($start > $end) {
                ($start, $end) = ($end, $start);
        }
        my @range = ();
        for (my $i = $start; $i <= $end; $i += $step) {
                push @range, $i;
        }

        return @range;
}

При запуске

my @range = range(-3, -2.7, 0.01);

Я получаю этот список:

...
$VAR23 = '-2.78';
$VAR24 = '-2.77';
$VAR25 = '-2.76000000000001';
$VAR26 = '-2.75000000000001';
$VAR27 = '-2.74000000000001';
$VAR28 = '-2.73000000000001';
$VAR29 = '-2.72000000000001';
$VAR30 = '-2.71000000000001';
$VAR31 = '-2.70000000000001';

Почему это происходит?

У меня есть perl v5.24.1 на машине 4.9.0-7-amd64 # 1 SMP Debian 4.9.107-1. Добавление биснума использования; модуль не меняет того факта, что вычисляемые переменные неверны.

Кроме того, это не происходит, когда вы делаете sometheng как «-2.7 - 0.01».

perl,floating-point,perl,

1

Ответов: 1


2

Почему это происходит?

Смотрите, что каждый программист должен знать о арифметике с плавающей точкой .

Добавление модуля субдиапазона не изменяет того факта, что вычисляемые переменные неверны.

Я предполагаю, что вы добавили bignumвнутреннюю часть sub, но поскольку bignumона ограничена, это не будет автоматически влиять на переданные в нее переменные range. Таким образом, либо bignumнеобходимо действовать везде, где определены литералы, в которые вы проходите sub, и они будут работать, или вы могли бы просто обновить переменные сами по себе, как в:use Math::BigRat; sub range { my $start = Math::BigRat->new(shift); my $end = Math::BigRat->new(shift); my $step = Math::BigRat->new(shift); ... return map {$_->numify} @range; }

Math::BigRat

Однако , используя Math::BigFloat, bignumи т.д. ( в том числе и с помощью bignumи связанными с ними прагм) объекты везде замедлит код, так что это может быть слишком много. Чтобы объекты использовались только в пределах subвышеперечисленного, я переориентирую объекты обратно на обычные скаляры numify, но это необязательно в зависимости от ваших требований к производительности.

В зависимости от того, какую точность вам действительно нужно, вы также можете округлить свои номера, например, sprintf("%.2f",$i)( например, как пример:) for (my $i = $start; $i <= $end; $i = 0+sprintf("%.2f",$i+$step) ). Другая возможность, о которой упоминал @ikegami в комментарии, заключалась бы в работе с целыми числами и делении последнего, как, например map { $_/100 } -300 .. -270.

Perl, с плавающей точкой, Perl,
Похожие вопросы