概要
よく使われる数値型
C | glib | gsl | OpenGL |
---|---|---|---|
char | gchar | GLchar | |
short | gshort | GLshort | |
long | glong | ||
int | gint, gboolean | GLint | |
unsigned char | guchar | GLubyte, GLboolean | |
unsigned short | gushort | GLushort | |
unsigned long | gulong | ||
unsigned int | guint | GLuint | |
float | gfloat | GLfloat | |
double | gdouble | (標準) | GLdouble |
複素数のための型
C99以降 | gsl | fftw |
---|---|---|
_Complex ,二項演算子で演算可 |
構造体,演算用の関数あり | 配列,演算用の関数なし |
ただし適切に <complex.h> を読み込めばgsl, fftwともにCの複素数型と同じになる(gslは2.7以降かつC11以降, fftw は C99以降) |
はじめに
gsl (Gnu Scientific Library) の design principle は以下のドキュメントにまとめられています。
https://www.gnu.org/software/gsl/design/gsl-design.html
Standards and conventions の章で glib に言及されており、曰く
- We follow the conventions of the glib GTK support Library.
と示されています。となれば glib や gtk との相互運用性を期待したいところですが、実際は gsl そのものの設計哲学もあり、そのまま利用出来るものではないようです。
ここでは、gsl をベースに、glib をはじめ fftw や gmp といったライブラリについて、よく使われる型の比較を行います。
主な数値型の比較
gslの場合
The library is written in ANSI C and is intended to conform to the ANSI C standard (C89).
It should be portable to any system with a working ANSI C compiler.
https://www.gnu.org/software/gsl/doc/html/usage.html#ansi-c-compliance (ansi-c-compliance)
In general, the algorithms in the library are written for double precision only.
The long double type is not supported for actual computation.
https://www.gnu.org/software/gsl/doc/html/usage.html#long-double (long-double)
というわけで gsl は数をあらわす型について、独自の型があるわけではなく、C の数値型が用いられています。
また、実数の数値型としては、特段の指示がない場合 double が使われています。
さらに、行列やベクトルを扱うための型として、gsl_block
ならびに gsl_vector
や gsl_matrix
型もあります。
これらは上記の型を要素として作成することができます。
各ライブラリや関数の型を表すために、関数名やライブラリ名に加えて数値の型を示すことがあります。
ライブラリ名を foo とし、その中にある関数を fn( ) とするなら、これらは以下のようなネーミングとなっています。
gsl_foo_fn double
gsl_foo_long_double_fn long double
gsl_foo_float_fn float
gsl_foo_long_fn long
gsl_foo_ulong_fn unsigned long
gsl_foo_int_fn int
gsl_foo_uint_fn unsigned int
gsl_foo_short_fn short
gsl_foo_ushort_fn unsigned short
gsl_foo_char_fn char
gsl_foo_uchar_fn unsigned char
glibの場合
https://github.com/GNOME/glib/blob/main/glib/gtypes.h
に別名が示されています。
https://www.freedesktop.org/software/gstreamer-sdk/data/docs/latest/glib/glib-fundamentals.html
等を確認してもよいでしょう。
typedef char gchar;
typedef short gshort;
typedef long glong;
typedef int gint;
typedef gint gboolean;
typedef unsigned char guchar;
typedef unsigned short gushort;
typedef unsigned long gulong;
typedef unsigned int guint;
typedef float gfloat;
typedef double gdouble;
さらに、glib では GVariant
を用いることで構造体や複雑なデータ構造を網羅した型を作成することができます。
数値関連の型は以下のようになっています。
Character Equivalent C type
b gboolean
y guchar
n gint16
q guint16
i gint32
u guint32
x gint64
t guint64
h gint32
d gdouble
GVariant
はglibで扱うことができる、とても強力なデータ型で、以下のように説明されています。
GVariant is a variant datatype; it can contain one or more values along with information about the type of the values.
A GVariant may contain simple types, like an integer, or a boolean value;
or complex types, like an array of two strings, or a dictionary of key value pairs.
A GVariant is also immutable: once it’s been created neither its type
nor its content can be modified further.
さらに、Cでオブジェクト指向のシステムを導入する GObject
を用いる方向に話が広がっていきます。
GObject, and its lower-level type system, GType, are used by GTK and most GNOME libraries to provide:
- object-oriented C-based APIs and
- automatic transparent API bindings to other compiled or interpreted languages.
- A lot of programmers are used to working with compiled-only or dynamically interpreted-only languages and do not understand the challenges associated with cross-language interoperability. This introduction tries to provide an insight into these challenges and > * briefly describes the solution chosen by GLib.
ここまで手を出すならもう C++ に移行したほうがいいんじゃないかという気がしますが、歴史的な経緯から GTK で頻繁に出てくるものなので、GTK を使う場合は手を出さざるを得ないものになるでしょう。
OpenGL の場合
ちょっと興味が出たのでついでに調べてみました。たとえば Ubuntu 20.04 で libgl-dev
を導入すると /usr/include/GL/
以下にヘッダファイルが入りますので、これを確認します。それぞれの型が何ビット持つかについては、アーキテクチャの差をヘッダファイルで吸収して、揃うようにしてあるようです。
複素数
gslの場合
gslで用いる複素数は構造体として定義されています。
typedef struct
{
double dat[2];
} gsl_complex;
ただし、gsl-2.7 以降では、
If a C compiler is available which supports the C11 standard,
and the<complex.h>
header file is included prior togsl_complex.h
,
thengsl_complex
will be defined to be the native C complex type:
typedef double complex gsl_complex
Some compilers, such as the gcc 4.8 series implement only a portion of
the C11 standard and so they may fail to correctly compile GSL code
when a user tries to turn on native complex functionality.
A workaround for this issue is to either remove<complex.h>
from
the include list, or add-DGSL_COMPLEX_LEGACY
to the compiler flags,
which will use the older struct-based definition ofgsl_complex
.
となっているので、C11に準拠するCコンパイラで <complex.h>
ヘッダを適切にインクルードすれば、C11と同じ複素数型となり、二項演算子による四則演算が可能になります。
なお、注釈にあるように、コンパイラが微妙に古い(C99には対応するがC11に対応していない?)場合は、コンパイラオプションを設定するなどして GSL が <complex.h>
を使用することを回避する必要があります。
もちろん gsl_complex
型の変数の演算が面倒なことになるので、Cコンパイラやgslのバージョンは新しくしておきたいところです。
fftwの場合
信号処理や画像処理などで頻繁に利用される fftw では、複素数は配列として定義されています。
typedef double fftw_complex[2];
ただし、
Alternatively, if you have a C compiler (such as
gcc
) that supports the C99 revision of the ANSI C standard, you can use C’s new native complex type (which is binary-compatible with the typedef above). In particular, if you#include <complex.h>
before<fftw3.h>
, thenfftw_complex
is defined to be the native complex type and you can manipulate it with ordinary arithmetic (e.g.x = y * (3+4*I)
, wherex
andy
arefftw_complex
andI
is the standard symbol for the imaginary unit);
とありますので、gsl同様に<complex.h>
ヘッダを適切にインクルードすれば C99 と同じ複素数型となり、二項演算子による四則演算などが可能な数値型になります。互換に必要なCのバージョンがgslより低いのは、C99の範囲で置き換えることが出来たからかな、と思います。利用者が fftw_complex
型同士で演算することは、おそらく考慮されていないことから、必要となる対応作業が少なくて済んだのかもしれません。
なおfftwの機能はgslにもありますが、gslのドキュメントによれば
For large-scale FFT work we recommend the use of the dedicated FFTW library by Frigo and Johnson.
とのことなので、取り扱うデータの規模に応じて適切にライブラリを選ぶことになろうかと思います。
glibの場合
glibに複素数型は存在しないようなので、複素数を扱いたい場合は C99以降の複素数型か gsl などライブラリを使うことになります。後者の場合は四則演算のレベルで関数などが必要になるので気を付けましょう。
おわりに
いくつかのライブラリで数値型がどのように扱われているかを比較しました。どのライブラリも基本的にCの標準的な型を踏襲しており、複素数型のようなCにとって後発の型はライブラリが独自に実装せざるを得ず、後になって標準に取り込まれたものに合わせる、というスタイルをとるようです。
<tgmath.h>
をあわせて活用し、C標準の計算精度の範囲内で様々なライブラリをうまく使いこなせるとよいですね。
蛇足
- 外部ライブラリに依存出来ない状況でもプログラムを書けるようになること(組み込み系とか競プロ系とか?)
- よりライブラリの豊富な C++ を使えるようになること(オブジェクト指向に向かうということ? あるいはJava?)
- python, go, rust, C#, TypeScript などを使えるようになること(プログラミングの目的が定まったら見えてくる?)
- VBA, GAS, Mathematica, MATLAB などを使えるようになること(特定分野のプロプライエタリ?な開発環境に慣れる?)
といったところでしょうか。Cのまま様々なライブラリを使って頑張るのは 1. と 2. の間くらいにいるのではないかと思います。
プログラミングの教授法として近年では python から入るパターンも増えているようなので、Cを汎用的低水準言語として位置付け、そこから積み上げる、という発想は、もはや古いものなのかもしれません。