6

64 ビット整数を使用する C 関数を Perl コードに移植したいと考えています。そのために、Perl XS を使用します。

私の問題は、Perl XS 型 (U32、U16、および U8 のみ) に 64 ビット整数がないことです。

では、Perl XS コードで 64 ビット整数を使用する最良の方法は何ですか?

これが私がやりたいことのコード例です:

uint64_t
foo(integer)
   uint64_t integer

   CODE:
      RETVAL = foo(integer);

   OUTPUT:
      RETVAL

foo() には C プロトタイプがあります。

uint64_t foo(uint64_t);

perlxs のドキュメントと stackoverflow.com には、役に立つ情報は何も見つかりませんでした。

4

3 に答える 3

6

符号なし整数に対する Perl の内部型はUV. プラットフォームに 64 ビット UV がある場合は、問題ありません。

32 ビット UV (32 ビット OS および Perl は use64bitint なしでコンパイルされました) では、大きな整数と浮動小数点数 (NV通常はdouble) の間で変換できます。しかし、IEEE double には 53 ビットの仮数部しかないため、これにより大きな数の精度が失われます。

uint64_t integer;

// Convert uint64_t to SV.

if (integer <= UV_MAX) {
    RETVAL = newSVuv((UV)integer);
}
else {
    RETVAL = newSVnv((NV)integer);
}

// Convert SV to uint64_t with range checks.

if (SvUOK(sv)) {
    integer = (uint64_t)SvUV(sv);
}
else if (SvIOK(sv)) {
    IV iv = SvIV(sv);
    if (iv < 0) croak(...);
    integer = (uint64_t)iv;
}
else if (SvNOK(sv)) {
    NV nv = SvNV(sv);
    if (nv < 0.0 || nv >= 18446744073709551616.0) croak(...);
    integer = (uint64_t)nv;
}
else {
    // Parse a uint64_t from the string value, or whatever.
}

精度の低下に耐えられず、32 ビット UV をサポートしたい場合は、次のC APIを使用できますMath::Int64(「 」も参照Module::CAPIMaker)。

#define MATH_INT64_NATIVE_IF_AVAILABLE
#include "perl_math_int64.h"

MODULE = ...

BOOT:
    PERL_MATH_INT64_LOAD_OR_CROAK;

SV*
foo(args...)
CODE:
    uint64_t integer = ...
    RETVAL = newSVu64(integer);
OUTPUT:
    RETVAL

のようなタイプマップで

TYPEMAP
uint64_t T_UINT64

INPUT
T_UINT64
    $var = SvU64($arg)

OUTPUT
T_UINT64
    $arg = newSVu64($var);

uint64_tXSで直接使用できます:

uint64_t
foo(arg)
    uint64_t arg
于 2015-07-19T10:09:23.983 に答える
5

この型は、Perl ディストリビューションに付属するuint64_tdefault では定義されていません。typemapによるとperlxstypemap

xsubppより実用的な用語では、typemap は、 C 関数のパラメーターと値を Perl の値にマップするためにコンパイラーによって使用されるコードフラグメントのコレクションです。

独自に定義できますtypemap(ファイルと同じディレクトリに.xs)。次のようになります。

TYPEMAP
unsigned long long T_U_LONG_LONG
uint64_t T_U_LONG_LONG              # equivalent to typedef unsigned long long uint64_t;

INPUT
T_U_LONG_LONG
    $var = (unsigned long long)SvUV($arg)

OUTPUT
T_U_LONG_LONG
    sv_setuv($arg, (UV)$var);

SvUVこれらのマッピングについては、特に部分的に確信が持てないUVため、実際のコードで慎重にテストする必要があります。それらは単なる「プレーンな」整数であると思われるuint64_tため、内部使用、つまり関数の定義内でのみ完全に機能します。

C標準(C99以降)によると、unsigned long long型は少なくとも64ビット幅ですが、私が知っているすべての実装で64ビットであることに注意してください。

次のようにtest.xs:

#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
#include "ppport.h"

MODULE = test       PACKAGE = test

unsigned int
u64_mul_high(a, b)
    unsigned int a
    unsigned int b
CODE:
    RETVAL = ((uint64_t) a * b) >> 32;
OUTPUT:
    RETVAL

次のようにtest.pl定義されます。

#!/usr/bin/perl
use ExtUtils::testlib;
use test;

print test::u64_mul_high(2147483647, 1000), "\n";

次の結果が得られます。

499 (0001 1111 0011)

32-bit x 32-bitこれは、結果が 64 ビット整数になる乗算の上位 32 ビット リムです。

これを 32 ビット GNU/Linuxsizeof(long) = 4と Perl 5.10 で確認しました。

于 2015-07-18T12:46:08.823 に答える
4
$ perl -MConfig -le 'print $Config{use64bitint}'

perl64 ビット整数を使用するようにコンパイルされているかどうかが表示されます。その場合、IV は 64 ビットです。

perldoc perlgutsも参照してください:

「Ⅳ」とは?

Perl は、ポインター (および整数) を保持するのに十分な大きさであることが保証されている単純な符号付き整数型である特別な typedef IV を使用します。さらに、単に署名されていない IV である UV があります。

Perl は、2 つの特別な typedef、I32 と I16 も使用します。これらは常に、それぞれ少なくとも 32 ビットと 16 ビットの長さになります。(繰り返しになりますが、U32 と U16 もあります。) 通常、これらは正確に 32 ビットと 16 ビットの長さですが、Cray では両方とも 64 ビットになります。

Math::Int64も参照してください。

このモジュールは、Perl に、符号付きおよび符号なしの 64 ビット整数のサポートを追加します。

...

利用可能な場合は、ネイティブ 64 ビット サポートにフォールバックします

プログラムで字句プラグマMath::Int64::native_if_availableが使用され、使用中の perl のバージョンが 64 ビット整数をネイティブにサポートしている場合、64 ビット整数を作成するモジュールからインポートされた関数 (つまりuint64int64string_to_int64native_to_int64など) は通常の perl スカラーを返します。

于 2015-07-18T12:11:07.307 に答える