ざっくりZig - 数値の表し方と計算

2024/03/06に公開

プログラミングでは計算をよく行います。計算の方法も一般的な四則演算(+, -, *, /)以外に数学で使う関数などがあります。Zigにも例外なくこれらが用意されています。

$ cat number.zig
const std = @import("std");
pub fn main() !void {
    // 出力先を取得(標準出力)
    const stdout = std.io.getStdOut().writer();

    // 数の表し方(以下すべて同じ数)
    const decimal     = 90;         // 10進数
    const binary      = 0b101_1010; // 2進数
    const octal       = 0o132;      // 8進数
    const hexadecimal = 0x5a;       // 16進数

    // すべての変数の値を表示
    try stdout.print("{d} / binary = {b}, octal = {o}, hexadecimal = {x}\n",
                     .{decimal, binary, octal, hexadecimal});
}

$ zig run number.zig
90 / binary = 1011010, octal = 132, hexadecimal = 5a

数値の表し方と表示

数値の表し方は10進数、2進数、8進数、16進数の4つが用意されています。画面に表示する方法もそれに対応したものがあります。桁数が多いときは_で区切ることもできます。小数点がつけられるのは10進数と16進数のみですが、小数点付きの16進数は表示するとき{x}ではなく{d}にする必要があります。

種類 表記 書式 備考
10進数 NNNN {d} (N:0-9)
2進数 0bNNNN {b} (N: 0, 1)
8進数 0oNNNN {o} (N: 0-7、oは小文字のオー)
16進数 0xNNNN {x}, {X} (N: 0-9, {x}のときa-f, {X}のときA-F)

2進数はコンピュータ内部での数値表現を表したもので、8進数や16進数はそれを変換したものです。8進数は2進数の3桁分を1桁で表していて、16進数は2進数の4桁分を1桁で表しています。16進数は文字コードやメモリアドレスなどの表記で使われています。

四則演算

基礎的な計算の方法として四則演算(+, -, *, /, %)と括弧による優先順位の変更があります。*掛け算、/は割り算、%は余り(剰余)を表します。これらの記号を 演算子 といいます。ただし、小数点のない整数と小数点のある実数とを混ぜて/を計算して余りが実数の時は実行時エラーとなります。

また、整数どうしの割り算の結果は小数点以下が切り捨てられます。そのため、小数点以下の値が必要な計算は実数どうしで計算するほうが結果がより正確になります。(整数と実数の変換は別途紹介します)

1 + 2           // 3
1 + 2 * 3       // 7
(1 + 2) * 3     // 9 (括弧内を先に計算)

7 / 4;          // 1
7.0 / 4.0       // 1.75
// 7.0 / 4      実行時エラー (コンパイルは可能)

7 % 4           // 3
7 % 2.3         // 0.1
7.3 % 2         // 1.3

関数による計算

あらかじめ用意されている関数でできる計算もあります。これらは必要な値を引数で与えると結果を返します。剰余算や絶対値など基本的な処理から三角関数のような数学的な処理まで、さまざまなものが用意されています。

実数を整数に丸める関数

括弧内の値(引数は実数)に近い整数を取得するための関数です。引数が正の数か負の数かによって結果の絶対値に違いが出るので注意してください。

関数 意味 結果
@floor 切り捨て 引数より小さくて最大の整数
@ceil 切り上げ 引数より大きくて最小の整数
@trunc 絶対値を小さくする丸め 絶対値が引数より小さくて最大の整数(符号は維持)
@round 絶対値を大きくする丸め 絶対値が引数より大きくて最小の整数(符号は維持)
@floor(1.5)		# 1.5を切り捨て
@ceil(1.5)		# 1.5を切り上げ

結果

引数 @floor @ceil @trunc @round
1.5 1.0 2.0 1.0 2.0
1.4 1.0 2.0 1.0 1.0
-1.4 -2.0 -1.0 -1.0 -1.0
-1.5 -2.0 -1.0 -1.0 -2.0

割り算の関数

割り算の結果をどのように丸めるかが異なる複数の関数が用意されています。
@divFloor, @divTrunc, @divExactはいずれも2つめの引数が0のときエラーとなります。

関数 結果
@divFloor 割り算の結果を切り捨て
@mod @divFloorを計算した時の剰余
@divTrunc 割り算の結果を絶対値が少ないほうに丸める
@rem @divTruncを計算した時の剰余
@divExact c = @divTrunc(a, b) のとき c * b と a が異なるとエラー
@divFloor(-5, 3)
@divTrunc(-3.5, 0.7)

結果

引数 @divFloor @mod @divTrunc @rem @divExact
-5, 3 -2 1 -1 -2 エラー
5, 3 1 2 1 2 エラー
-3.5, 0.7 -5.0 0.7 -5.0 0.0 エラー
3.5, 0.7 5.0 0.0 5.0 0.0 エラー
-6, 3 -2 0 -2 0 -2

数学関数

三角関数、対数など数学関連の関数も用意されています。関数名の先頭に@がつくもののほか、std.math.NNNNNNNNのような関数名のものもあります。また、std.math.pi(円周率)のように定数も含まれています。

  • @関数
    Zigには関数名の最初に@がつく関数が多数組み込まれていますが、その中には数学で使われるものもあります。
const pi = std.math.pi;		# 円周率(3.14159...)
try stdout.print("sin(pi / 2.0) = {d}\n", .{@sin(pi / 2.0)});	// sin(pi / 2.0) = 1
try stdout.print("cos(pi / 2.0) = {d}\n", .{@cos(pi / 2.0)});	// cos(pi / 2.0) = -1
関数 結果 実数 整数
@abs 絶対値
@sqrt 平方根(ルート) ×
@sin 正弦 ×
@cos 余弦 ×
@tan 正接 ×
@exp 指数関数(e) ×
@exp2 指数関数(2) ×
@log 自然対数(e) ×
@log2 対数(底は2) ×
@log10 常用対数 ×
...など
  • std.math関数
    std.math.XXXXXという名称の関数です。APIドキュメントでは明らかになっていませんが、実際には引数の型がf32やf64に制限されていて直接数値定数を書けないものがあります。(型については別途紹介します)
const std = @import("std");
const math = std.math;
math.cosh(...);	// 引数に直接数値定数を書けない
math.sinh(...); // (同上)
関数 結果 実数 整数
std.math.acos 逆余弦 ×
(以下略) cosh 双曲線余弦 ×
acosh 逆双曲線余弦 ×
asin 逆正弦 ×
sinh 双曲線正弦 ×
asinh 逆双曲線正弦 ×
atan 逆正接 ×
tanh 逆双曲正接 ×
atanh 逆双曲線正接 ×
atan2 4象限逆正接 ×
...など
  • 定数
    std.math.XXXXXの中には、円周率、ネイピア数(自然対数の底)、黄金比、対数など数学でよく使われる定数も定義されています。これらを使うことで余計な計算をせずに済みます。以下のようにターミナルで表示するときは小数点以下15桁までがデフォルトですが、github上のソースコードによれば、もっと多い桁数で定義されています。
const std = @import("std");
const math = std.math;
try stdout.print("pi = {d}\n", .{math.pi});             // pi = 3.141592653589793
try stdout.print("e = {d}\n", .{math.e});               // e = 2.718281828459045
try stdout.print("phi = {d}\n", .{math.phi});           // phi = 1.618033988749895
try stdout.print("log2e = {d}\n", .{math.log2e});       // log2e = 1.4426950408889634
try stdout.print("log10e = {d}\n", .{math.log10e});     // log10e = 0.4342944819032518
try stdout.print("ln2 = {d}\n", .{math.ln2});           // ln2 = 0.6931471805599453
try stdout.print("ln10 = {d}\n", .{math.ln10});         // ln10 = 2.302585092994046
try stdout.print("sqrt2 = {d}\n", .{math.sqrt2});       // sqrt2 = 1.4142135623730951
try stdout.print("sqrt1_2 = {d}\n", .{math.sqrt1_2});   // sqrt1_2 = 0.7071067811865476

まとめ

  • 数値の表し方は10進数({d})、2進数(0b, {b})、8進数(0o, {o})、16進数(0x, {x}, {X})がある
  • 四則演算: - +, -, *, /, ()括弧
  • 計算をする関数には@関数とstd.math関数がある
    • 数値を丸める: @floor, @ceil, @trunc, @round
    • 割り算: @difFloor, @mod, @divTrunc, @rem, @divExact
    • @関数: @abs, @sqrtなど
    • std.math関数: std.math.acosなど
  • 定数
    • std.math.pi, std.math.eなどが予め定義されている

2進数とビット演算 >
< 変数と代入
ざっくりZig 一覧

Discussion