Luaの数値型
Lua 5.2まで
Lua 5.2までは、Luaの数値型は全て浮動小数点数(典型的には倍精度浮動小数点数)でした。
組み込み環境向けの場合はコンパイル時にカスタマイズすることで整数型を使うようにもできました。
倍精度浮動小数点数の場合は、
この辺は伝統的なJavaScriptと同じですね。
32ビット整数の演算をwrap-aroundさせたい場合は、加減算の場合は
function add32(x, y)
return (x + y) % 0x100000000
end
function sub32(x, y)
return (x - y) % 0x100000000
end
とすれば良いです。乗算は上位ビットと下位ビットを分けるような工夫が必要です。整数除算は
function div32(x, y)
return math.floor(x / y)
end
function quot32(x, y)
return (math.modf(x / y))
end
とすれば良いでしょう。
Lua 5.2のbit32
入力として
-
bit32.arshift(x, disp)
- 算術右シフト
- シフト量
disp
が負の場合は左シフトが行われます。 - シフト量の絶対値が31より大きい場合の結果は 0 または 0xFFFFFFFF となります。
bit32.band(...)
bit32.bnot(x)
bit32.bor(...)
-
bit32.btest(...)
-
bit32.band(...) ~= 0
みたいなやつです。
-
bit32.bxor(...)
bit32.extract(n, field [, width])
bit32.replace(n, v, field [, width])
bit32.lrotate(x, disp)
-
bit32.lshift(x, disp)
- 左シフト
- シフト量
disp
が負の場合は右シフトが行われます。 - シフト量の絶対値が31より大きい場合の結果は 0 となります。
bit32.rrotate(x, disp)
-
bit32.rshift(x, disp)
- 論理右シフト
- シフト量
disp
が負の場合は左シフトが行われます。 - シフト量の絶対値が31より大きい場合の結果は 0 となります。
Lua 5.3/5.4
を参照してください。
ビット演算に関して、算術右シフトの演算子はありません。 x // (1 << n)
を使うことになるでしょう。
LuaJIT
LuaJITは基本的にLua 5.1と互換ですが、一部独自拡張が入っています。
ビット演算
ビット演算として、Lua 5.2の bit32
とは無関係なLua BitOpというライブラリーが組み込まれています。なお、登場したのはLua BitOpの方がLua 5.2よりも早いです。
Lua BitOpは数値型が符号付き32ビット整数な環境も考慮しています。そのため、たとえ各演算の内部で符号なし32ビット整数が使われていたとしても、返り値は符号付き32ビット整数となります。
Luaの数値型が倍精度浮動小数点数の場合、
関数一覧:
-
bit.tobit(x)
- 与えられた数を符号付き32ビット整数に正規化します。
-
bit.tohex(x [, n])
- 与えられた数を指定された桁数(
n
の絶対値)で十六進文字列化します。 -
n
が正の場合は小文字、負の場合は大文字が使用されます。 -
n
の絶対値は1以上8以下で、指定された桁数では引数を文字列化できない場合は下位|n|
桁だけが返されます。
- 与えられた数を指定された桁数(
bit.bnot(x)
bit.band(x1 [, x2...])
bit.bor(x1 [, x2...])
bit.bxor(x1 [, x2...])
-
bit.lshift(x, n)
- 論理左シフト
-
n
の下位5ビットのみが使用されます。
-
bit.rshift(x, n)
- 論理右シフト
-
n
の下位5ビットのみが使用されます。
-
bit.arshift(x, n)
- 算術右シフト
-
n
の下位5ビットのみが使用されます。
-
bit.rol(x, n)
-
n
の下位5ビットのみが使用されます。
-
-
bit.ror(x, n)
-
n
の下位5ビットのみが使用されます。
-
bit.bswap
LuaJIT 2.1系の場合は、後述する64ビット整数もLua BitOpで扱えるようです。その場合、tobit
は下位32ビットを符号付き32ビット整数として返し、シフト演算は下位6ビットが使用されるようです。band
等に複数の型を混ぜて与えた場合、uint64_t
, int64_t
, 符号付き32ビット整数の順に優先されるようです。
Lua 5.2のbit32と比較すると、返り値の範囲が
64ビット整数
LuaJITのFFIライブラリーでは(ボックス化された)64ビット整数を扱えます。
コンストラクター uint64_t
, int64_t
は次のように取得できます:
local ffi = require("ffi")
local uint64_t = ffi.typeof("uint64_t")
local int64_t = ffi.typeof("int64_t")
print(uint64_t(1)) --> 1ULL
print(int64_t(1)) --> 1LL
文字列化の際には ULL
, LL
のサフィックスがつきます。また、これらのサフィックスを使ったリテラルをソース中に記述できます(パーサーの拡張 http://luajit.org/ext_ffi_api.html#literals)。
64ビット整数には通常の算術演算(+
, -
, *
, /
, %
, ^
, 単項マイナス)やLua BitOpの演算が使えます。引数の型を混在させた場合は、uint64_t
, int64_t
, 通常の浮動小数点数の順で優先されます。つまり、どちらか一方が uint64_t
であればもう一方も uint64_t
に変換した上で演算が行われます。
比較は通常の大小比較 <
が使えます。片方が uint64_t
であればもう一方も uint64_t
に変換した上で比較されます。
64ビット整数を通常の浮動小数点数に変換するには tonumber
が使えます。
【追記】詳しい記事を書きました:
dual number modeと負のゼロ
LuaJITは一部のアーキテクチャー(AArch64を含む)上ではdual number modeを使用します。これは、符号付き32ビット整数で表現できる値を内部的に32ビット整数で表すものです。
dual number modeでは単項マイナスと乗算がIEEE 754の「負のゼロ」を考慮しない場合があります。
単項マイナスの代わりに 0 / (-1) - x
を、乗算の代わりに
function float_mul(x, y)
local z = x * y
if z == 0 then
if x < 0 then
return 0 / (-1) * y
elseif y < 0 then
return 0 / (-1) * x
end
end
return z
end
というようなコードを使うことで回避できます。
おまけ:32ビット浮動小数点数のエミュレート
JavaScriptの Math.fround
みたいな、倍精度浮動小数点数を単精度浮動小数点数にキャストする関数があれば単精度浮動小数点数の演算をエミュレートできます。
Lua 5.3以降では string.pack
を使うことで単精度浮動小数点数へのキャストが行えます:
function to_f32(x)
return (string.unpack("f", string.pack("f", x)))
end
print(to_f32(3.14)) --> 3.1400001049042
LuaJITでは ffi.typeof "float"
を使うことで単精度浮動小数点数へのキャストが行えます:
local ffi = require "ffi"
local float = ffi.typeof "float"
function to_f32(x)
return tonumber(float(x))
end
print(to_f32(3.14)) --> 3.1400001049042
Discussion