System environment: 64 bit Windows 7 Ultimate; Active State Perl revision 5 version 24 subversion 3; Build 2404 [404865] compiled Dec 11 2017 11:09:26.
I’m trying to write a perl script that calls the function declared as:
extern "C" POLYFITGSL_API int PolyFit(int numPts, const double* xVals, const double* yVals, int fitOrder, double* coef, double* fitVals, double* rSquared);
The first four parameters are inputs to PolyFit and the last three are outputs.
With pointers allocated in a C program, it is called in this form:
coef = (double*)malloc((fitOrder + 1) * sizeof(double));
estYVals = (double*)malloc(n * sizeof(double));
rSquared = (double*)malloc(sizeof(double));
resFit = PolyFit(n, xVals, yVals, fitOrder, coef, estYVals, rSquared);
The DLL exports :DSL Viewer display
Attempts using the parameter list option have not been successful. Further, https://metacpan.org/pod/Win32::API#1 recommends importing by prototype. However I don’t know how to write it and can’t find an example.
Using the parameter list option in the code fragment below, except for the two integers, all are defined as pointers, and for the outputs the referenced arrays and the final float have been pre-defined and populated with zeros.
# This assumes that the integers are 4 bytes wide and all others are 8:
$returnbuf = " " x 48;
$parmsbuf = " " x 48;
my $PolyFit = Win32::API::More->new('D:/prjct/model/code/SRS1/binaries/PolyFitGSL','PolyFit','PNP','N');
die $! unless defined $PolyFit;
# no error is produced here
$parmsbuf = pack('iNNiNNN', $numvals, $xValsptr, $yValsptr, $fitorder, $coeffsptr, $fitValsptr, $rSquaredptr);
# display the parameters
#outref = unpack('iNNiNNN', $parmsbuf);
print ("The unpacked calling buffer: #outref \n");
$returncode = $PolyFit ->Call($parmsbuf, 3, $returnbuf);
# the return value is 52
$error = Win32::GetLastError();
if ($error) {print("function call failed: $^E \n")};
#returnvals = unpack('iNNiNNN', $returnbuf);
print ("Return values: #returnvals \n");
On execution, this produces:
The unpacked calling buffer: 600 58497768 58498512 3 58497816 58497840 58489400
Return values: 538976288 538976288 538976288 538976288 538976288 538976288 538976288
The return value of the call is 52 under all conditions tested.
The output arrays and scalar referenced by $coeffsptr, $fitValsptr, and $rSquaredptr remain in their initialized state.
The input buffer’s values look right to me and the pointer values look like reasonable locations in perl’s address space.
No execution errors are detected but the returned values clearly aren’t valid. I’m making mistakes here but it’s not obvious to me how to resolve them.
There is disagreement between authorities on the parameter type identifiers. https://metacpan.org/pod/Win32::API#1 says that a double float is specified with a D but the pack function rejects it as an invalid type.
I’m relying on this source for specifying the sizes of the variables the GSL PolyFit function is expecting: https://www.ibm.com/support/knowledgecenter/en/SSFKSJ_9.0.0/com.ibm.mq.ref.dev.doc/q104610_.htm
If I should be importing by prototype instead, an example of how to write the import and call statements would be of great value. I’m not a developer, I’m just trying to get some science done and a fast polynomial fitting routine is critical. The GSL PolyFit function can fit a third degree polynomial to 600 data points in about 350 microseconds on this 3.5 GHz, 7 year old computer.
Thanks very much for helping;
Lots of problems.
PNP is obviously wrong for a function with 7 arguments.
Similarly, what's up with ->Call($parmsbuf, 3, $returnbuf)?
N is not the correct type of the return value.
Win32::API uses the stdcall calling convention by default, but the function appears to use the cdecl calling convention.
You can use the following: (Notes follow)
use feature qw( state );
use Config qw( %Config );
use Win32::API qw( );
use constant PTR_SIZE => $Config{ptrsize};
use constant PTR_PACK_FORMAT =>
PTR_SIZE == 8 ? 'Q'
: PTR_SIZE == 4 ? 'L'
: die("Unrecognized ptrsize\n");
use constant PTR_WIN32API_TYPE =>
PTR_SIZE == 8 ? 'DWORD64'
: PTR_SIZE == 4 ? 'DWORD32'
: die("Unrecognized ptrsize\n");
Win32::API::Type->typedef('uintptr_t' => PTR_WIN32API_TYPE);
my $dll = 'D:/prjct/model/code/SRS1/binaries/PolyFitGSL';
sub get_buffer_addr { unpack(PTR_PACK_FORMAT, pack('P', $_[0])) }
sub poly_fit {
my ($vals, $fit_order) = #_;
state $PolyFit;
if (!$PolyFit) {
my $adjusted_proto = '
int __cdecl PolyFit(
int numPts,
uintptr_t xVals,
uintptr_t yVals,
int fitOrder,
uintptr_t coef,
uintptr_t fitVals,
uintptr_t rSquared
)
';
$PolyFit = Win32::API::More->new($dll, $adjusted_proto)
or die("Can't link to PolyFit: $^E\n");
}
my $n = #$vals;
my $x_vals = pack("d$n", map $_->[0], #$vals);
my $y_vals = pack("d$n", map $_->[1], #$vals);
my $coef = pack('d'.( $fit_order + 1 ), ( 0 )x( $fit_order + 1 ));
my $fit_vals = pack("d$n", ( 0 )x( $n ));
my $r_squared = pack('d', 0);
my $rv = $PolyFit->Call(
$n,
get_buffer_addr($x_vals),
get_buffer_addr($y_vals),
$fit_order,
get_buffer_addr($coef),
get_buffer_addr($fit_vals),
get_buffer_addr($r_squared),
);
# I'm assuming the return value indicates whether the call was successful or not?
return if !$rv;
return (
[ unpack('d'.( $fit_order + 1 ), $coef) ],
[ unpack("d$n", $fit_vals) ],
[ unpack('d', $r_squared) ],
);
}
my ($coef, $fit_vals, $r_squared) = poly_fit(
[ [ $x1, $y1 ], [ $x2, $y2 ], [ $x3, $y3 ], ... ],
$fit_order,
)
or die("Error");
Or, if you prefer to use parallel arrays for the inputs,
sub poly_fit {
my ($x_vals, $y_vals, $fit_order) = #_;
#$x_vals == #$y_vals
or croak("Mismatch in the number of X vals and Y vals");
...
my $n = #$x_vals;
my $x_vals = pack("d$n", #$x_vals);
my $y_vals = pack("d$n", #$y_vals);
...
}
my ($coef, $fit_vals, $r_squared) = poly_fit(
[ $x1, $x2, $x3, ... ],
[ $y1, $y2, $y3, ... ],
$fit_order,
)
or die("Error");
Notes
When I wrote the above code, I thought specifying a calling convention other than __stdcall required switching to the prototype syntax of Win32:API. But I was mistaken. I could have used the following:
use constant PTR_WIN32API_TYPE =>
PTR_SIZE == 8 ? 'Q'
: PTR_SIZE == 4 ? 'N'
: die("Unrecognized ptrsize\n");
$PolyFit = Win32::API::More->new(
$dll, 'PolyFit', 'PPiPPP' =~ s/P/PTR_WIN32API_TYPE/ger, 'i', '__cdecl')
Win32::API's prototype parser is very lame. When it sees const double* xVals, it sees const foo! And double* xVals is no better cause it just sees double foo;.
We could use LPDOUBLE instead of double*, but that doesn't buy us much. Regardless of whether the prototype syntax is used or not, Win32::API expects us to provide a single number, not an array.
So we handle the pointers ourselves. By telling Win32::API that the pointer parameters are integers of the appropriate size (DWORD32 or DWORD64 depending on the whether we're using 32-bit or 64-bit pointers), we can pass a pointer without any interpretation by Win32::API.
What follows is my entire test.
a.h
#ifndef A_H
#define A_H
#ifdef __cplusplus
extern "C" {
#endif
#ifdef POLYFITGSL_EXPORTS
#define POLYFITGSL_API __declspec(dllexport)
#else
#define POLYFITGSL_API __declspec(dllimport)
#endif
POLYFITGSL_API int PolyFit(int numPts, const double* xVals, const double* yVals, int fitOrder, double* coef, double* fitVals, double* rSquared);
#ifdef __cplusplus
}
#endif
#endif // A_H
a.c
#include <stdio.h>
#include "a.h"
POLYFITGSL_API int PolyFit(int numPts, const double* xVals, const double* yVals, int fitOrder, double* coef, double* fitVals, double* rSquared) {
// %I64u is MS-specific and shoulnd't be hardcoded.
printf("[C] sizeof(int): %I64u\n", sizeof(int));
printf("[C] sizeof(double*): %I64u\n", sizeof(double*));
printf("[C] numPts: %d\n", numPts);
printf("[C] xVals: %p\n", (void*)xVals);
printf("[C] yVals: %p\n", (void*)yVals);
printf("[C] fitOrder: %d\n", fitOrder);
printf("[C] coef: %p\n", (void*)coef);
printf("[C] fitVals: %p\n", (void*)fitVals);
printf("[C] rSquared: %p\n", (void*)rSquared);
for (int i=0; i<numPts; ++i) {
printf("[C] xVals[%d]: %f\n", i, xVals[i]);
printf("[C] yVals[%d]: %f\n", i, yVals[i]);
}
for (int i=0; i<fitOrder+1; ++i)
coef[i] = (i+1)/10.0;
for (int i=0; i<numPts; ++i)
fitVals[i] = (i+1)/100.0;
*rSquared = 3.14;
return 1;
}
a.pl
#!perl
use 5.014;
use warnings;
use Config qw( %Config );
use Data::Dumper qw( Dumper );
use Devel::Peek qw( Dump );
use Win32::API qw( );
use constant PTR_SIZE => $Config{ptrsize};
use constant PTR_PACK_FORMAT =>
PTR_SIZE == 8 ? 'Q'
: PTR_SIZE == 4 ? 'L'
: die("Unrecognized ptrsize\n");
use constant PTR_WIN32API_TYPE =>
PTR_SIZE == 8 ? 'DWORD64'
: PTR_SIZE == 4 ? 'DWORD32'
: die("Unrecognized ptrsize\n");
Win32::API::Type->typedef('uintptr_t' => PTR_WIN32API_TYPE);
my $dll = $0 =~ s/\.pl\z/.dll/r;
sub get_buffer_addr { unpack(PTR_PACK_FORMAT, pack('P', $_[0])) }
sub poly_fit {
my ($vals, $fit_order) = #_;
state $PolyFit;
if (!$PolyFit) {
my $adjusted_proto = '
int __cdecl PolyFit(
int numPts,
uintptr_t xVals,
uintptr_t yVals,
int fitOrder,
uintptr_t coef,
uintptr_t fitVals,
uintptr_t rSquared
)
';
$PolyFit = Win32::API::More->new($dll, $adjusted_proto)
or die("Can't link to PolyFit: $^E\n");
}
my $n = #$vals;
my $x_vals = pack("d$n", map $_->[0], #$vals);
my $y_vals = pack("d$n", map $_->[1], #$vals);
my $coef = pack('d'.( $fit_order + 1 ), ( 0 )x( $fit_order + 1 ));
my $fit_vals = pack("d$n", ( 0 )x( $n ));
my $r_squared = pack('d', 0);
printf("[Perl] sizeof(double*): %u\n", PTR_SIZE);
printf("[Perl] numPts: %d\n", $n);
printf("[Perl] xVals: %016X\n", get_buffer_addr($x_vals));
printf("[Perl] yVals: %016X\n", get_buffer_addr($y_vals));
printf("[Perl] fitOrder: %d\n", $fit_order);
printf("[Perl] coef: %016X\n", get_buffer_addr($coef));
printf("[Perl] fitVals: %016X\n", get_buffer_addr($fit_vals));
printf("[Perl] rSquared: %016X\n", get_buffer_addr($r_squared));
Dump($coef);
my $rv = $PolyFit->Call(
$n,
get_buffer_addr($x_vals),
get_buffer_addr($y_vals),
$fit_order,
get_buffer_addr($coef),
get_buffer_addr($fit_vals),
get_buffer_addr($r_squared),
);
Dump($coef);
# I'm assuming the return value indicates whether the call was successful or not?
return if !$rv;
return (
[ unpack('d'.( $fit_order + 1 ), $coef) ],
[ unpack("d$n", $fit_vals) ],
[ unpack('d', $r_squared) ],
);
}
my $fit_order = 4;
my ($coef, $fit_vals, $r_squared) = poly_fit(
[ [ 14.5, 24.5 ], [ 15.5, 25.5 ], [ 15.5, 25.5 ] ],
$fit_order,
)
or die("Error");
print(Dumper($coef, $fit_vals, $r_squared));
a.bat
(This is using mingw installed by Strawberry Perl.)
#echo off
gcc -Wall -Wextra -pedantic -c -DPOLYFITGSL_EXPORTS a.c & gcc -shared -o a.dll a.o -Wl,--out-implib,liba.a & perl a.pl
Related
I've checked other similar posts but I think I just need a second set of eyes. This file is for the lex Unix utility.
I created a makefile and this is the error I receive:
gcc -g -c lex.yy.c
cxref.l:57: error: expected ‘;’, ‘,’ or ‘)’ before numeric constant
make: *** [lex.yy.o] Error 1
Line 57 is just inside the void inserID() function near the top.
Here is the code:
%{
#include <stdio.h>
#include <string.h>
char identifier[1000][82];
char linesFound[100][100];
void insertId(char*, int);
int i = 0;
int lineNum = 1;
%}
%x comment
%s str
%%
"/*" BEGIN(comment);
<comment>[^*\n]* /* eat anything that's not a '*' */
<comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
<comment>\n ++lineNum;
<comment>"*"+"/" BEGIN(INITIAL);
"\n" ++lineNum;
auto ;
break ;
case ;
char ;
continue ;
default ;
do ;
double ;
else ;
extern ;
float ;
for ;
goto ;
if ;
int ;
long ;
register ;
return ;
short ;
sizeof ;
static ;
struct ;
switch ;
typedef ;
union ;
unsigned ;
void ;
while ;
[*]?[a-zA-Z][a-zA-Z0-9_]* insertId(yytext, lineNum);
[^a-zA-Z0-9_]+ ;
[0-9]+ ;
%%
void insertId(char* str, int nLine)
{
char num[2];
sprintf ( num, "%d", nLine);
int iter;
for(iter = 0; iter <= i; iter++)
{
if ( strcmp(identifier[iter], str) == 0 )
{
strcat( linesFound[iter], ", " );
strcat( linesFound[iter], num );
return;
}
}
strcpy( identifier[i], str );
strcat( identifier[i], ": " );
strcpy( linesFound[i], num );
i++;
}
Your problem is:
%s str
There is a reason that it's normal to write condition names in CAPS: it makes them look like macros, which is exactly what they are.
So
void insertId(char* str, int nLine)
get macro expanded to something like:
void insertId(char* 2, int nLine)
and the compiler complains that 2 is not really expected at that point in the declaration.
I'm trying to draw a line, broken out into segments dependent on values. For example, if there are 5 fields, and all 5 fields were true, then my Line would look like
-----
If say the first and last fields were true, and everything else would be false, then it would be
- -
I thought I could do this with a bit mask of some sort. First of all, I've never done a bit mask before, but I think I've seen them here and there. I was wondering how I could go about this, and use enumerations instead of 1/0 for readability. As far as I can see from my data, I would only need values of either 1 or 0 for the different properties. However, it would be good to know how to have one of the values be a three level or higher enumeration for future reference. Thanks!
Trying to do something like:
enum CodingRegions {
Coding = 0x01,
NonCoding = 0x02
};
enum Substitution {
Synonymous = 0x04,
NonSynonmous = 0x05
};
Then based on the value of the object, I could do
bitmask???? = object.CodingRegion | object.Substitution;
Then later, check the value of the bitmask somehow, and then draw the line accordingly based on what the values are.
Not sure exactly what your requirements are, but here is one way it might be written in C:
#include <stdio.h>
typedef enum MyField_ {
hasWombat = 1 << 0,
hasTrinket = 1 << 1,
hasTinRoof = 1 << 2,
hasThreeWheels = 1 << 3,
myFieldEnd = 1 << 4,
} MyField;
void printMyField(MyField data) {
MyField field = 1;
while (field != myFieldEnd) {
printf("%c", data & field ? '-' : ' ');
field <<= 1;
}
printf("\n");
}
int main() {
MyField data = hasTrinket | hasThreeWheels;
printMyField(data);
data |= hasWombat; // set a field
data &= ~hasTrinket; // clear a field
printMyField(data);
return 0;
}
Not sure this is what you want, but:
// assumed Coding/NonCoding, Synonomous/NonSynonymous are opposites of each other. If not, add more bit fields
enum CodingRegions
{
Coding = 1 << 0
} ;
enum Substitution
{
Synonymous = 1 << 1
}
void PrintBitmask( NSUInteger bitmask )
{
printf( "%s", bitmask & Coding != 0 ? "-" : " " ) ;
printf( "%s", bitmask & Substitution != 0 ? "-" : " " ) ;
printf( "\n" ) ;
}
Your PrintBitmask() could also look like this:
void PrintBitmask( NSUInteger bitmask )
{
printf( "%s", bitmask & Coding != 0 ? "Coding" : "Noncoding" ) ;
printf( "|" ) ;
printf( "%s", bitmask & Substitution != 0 ? "Synonymous-" : "Nonsynonymous" ) ;
printf( "\n" ) ;
}
/* I prefer macros over enums (at least for something this simple) */
#define SPACE 0x0
#define DASH 0x1
/* input fields */
int fields[5] = {DASH,SPACE,SPACE,SPACE,DASH};
/* create bitmask */
for (int i=0; i<5; i++) {
mask |= (fields[i] << i);
}
/* interpret bitmask and print the line */
for (int i=0; i<5; i++) {
if (mask & (fields[i] << i)) {
printf("%c", '-');
} else {
printf("%c", ' ');
}
}
I have a piece of code like this (Perl file):
print "9 + 16 = ", add(9, 16), "\n";
print "9 - 16 = ", subtract(9, 16), "\n";
C code also,
#include<stdio.h>
main ()
{
int x = 9;
int y = 16;
printf(" add() is %d\n", add(x,y));
printf(" sub() is %d\n", subtract(x,y));
// return 0;
}
int add(int x, int y)
{
return x + y;
}
int subtract(int x, int y)
{
return x - y;
}
How can I run this C code with perl using Inline::C? I tried but i am not exactly getting.
Have a look at
Inline::C-Cookbook - A Cornucopia of Inline C Recipes, you
will get lot of examples using Inline
with C.
See Inline - Write Perl subroutines in other programming languages, you will come to know how to use Inline and you will get C examples too.
try:
use Inline 'C';
print "9 + 16 = ", add(9, 16), "\n";
print "9 - 16 = ", subtract(9, 16), "\n";
__END__
__C__
int add(int x, int y) {
return x + y;
}
int subtract(int x, int y) {
return x - y;
}
Say I have a large number (integer or float) like 12345 and I want it to look like 12,345.
How would I accomplish that?
I'm trying to do this for an iPhone app, so something in Objective-C or C would be nice.
Here is the answer.
NSNumber* number = [NSNumber numberWithDouble:10000000];
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setNumberStyle:kCFNumberFormatterDecimalStyle];
[numberFormatter setGroupingSeparator:#","];
NSString* commaString = [numberFormatter stringForObjectValue:number];
[numberFormatter release];
NSLog(#"%# -> %#", number, commaString);
Try using an NSNumberFormatter.
This should allow you to handle this correctly on an iPhone. Make sure you use the 10.4+ style, though. From that page:
"iPhone OS: The v10.0 compatibility mode is not available on iPhone OS—only the 10.4 mode is available."
At least on Mac OS X, you can just use the "'" string formatter with printf(3).
$ man 3 printf
`'' Decimal conversions (d, u, or i) or the integral portion
of a floating point conversion (f or F) should be
grouped and separated by thousands using the non-mone-
tary separator returned by localeconv(3).
as in printf("%'6d",1000000);
Cleaner C code
// write integer value in ASCII into buf of size bufSize, inserting commas at tousands
// character string in buf is terminated by 0.
// return length of character string or bufSize+1 if buf is too small.
size_t int2str( char *buf, size_t bufSize, int val )
{
char *p;
size_t len, neg;
// handle easy case of value 0 first
if( val == 0 )
{
a[0] = '0';
a[1] = '\0';
return 1;
}
// extract sign of value and set val to absolute value
if( val < 0 )
{
val = -val;
neg = 1;
}
else
neg = 0;
// initialize encoding
p = buf + bufSize;
*--p = '\0';
len = 1;
// while the buffer is not yet full
while( len < bufSize )
{
// put front next digit
*--p = '0' + val % 10;
val /= 10;
++len;
// if the value has become 0 we are done
if( val == 0 )
break;
// increment length and if it's a multiple of 3 put front a comma
if( (len % 3) == 0 )
*--p = ',';
}
// if buffer is too small return bufSize +1
if( len == bufSize && (val > 0 || neg == 1) )
return bufSize + 1;
// add negative sign if required
if( neg == 1 )
{
*--p = '-';
++len;
}
// move string to front of buffer if required
if( p != buf )
while( *buf++ = *p++ );
// return encoded string length not including \0
return len-1;
}
I did this for an iPhone game recently. I was using the built-in LCD font, which is a monospaced font. I formatted the numbers, ignoring the commas, then stuck the commas in afterward. (The way calculators do it, where the comma is not considered a character.)
Check out the screenshots at RetroJuJu. Sorry--they aren't full-sized screenshots so you'll have to squint!
Hope that helps you (it's in C) :
char* intToFormat(int a)
{
int nb = 0;
int i = 1;
char* res;
res = (char*)malloc(12*sizeof(char));
// Should be enough to get you in the billions. Get it higher if you need
// to use bigger numbers.
while(a > 0)
{
if( nb > 3 && nb%3 == 0)
res[nb++] = ',';
// Get the code for the '0' char and add it the position of the
// number to add (ex: '0' + 5 = '5')
res[nb] = '0' + a%10;
nb++;
a /= 10;
}
reverse(&res);
return res;
}
There might be a few errors I didn't see (I'm blind when it comes to this...)
It's like an enhanced iToA so maybe it's not the best solution.
Use recursion, Luke:
#include <stdio.h>
#include <stdlib.h>
static int sprint64u( char* buffer, unsigned __int64 x) {
unsigned __int64 quot = x / 1000;
int chars_written;
if ( quot != 0) {
chars_written = sprint64u( buffer, quot);
chars_written += sprintf( buffer + chars_written, ".%03u", ( unsigned int)( x % 1000));
}
else {
chars_written = sprintf( buffer, "%u", ( unsigned int)( x % 1000));
}
return chars_written;
}
int main( void) {
char buffer[ 32];
sprint64u( buffer, 0x100000000ULL);
puts( buffer);
return EXIT_SUCCESS;
}
I built a Perl Inline::C module, but there is some oddity with the sorting. Does anyone know why it would sort like this? Why is the 4.0e-5 is not first?
my $ref = [ 5.0e-5,4.2e-5,4.3e-5,4.4e-5,4.4e-5,4.2e-5,4.2e-5,4.0e-5];
use Inline C => <<'END_OF_C_CODE';
void test(SV* sv, ...) {
I32 i;
I32 arrayLen;
AV* data;
float retval;
SV** pvalue;
Inline_Stack_Vars;
data = SvUV(Inline_Stack_Item(0));
/* Determine the length of the array */
arrayLen = av_len(data);
// sort
sortsv(AvARRAY(data),arrayLen+1,Perl_sv_cmp_locale);
for (i = 0; i < arrayLen+1; i++) {
pvalue = av_fetch(data,i,0); /* fetch the scalar located at i .*/
retval = SvNV(*pvalue); /* dereference the scalar into a number. */
printf("%f \n",newSVnv(retval));
}
}
END_OF_C_CODE
test($ref);
0.000042
0.000042
0.000042
0.000043
0.000044
0.000044
0.000040
0.000050
Because you are sorting lexically, Try this code:
#!/usr/bin/perl
use strict;
use warnings;
my $ref = [ 5.0e-5,4.2e-5,4.3e-5,4.4e-5,4.4e-5,4.2e-5,4.2e-5,4.0e-5];
print "Perl with cmp\n";
for my $val (sort #$ref) {
printf "%f \n", $val;
}
print "Perl with <=>\n";
for my $val (sort { $a <=> $b } #$ref) {
printf "%f \n", $val;
}
print "C\n";
test($ref);
use Inline C => <<'END_OF_C_CODE';
void test(SV* sv, ...) {
I32 i;
I32 arrayLen;
AV* data;
float retval;
SV** pvalue;
Inline_Stack_Vars;
data = SvUV(Inline_Stack_Item(0));
/* Determine the length of the array */
arrayLen = av_len(data);
// sort
sortsv(AvARRAY(data),av_len(data)+1,Perl_sv_cmp_locale);
arrayLen = av_len(data);
for (i = 0; i < arrayLen+1; i++) {
pvalue = av_fetch(data,i,0); /* fetch the scalar located at i .*/
retval = SvNV(*pvalue); /* dereference the scalar into a number. */
printf("%f \n",newSVnv(retval));
}
}
END_OF_C_CODE
Of course, lexically 0.00040 is smaller than 0.00042 as well, but you aren't comparing 0.00040 to 0.00042; you are comparing the number 0.00040 converted to a string with the number 0.00042 converted to a string. When a number gets too large or small, Perl's stringifying logic resorts to using scientific notation. So you are sorting the set of strings
"4.2e-05", "4.2e-05", "4.2e-05", "4.3e-05", "4.4e-05", "4.4e-05", "4e-05", "5e-05"
which are properly sorted. Perl happily turns those strings back into their numbers when you ask it to with the %f format in printf. You could stringify the numbers yourself, but since you have stated you want this to be faster, that would be a mistake. You should not to be trying to optimize the program before you know where it slow (premature optimization is the root of all evil*). Write your code then run Devel::NYTProf against it to find where it is slow. If necessary, rewrite those portions in XS or Inline::C (I prefer XS). You will find that you get more speed out of choosing the right data structure than micro-optimizations like this.
* Knuth, Donald. Structured Programming with go to Statements, ACM Journal Computing Surveys, Vol 6, No. 4, Dec. 1974. p.268.
Perl_sv_cmp_locale is your sorting function which I suspect is lexical comparison. Look for numeric sorting one or write your own.
Have an answer with help from the people over at http://www.perlmonks.org/?node_id=761015
I ran some profiling (DProf) and it's a 4x improvement in speed
Total Elapsed Time = 0.543205 Seconds
User+System Time = 0.585454 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
100. 0.590 0.490 100000 0.0000 0.0000 test_inline_c_pkg::percent2
Total Elapsed Time = 2.151647 Seconds
User+System Time = 1.991647 Seconds
Exclusive Times
%Time ExclSec CumulS #Calls sec/call Csec/c Name
104. 2.080 1.930 100000 0.0000 0.0000 main::percent2
Here is the code
use Inline C => <<'END_OF_C_CODE';
#define SvSIOK(sv) ((SvFLAGS(sv) & (SVf_IOK|SVf_IVisUV)) == SVf_IOK)
#define SvNSIV(sv) (SvNOK(sv) ? SvNVX(sv) : (SvSIOK(sv) ? SvIVX(sv) : sv_2nv(sv)))
static I32 S_sv_ncmp(pTHX_ SV *a, SV *b) {
const NV nv1 = SvNSIV(a);
const NV nv2 = SvNSIV(b);
return nv1 < nv2 ? -1 : nv1 > nv2 ? 1 : 0;
}
void test(SV* sv, ...) {
I32 i;
I32 arrayLen;
AV* data;
float retval;
SV** pvalue;
Inline_Stack_Vars;
data = SvUV(Inline_Stack_Item(0));
/* Determine the length of the array */
arrayLen = av_len(data);
/* sort descending (send numerical sort function S_sv_ncmp) */
sortsv(AvARRAY(data),arrayLen+1, S_sv_ncmp);
for (i = 0; i < arrayLen+1; i++) {
pvalue = av_fetch(data,i,0); /* fetch the scalar located at i .*/
retval = SvNV(*pvalue); /* dereference the scalar into a number. */
printf("%f \n",newSVnv(retval));
}
}
END_OF_C_CODE