一、版本间的区别
在 5.2 及 之前版本,只有 双精度 浮点格式。
在 5.3 之后,开始有 64 位整数和 float 双精度浮点类型。
lua 5.3 之后版本也可以编译为 精简 Lua 模式,该模式中使用 32 位整数 和 单精度浮点类型 。
双精度浮点类型能表示 2^53 整型数值。
精简 Lua 和标准 Lua 源码一样,除了数值表示占用的字节不一样,其余完全一致。可以通过 LUA_32BITS 进行定义(luaconf.h 文件)
二、整型和浮点型
lua 中,无论是 "浮点数" 还是 "整数" 都是 number 类型。 两者都可以进行转换。
如果真的需要区分整数和浮点数,则需要通过 math.type(xxx)
进行获取
lua
print(math.type(3)) --> integer
print(math.type(3.0)) --> float
print(math.type(3e10)) --> float
type(3) --> number
type(3.0) --> number
具有 十进制小数 或者 指数的数值 都会被当作 浮点型值 ,否则会被当作整型值。
三、十六进制浮点数
lua 5.2 之后引入支持 十六进制浮点数
十六进制浮点数由小数部分和 以 p 和 P 开头的指数部分组成。
lua
print(0x0.2) --> 0.125
print(0x1p-1) --> 0.5
print(0xa.bp2) --> 42.75
可以使用 string.format("%a", number)
进行十六进制浮点数的格式化输出。
这种格式的阅读难,但是可以保留所有的浮点数精度,并且比十进制的转换速度更快。
lua
string.format("%a", 419) --> 0x1.a3p+8
string.format("%a", 0.1) --> 0x1.999999999999ap-4
四、数值运算通用规则
和 java、kotlin 类似,只要运算的数值中有一个为 float ,则结果为 float ,否则结果为 integer 。
五、数值除法
因为两个整数相除有可能产生小数,所以在 lua 中,所有的除法运算操作永远是浮点数并且产生浮点型值
这点和 java、kotlin 不同,大概是因为 lua 数值型不区分 integer 和 float ,存在兼容意义
lua
print(3 / 2) --> 1.5
print(3.0 / 2.0) --> 1.5
print(4 / 2) --> 2.0
六、floor 除法 运算符 //
float 除法会对得到的商向负无穷取整,数值的类型保持不变,如果结果是 float 就是 float,是 integer 就是 integer(符合通用规则)
lua
print(3 // 2) --> 1
print(3.0 // 2) --> 1.0
print(6 // 2) --> 3
print(6.0 // 2.0) --> 3.0
print(-9 // 2) --> -5
print(1.5 // 0.5) --> 3.0
七、取模
如果操作数是整数,那么取模运算的结果也是整数。(符合通用规则)下面两种表达式效果一致
lua
a % b == a - ((a // b) * b)
对于任意正常量 K ,即使 x 是负数,表达式 x % K
的结果永远在 [0, K-1] 之间。
lua
print(-15 % 2) --> 1
可以使用 x = x - x % 0.01
进行保留有效小数点数
lua
print(pi - pi % 0.01) --> 3.14
八、幂运算
幂运算不遵循通用规则。
幂运算操作数永远是浮点类型,结果也永远是浮点类型。
lua
print(4 ^ (1 / 2)) --> 2.0
print(4 ^ 2) --> 16.0
print(4 ^ (1 / 3)) --> 1.5874010519682
九、关系运算
具有如下运算符
lua
< > <= >= == ~=
==
和 ~=
可以用于任何值,只要类型不同都认为不想等,类型相同则比较具体内容。
数值的比较和 具体子类型 无关,但如果具有相同子类型,效率更高。
lua
print("江澎涌" == "jiangpengyong") --> false
print("江澎涌" ~= "jiang") --> true
print("江澎涌" ~= 28) --> true
--print("江澎涌" <= 28) --> 错误 attempt to compare string with number
print(4 == 4.0) --> true
print(4 <= 4.0) --> true
十、math 库
1、huge
math.huge
用于最大可表示数值,大多数平台代表 inf
lua
print(math.huge) --> inf
math.maxinteger
、 math.minteger
获取最大最小整型值
lua
print("math.maxinteger", math.maxinteger, math.type(math.maxinteger)) --> 9223372036854775807 integer
print("math.minteger", math.mininteger, math.type(math.mininteger)) --> -9223372036854775808 integer
2、三角函数
所有的三角函数都以 弧度 为单位。
可以通过 deg
(弧度转角度) 和 rad
(角度转弧度) 进行角度和弧度的转换。
lua
print(math.deg(6.2831853071796)) --> 360
print(math.rad(360)) --> 6.2831853071796
3、random
(1)产生数值
math.random()
--> [0, 1)
math.random(a)
--> [1, a]
math.random(a, b)
--> [a, b]
(2)设置种子
lua
math.randomseed(number)
如果种子为 1 ,则每次程序运行产生的数字序列都是相同的,这个在测试的时候会很有用。
如果想每次都不同,则可以使用 math.randomseed(os.time())
进行设置即可。
lua
-- 如果设置了这一句,每次运行程序产生的值都是一样的
math.randomseed(1)
local r1 = ""
for _ = 1, 5 do
r1 = r1 .. " " .. math.random()
end
print("math.random() 五次:", r1) --> math.random() 五次: 0.81558781554723 0.98657750643458 0.079330719590026 0.49864849323369 0.59181018547899
local r2 = ""
for _ = 1, 5 do
r2 = r2 .. " " .. math.random(10)
end
print("math.random() 五次:", r2) --> math.random() 五次: 7 9 8 5 3
local r3 = ""
for _ = 1, 5 do
r3 = r3 .. " " .. math.random(11, 20)
end
print("math.random() 五次:", r3) --> math.random() 五次: 12 13 17 17 13
4、取整
floor 向 负无穷取整
ceil 向 正无穷取整
modf 向 零取整 , 会返回两个结果,小数部分作为第二个结果
lua
print(math.floor(3.3)) --> 3
print(math.ceil(3.3)) --> 4
print(math.modf(3.3)) --> 3 0.3
小技巧
可以通过对 floor(x + 0.5)
达到对 x 向最近的整数取整,但是值过大时会有问题。
lua
print(string.format("1.3 最近值取整:%f", math.floor(1.3 + 0.5))) -->1
x = 2 ^ 52 + 1
print(string.format("大数字:%f", x)) --> 4503599627370497.000000
print(string.format("大数字+0.5 floor :%f", math.floor(x + 0.5))) --> 4503599627370498.000000 错误的!!
这是因为 2 ^ 52 + 1
浮点值表示不精确,内部会以不可控方式取整,所以可以单独对整数部分做处理即可:
lua
function round(x)
local f = math.floor(x)
if x == f then
return f
else
return math.floor(x + 0.5)
end
end
print("大数字 round: ", round(x)) --> 4503599627370497
print("大数字 round: ", round(x + 0.5)) --> 4503599627370498
无偏取整(取最近的偶数)
lua
function unbiasedRound(x)
local f = math.floor(x)
if (x == f) or (x % 2.0 == 0.5) then
return f
else
return math.floor(x + 0.5)
end
end
print("无偏取整: ", unbiasedRound(2.5)) --> 无偏取整: 2
十一、表示范围
1、整型
标准 Lua 使用 64 比特位存储整型值,最大值为 2^63-1
精简 Lua 使用 32 比特位存储整型值,最大值为 2^31 -1
整型值最大可以通过 math.maxinteger
获取,最小值通过 math.mininteger
lua
print("math.huge", math.huge, math.type(math.huge)) --> inf float
print("math.maxinteger", math.maxinteger, math.type(math.maxinteger)) --> 9223372036854775807 integer
print("math.mininteger", math.mininteger, math.type(math.mininteger)) --> -9223372036854775808 integer
如果整型值发生溢出,则会有回环。
lua
print(math.maxinteger + 1 == math.mininteger) --> true
print(math.mininteger - 1 == math.maxinteger) --> true
print(-math.mininteger == math.mininteger) --> true
print(math.mininteger // -1 == math.mininteger) --> true
print(math.maxinteger) --> 9223372036854775807
print(0x7fffffffffffffff) --> 9223372036854775807
print(math.mininteger) --> -9223372036854775808
print(0x7fffffffffffffff + 1) --> -9223372036854775808
print(0x8000000000000000) --> -9223372036854775808
2、浮点数
标准使用双精度浮点数表示,具有 16 位有效十进制的数 [-10^308, 10^308] , 二进制的话为 [-2^53, 2^53]
精简使用 32 位,表示 7 位有效十进制的数 [-10^38, 10^38]
浮点数溢出会取近似值
lua
print(math.maxinteger + 1.0) --> 9.2233720368548e+18
print(math.maxinteger + 2.0) --> 9.2233720368548e+18
print(math.maxinteger + 1.0 == math.maxinteger + 2.0) --> true
3、整型和浮点型
在 [-2^53, 2^53] 内,可以忽略整型和浮点型的区别
十二、转换
1、整型转换为浮点数
在 [-2^53, 2^53] 范围内,只需要将 整型数值 + 0.0 则可以进行转换为 浮点数
如果超出范围,会导致精度丢失,取近似值。
2^53 即为 9007199254740992 ,一旦溢出则会进行取近似值。
例如 (9007199254740993 + 0.0) 会被取整为 9007199254740992 。
lua
print((9007199254740993 + 0.0) == 9007199254740993) --> false
print((9007199254740993 + 0.0) == 9007199254740992) --> true
print((9007199254740992 + 0.0) == 9007199254740992) --> true
print((9007199254740991 + 0.0) == 9007199254740991) --> true
2、浮点数转为整型
第一种,通过与 0 进行按位或运算(Lua 5.3 之后才引入位运算)
lua
fl2 = 3.0
print(fl2, "type", math.type(fl2)) --> 3.0 type float
int2 = fl2 | 0
print(int2, "type", math.type(int2)) --> 3 type integer
如果带有小数点则会抛异常 number has no integer representation
lua
print("有小数:", (3.2 | 0))
超出范围则会异常 number has no integer representation
lua
print("超出范围:", (2 ^ 64 | 0))
第二种
使用 math.tointeger
,规则是一样的,只是转不成功则返回 nil (带有小数或超出范围返回 nil)
lua
print(math.tointeger(-258.0)) --> -258
print(math.tointeger(2 ^ 32)) --> 4294967296
print(math.tointeger(258.01)) --> nil
print(math.tointeger(2 ^ 64)) --> nil
妙用
可用 math.tointeger
检测是否可以将一个值转为整型值
lua
function cond2int(x)
return math.tointeger(x) or x
end
print(cond2int(5.3)) --> 5.3
print(cond2int(10.0)) --> 10
十三、兼容性
主要区分在 Lua 5.3 之前和之后(包括 5.3)
1、类型不同
5.2 及之前版本不存在 整型类型,只有浮点类型,所以最大的整型值 只有 2^53
5.3 及之后版本数值分为 整型 和 浮点型,整型的范围 [-2^63, 2^63-1],浮点型还是和之前一样 [-2^53,2^53]
2、函数不同
因为 5.2 及之前版本没有整型,所以没有 math.type
、 math.maxinteger
、 math.mininteger
、 floor
(因为 5.2 的取模运算基本和除法一样) 这些函数
3、转换规则
5.2 及之前的版本,在和 c 之间的数值转换是不确定的。
5.3 及之后的版本,则数值只有在恰好可以表示整数时才会进行转换
4、输出
5.2 及之前版本,"3.0" 会输出为 "3"
5.3 及之后版本,"3.0" 会输出位 "3.0"
lua
--- 5.3 lua
print(3.0) --> 3.0
print(string.format("%s", 3.0)) --> 3.0
这样做其实就是遵循了,使用了什么类型,就输出什么样的格式。
十四、写在最后
Lua 项目地址:Github传送门 (如果对你有所帮助或喜欢的话,赏个star吧,码字不易,请多多支持)
公众号搜索 "江澎涌" 可以更快的获取到后续的更新文章