在 PDL 上向零舍入

Rounding towards zero on a PDL

我有一个混合值(正值和负值)的 PDL(类型 double)。我想将每个条目四舍五入为零。 所以+1.2变成+1+1.7变成+1-1.2变成-1-1.7变成-1

我考虑过使用 int(),但它不适用于 PDL 类型。

我也可以使用 round(abs($x) - 0.5) * ($x <=> 0),但不确定如何在 PDL 上使用此逻辑。

指针?

PDL::Mathfloorceilrint。所有这些功能都可以正常工作。

因此,像下面这样的东西应该可以工作:

#!/usr/bin/env perl
use warnings;
use strict;

use PDL;

my $pdl = 'PDL'->new(
    [  1,  1.3,  1.9,  2,  2.1,  2.7 ],
    [ -1, -1.3, -1.9, -2, -2.1, -2.7 ]
);

print $pdl;

floor(inplace $pdl->where($pdl >= 0));
ceil (inplace $pdl->where($pdl <  0));

print $pdl;

输出:

[
 [   1  1.3  1.9    2  2.1  2.7]
 [  -1 -1.3 -1.9   -2 -2.1 -2.7]
]

[
 [ 1  1  1  2  2  2]
 [-1 -1 -1 -2 -2 -2]
]

PS:@choroba 的回答在 an ancient MacBook Pro 上使用非线程 perl 5.24 的以下基准测试中似乎 运行 快了大约 20%:

#!/usr/bin/env perl
use warnings;
use strict;

use constant N_ELEMS => $ARGV[0] || 100_000;

use Dumbbench;
use PDL;

sub one_scan {
    my $pdl = 100 * grandom(N_ELEMS);
    $pdl = floor(abs($pdl)) * ($pdl <=> 0);
    return;
}

sub two_scans {
    my $pdl = 100 * grandom(N_ELEMS);
    floor(inplace $pdl->where($pdl >= 0));
    ceil (inplace $pdl->where($pdl <  0));
    return;
}

sub baseline {
    my $pdl = 100 * grandom(N_ELEMS);
    return;
}

my $bench = Dumbbench->new;

$bench->add_instances(
    Dumbbench::Instance::PerlSub->new(code => \&baseline,  name => 'Baseline'),
    Dumbbench::Instance::PerlSub->new(code => \&one_scan,  name => 'One Scan'),
    Dumbbench::Instance::PerlSub->new(code => \&two_scans, name => 'Two Scans'),
);

$bench->run;
$bench->report;

PDL::Mathrint 函数的文档说:

If you want to round half-integers away from zero, try floor(abs($x)+0.5)*($x<=>0).

只需稍作更改即可使其按您想要的方式工作:

#!/usr/bin/perl
use warnings;
use strict;

use PDL;

my $pdl = 'PDL'->new(
    [  1,  1.3,  1.9,  2,  2.1,  2.7 ],
    [ -1, -1.3, -1.9, -2, -2.1, -2.7 ]
);
$pdl = floor(abs($pdl)) * ($pdl <=> 0);
print $pdl;

输出:

[
 [ 1  1  1  2  2  2]
 [-1 -1 -1 -2 -2 -2]
]