Sum*_*nal 5 c rakudo nativecall raku
我正在寻找编写linspaceNumPy 的函数。
由于编译代码中的循环速度更快,因此尝试用 C 编写并从 Raku 调用。
// C code
#include <stdio.h>
#ifdef _WIN32
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT extern // if c++ code, requires extern "C"
#endif
DLLEXPORT void c_linspace(double start, double step, int num, double* vals) {
for (int i = 0; i < num; i++)
{
vals[i] = start;
start += step;
}
}
Run Code Online (Sandbox Code Playgroud)
// Raku code
sub c_linspace(num64, num64, int32, CArray[num64])
is native( MYDYN) { * };
sub raku_linspace($start, $end, $num, :$endpoint = True, :$retstep = False) {
my $step = $endpoint ?? ($end - $start)/($num - 1) !! ($end-$start)/($num);
my $vals = CArray[num64].allocate($num);
c_linspace($start.Num, $step.Num, $num.Int, $vals);
$retstep ?? ($vals.list, $step) !! $vals.list
}
Run Code Online (Sandbox Code Playgroud)
工作正常,但检查结果的精度,例如:
拉库给出:
say raku_linspace(1,3,300000)[200] # gives 1.0013333377777869
Run Code Online (Sandbox Code Playgroud)
虽然 NumPy 给出:
// Raku code
sub c_linspace(num64, num64, int32, CArray[num64])
is native( MYDYN) { * };
sub raku_linspace($start, $end, $num, :$endpoint = True, :$retstep = False) {
my $step = $endpoint ?? ($end - $start)/($num - 1) !! ($end-$start)/($num);
my $vals = CArray[num64].allocate($num);
c_linspace($start.Num, $step.Num, $num.Int, $vals);
$retstep ?? ($vals.list, $step) !! $vals.list
}
Run Code Online (Sandbox Code Playgroud)
为什么精度会出现这种差异?我的期望是double保持精确到 15 位小数。
文档提到的内容double映射到num64.
还提到了Rakudo 特定类型long,其中longlong提到了 , 。那么 Raku 中映射到什么long double。看来不是num64。
系统信息:
Windows 10 64位
gcc 13.2.0
由于浮点错误,问题似乎直接出现在 C 中,而不是通过 nativecall 调用。因为运行 C 可以提供 Raku 所提供的东西。
然而,nativecall中有没有机制可以处理long double或者long long最好有一个例子?
另外,如何才能使该函数具有与 NumPy 相同的精度?
我不知道我们是否应该取消标记这个问题,编辑它,或者其他事情,但无论如何:这不是一个 NativeCall 问题,也不是一个 Raku 问题,它是一个 C 问题,这里的“真正”问题是:
为什么我
c_linspace生成的结果不同numpy.linspace?
这个问题的答案是:numpy使用linspace了不同的计算方式,可能是使用SMP指令。它确实(稍微简化):
y = _nx.arange(0, num, dtype=dt).reshape((-1,) + (1,) * ndim(delta))
step = delta / div
y *= step
y += start
Run Code Online (Sandbox Code Playgroud)
C 等价物是:
void c_linspace(double start, double step, int num, double* vals) {
for (int i = 0; i < num; i++)
vals[i] = (double) i;
for (int i = 0; i < num; i++)
vals[i] *= step;
for (int i = 0; i < num; i++)
vals[i] += start;
}
Run Code Online (Sandbox Code Playgroud)
1.0013333377777927如果您使用此实现,您的 Raku 代码将返回与版本相同的结果numpy。
但是,正如评论中所说,即使
void c_linspace(double start, double step, int num, double* vals) {
for (int i = 0; i < num; i++)
vals[i] = i * step + start;
}
Run Code Online (Sandbox Code Playgroud)
就足够了...
回答你的另一个问题,即
然而,nativecall中有没有机制可以处理
long double或者long long最好有一个例子?
long long在 rakudo 中受支持,该类型被称为longlong或其ulonglong无符号变体。该类型尚不支持(还?),但在https://github.com/rakudo/rakudo/blob/main/lib/NativeCall.rakumodlong double中添加对它的支持应该很容易(尽管不是微不足道的)(这不是微不足道的)部分是完成测试等)。
| 归档时间: |
|
| 查看次数: |
181 次 |
| 最近记录: |