阅读《Lua程序设计(第4版)》---3
总结
精简模式:在标准Lua下,整型是64位大小,浮点数是双精度的。在资源有限的平台下,可以通过宏 LUA_32BITS 编译出精简Lua ,精简模式下整型大小是32位,浮点数是单精度。
数值常量:表示数值常量时,小数点前后的内容都是可选的;如 3.3 .133 67. 都是正确的。
数值当然也可以用指数形式表示,如4E+20 .5e12 等前面的小数部分和之前相同,小数点前后内容都是可选的,指数部分必须是一个整数,不能是小数, e 和 E 都是可以的。
也可以在开头加 0x 或 0X 来表示十六进制,Lua 同样支持 C99 中的十六进制浮点数,由小数部分和以 p 或 P 开头的指数部分组成,十六进制小数虽然可读性很差,但是它的运算是精准的且格式输出时转换速度更快。
C99 里16进制浮点数记数法:类似E,但不同。数以0x开头(数字0字母x),然后是16进制浮点数部分,接着是p,后面是以 2为底的阶码。
例如:0xb.1ep5 其中:
b.1e 是16进制浮点数, 乘后面的p5。p5 等于 2的5次方(10进制512)。
所以化成十进制:(11+1/16.0+14/256.0) * 512.0。
1.type和math.type
math.type函数:存在小数部分或者用指数形式表示的数值都认为是浮点型值,否则认为是整型。Lua标准库中的 math.type 函数会返回数值的类型(一个字符串),float 或 integer
type函数:返回数值的基本数据类型(一个字符串)
print(type(3.0)) print(type(3)) print("=====") print(math.type(3.0)) print(math.type(3))
2.16进制浮点数记数法
print(0x1p-1) ---1*2^-1 print(0xa.bp2)---(10+0.6875)*2^2 print(string.format("%a",419)) print(string.format("%a",0.1))
3.算术运算
两个整型运算结果是整型,两个浮点型运算结果是浮点型,一个整型一个浮点型时先将整型值转换为浮点型然后运算。
Lua 中整除( // )的结果会向负无穷取整,取余( % )运算的定义是 a % b== a - (( a // b) * b ),因此取模结果的符号和操作数 b 相同,这与C类语言中的取模运算大不相同,因为整除的结果是向负无穷取整,而不是向零取整。
浮点数也可以进行取余和整除运算。
除法和指数运算的结果必然是浮点数,因此会将两个操作数都转化为浮点数后再进行运算。
--[[ 算术运算前先将整形值转换成浮点型,再运算 除法/、^永远是浮点数 floor除法 无穷取整数 ]] print(3/2) print(3//2) print(3.0//2) --[[ %定义:a % b = a - ((a//b)*b) ]] x = math.pi print(x-x%0.01) print(x-x%0.001)
4.关系运算
关系运算符有 > < <= >= == ~= ,关系运算的结果都是 boolean 类型,比较数值时相同子类型运算效率会更高。
5.位运算
Lua 中的位运算功能和 C 语言中的完全一致;
异或的运算符号有所不同,Lua 语言中的异或运算符是 ~ ,和按位取反的符号相同;
位运算只能对整型数使用,因此运算前会试图将两个操作数都转化为整型,如果转换失败(小数部分不为 0 ),会立即引发错误。
6.运算精度
标准 Lua 下整型最大值为 2^63 - 1,最小值为 -2^63 ,整型数的运算都是精确且可以预测的;
而双精度浮点数能精确表示的整数范围被限制在 [ -2^53 , 2^53 ] 之间,超出这个范围后多出的位数不再存储,因此浮点数的运算必须要考虑精度损失的问题。
--[[ 表示范围 浮点数表示整数范围被精确地限制在[-2^53,2^53],2^53数值9007 1992 5474 0992 ]] print("\n") print(math.maxinteger + 1 == math.mininteger) print(math.mininteger - 1 == math.maxinteger) print(- math.mininteger == math.mininteger) print(math.mininteger // -1 == math.mininteger) print(math.maxinteger) print(0x7fffffffffffffff) print(math.mininteger) print(0x8000000000000000) print(math.maxinteger + 2) print(math.maxinteger + 2.0) print(math.maxinteger + 2.0 == math.maxinteger + 1.0) --[[ 惯例 ]] print("\n") -- 整型强制转换成浮点型 print(-3+0.0) print(0x7fffffffffffffff+0.0) --精度对比 print(2^53) print(9007199254740991 + 0.0 == 9007199254740991) print(9007199254740992 + 0.0 == 9007199254740992) print(9007199254740993 + 0.0 == 9007199254740993) --浮点型强制转换成整数型 print(2^53) print(2^53 | 0) --print(3.2 | 0) --error : number has no integer representatiion 存在小数部分 --print(2^64 | 0) --error: number has no integer representatiion 超出范围 --print(math.random(1,3.5)) -- bad argument #2 to ‘random’ (数值没有用整形表示) print(math.tointeger(-258.0)) print(math.tointeger(2^30)) print(math.tointeger(5.01)) --不是整数值 print(math.tointeger(2^64)) --超出范围 function cond2int(x) -- do somethings return math.tointeger(x) or x end print(cond2int(-258.0)) print(cond2int(2^30)) print(cond2int(5.01)) --不是整数值 print(cond2int(2^64)) --超出范围 print("\n")
7.运算符优先级
--[[ 运算符优先级(^ 从右到左结合,其他从左到右结合) ^ 一元运算符(- ~ # not) * / // % + - .. (链接) << >> (按位位移) & (按位与) ~ (按位异或) | (按位或) < > <= >= == ~= and or ]] ---位运算只能对整型数使用 ---Lua 语言中的异或运算符是 ~ ,和按位取反的符号相同。位运算只能对整型数使用,因此运算前会试图将两个操作数都转化为整型,如果转换失败(小数部分不为 0 ),会立即引发错误
--[[ 指数运算是从右到左 ]] print(2^3^4) print(2^-3^4)
8.数学库
Lua 标准库中有标准数学库 math ,包含了一组常用数学函数。
math.tointeger 浮点类型转整型,不成功则返回 nil
math.type 返回数值类型的子类型,不是数值类型则返回 nil
math.abs math.sqrt 绝对值/开方(Lua中用处不大,开方可以直接 x^0.5 )
math.sin math.cos math.tan 正弦/余弦/正切
math.asin math.acos math.atan 反正弦/反余弦/反正切
math.ceil math.floor math.modf 向上取整/向下取整/得到整数和小数部分
math.exp 计算 e^x
math.rad math.deg 度数转弧度/弧度转度数
math.fmod C语言中的取余(整除是向零取整)
math.log 对数运算,默认底数是 e
math.max math.min 若干个数的最大值/最小值
math.ult 把两个参数当作无符号数运算,当第一个参数小于第二个时返回true(整型参量)
math.random math.randomseed 产生随机数,默认产生[ 0,1 )的浮点数,还可以给定两个整型参数,返回[ x,y ]之间的整型随机数/重置随机数种子
标准数学库中的常量
math.huge C语言math.h中的HUGE_VAL,无穷大
math.maxinteger math.mininteger 整型数的最大值/最小值
math.pi Pi 值
--[[ math -->三角函数 弧度制,deg和rad角度和弧度转换 huge -->最大可表表示数值,在大多数平台上表示Inf ]] print(math.rad(30)) print(math.deg(math.pi/6)) print(math.sin(math.pi/6)) print(math.sin(math.rad(30))) print("--------------") --[[ 生成伪随机数 math.random() -->[0,1) math.random(6) -->[1,6] math.random(1.4) -->[1,4] math.randomseed() -->默认为1,每次程序运行都会生成相同的伪随机数序列,一般math.randomseed(os.time()) ]] math.randomseed(os.time())--执行/取消 print(math.random()) print(math.random(6)) print(math.random(1,3)) print("--------------") --[[ 取整函数 floor 、ceil、modf,分别为向负无穷取整,向正无穷取整、向零取整(返回整数+小数部分) ]] print("\n") print(math.floor(3.3)) print(math.floor(-3.3)) print(math.ceil(3.3)) print(math.ceil(-3.3)) print(math.modf(3.3)) print(math.modf(-3.3)) print("--------------") print(math.floor(2^70)) x = 2^52 + 1 print(string.format("%d %d",x,math.floor(x+0.5))) function round(x) -- do somethings local f = math.floor(x) if x == f then return f else return math.floor(x+0.5) end end print(string.format("%d %d",x,round(x+0.5))) --[[ 无偏取整-->向距离最近的偶数取整半个整数 ]] function round2(x) -- do somethings 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(round2(2.5)) print(round2(3.5)) print(round2(-2.5)) print(round2(-3.5)) print(round2(-1.5))
练习3.1
---练习3.1 以下哪些值是有效的数值常量?它们的值分别是多少? -- .0e12 有效 0.0 -- .e12 无效 '.' unexpected -- 0.0e 无效 malformed number near '0.0e' -- 0x12 有效 18 -- 0xABFG 无效 syntax error -- 0xA 有效 10 -- FFFF 无效 变量名 -- 0xFFFFFFFF 有效 4294967295 -- 0x 无效 syntax error -- 0x1P10 有效 1024.0 -- 0.1e1 有效 1.0 -- 0x0.1p1 有效 0.125 print(.0e12) --print(.e12) print(0x12) print(0xA) print(0xFFFFFFFF) print(0x1P10) print(0.1e1) print(0x0.1p1)
练习3.2
---练习3.2 解释下列表达式之所以得出相应结果的原因。(注意:整型算术运算总是会回环) print(math.maxinteger * 2) -- => maxinteger + (maxinteger + 1) - 1 => maxinteger + mininteger - 1 => -1 + -1 => -2 print(math.mininteger * 2) -- => mininteger + (mininteger - 1) + 1 => mininteger + maxinteger + 1 => -1 + 1 => 0 print(math.maxinteger * math.maxinteger) -- => maxinteger * (mininteger - 1) => mininteger - maxinteger => 1 print(math.mininteger * math.mininteger) -- => mininteger * (maxinteger + 1) => mininteger + mininteger => 0
--[[ 1. 乘2相当于左移1位,0111…1111左移1位后变为1111…1110,对应的原码值就是 -2 2. 1000…0000左移1位后变为 0000…0000,对应的原码值就是 0 3. 0111…1111 * 0111…1111 除最低位是1不变,其他位均发生进位后为 0(每高一位1的个数加1,进位得到的1也加一,一直保持偶数个1) 4. 最高位发生进位后,所有位都是 0 ]]
练习3.3
---练习3.3 下列代码的输出结果是什么? for i = -10, 10 do print(i , i % 3) end -- 太长就不贴出来了 运行下即可 -- 注意 : Lua的取整除法向负无穷取整 -- 比如 -10 // 3 等于 -4 而不是 -3 -- 所以 -10 % 3 等于 2 而不是 -1 --若要得到与C++等语言一样的结果请使用 math.fmod
--[[
floor为向负无穷取整
%
可以看出Lua 中向下取余的特性在正数时与C类语言中向零取余的结果是一样的,但在负数时Lua取余也是向下取整后加上余数等于被除数,C类语言中取余变为向上取整后加上余数等于被除数。
]]
练习3.4
---练习3.4 表达式2^3^4的值是什么?表达式2^-3^4呢? print(2^3^4) -- 2.4178516392293e+024 print(2^-3^4) -- 4.1359030627651e-025
练习3.5
--[[ 12.7可以表示成以10为底的幂作为分母的分数,你能把12.7表示为以2为底的幂作为分母的分数吗?再试试5.5? 只需找到一个 2^n * 12.7 结果是一个整数,Lua中有两个简便的方法。 1.得到的结果对1取余,结果是0,因为Lua的浮点数也是可以取余哒,就很方便。 2.使用math.tointeger函数,如果不能转整型,函数会返回 nil ,这个特性也可以轻松判断是否是整数。 ———————————————— ]] function exercise3_5(num) -- do somethings local base,sp,cnt = 1,num,0 ---Lua的赋值 while math.tointeger(sp) == nil do base = base *2 cnt = cnt + 1 if cnt == 63 then print("Huge Number,Can Not Solve!\n") return end sp = num * base end sp = math.tointeger(sp) print("Got It,2^"..cnt) print(sp.."/"..base.."="..(sp/base)) end exercise3_5(12.7) print("\n") exercise3_5(5.5) print("------------------------")
练习3.6
function exercise3_6(h,p)---高度和角度 return 1.0/3.0*((math.tan(math.rad(p))*h)^2.0*math.pi)*h end print("------------------------")
练习3.7
function exercise3_7() local rand1,rand2 = math.random(),math.random() rand1 = (rand1 == 0) and (1) or rand1 rand2 = (rand2 == 0) and (1) or rand2 return math.sqrt(-2.0*math.log(rand1))*math.sin(2*math.pi*rand2) end tabexercise3_7 = {} math.randomseed(os.time()) for i=1,10000000 do local sp= exercise3_7() sp = (sp+0.05)//0.1 if tabexercise3_7[sp] == nil then tabexercise3_7[sp] = 1 else tabexercise3_7[sp] = tabexercise3_7[sp]+1 end end for i=-100,100,1 do if tabexercise3_7[i] ~= nil then print(i,tabexercise3_7[i]) end end