I was looking to write linspace
function of NumPy.
As the loops are faster in compiled code, tried writing in C and calling from 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;
}
}
// 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
}
Works OK, but checking the precision of the results for example:
Raku gives:
say raku_linspace(1,3,300000)[200] # gives 1.0013333377777869
While NumPy gives:
import numpy as np
arr = np.linspace(1,3,300000)[200]
print(arr) # gives 1.0013333377777927
Why this difference in precision? My expectation was double
was supposed to maintain precision to 15 decimal digits.
The docs mention double
is mapped to num64
.
Also mentions there are Rakudo specific types where long
, longlong
is mentioned. So what does long double
maps to in Raku. It does not appear to be num64
.
System information:
Windows 10 64 bit
gcc 13.2.0
It looks like the problem is directly in C due to floating point errors rather than calling via nativecall. Because running C gives what Raku is giving.
Yet, is there mechanism in nativecall to handle long double
or long long
preferably with an example?
Also what could be done to make this function with the same precision as NumPy?
I don't know if we should un-tag this question, edit it, or another thing, but anyway: this is not a NativeCall question, nor a Raku question, it's a C question, and the "real" question here is:
The answer to this question is:
numpy
'slinspace
uses a different calculation, probably to use SMP instructions. It does (simplifying a bit):The C equivalent would be:
If you use this implementation, your Raku code will return the same result of
1.0013333377777927
as thenumpy
version.But, as stated in the comments, even
would suffice...
Answering your other question, i.e.,
long long
is supported in rakudo, the type is calledlonglong
orulonglong
in its unsigned variant. Thelong double
type is not supported (yet?) but it should be easy (although not trivial) to add support for it in https://github.com/rakudo/rakudo/blob/main/lib/NativeCall.rakumod (the not trivial part is to complete the tests etc).