Lua 数据类型 —— 数值

一、版本间的区别

在 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.maxintegermath.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.typemath.maxintegermath.minintegerfloor(因为 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吧,码字不易,请多多支持)

公众号搜索 "江澎涌" 可以更快的获取到后续的更新文章

相关推荐
波音彬要多做17 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
捕鲸叉18 分钟前
C++软件设计模式之外观(Facade)模式
c++·设计模式·外观模式
Schwertlilien1 小时前
图像处理-Ch5-图像复原与重建
c语言·开发语言·机器学习
只做开心事1 小时前
C++之红黑树模拟实现
开发语言·c++
程序员buddha2 小时前
C语言从入门到放弃教程
c语言·开发语言
程序员老冯头2 小时前
第十五章 C++ 数组
开发语言·c++·算法
程序猿会指北3 小时前
【鸿蒙(HarmonyOS)性能优化指南】启动分析工具Launch Profiler
c++·性能优化·harmonyos·openharmony·arkui·启动优化·鸿蒙开发
AAA.建材批发刘哥7 小时前
Linux快速入门-Linux文件系统管理
linux·运维·服务器·c语言·学习方法
无 证明8 小时前
new 分配空间;引用
数据结构·c++
Kisorge8 小时前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言