3 数值

由于我使用的lua for windows是 5.1.5 版本所以很多 5.3 的特性并不能使用,比如说整型。

3.1 数值常量

我们可以用科学计数法书写数值常量,例如:

4
0.4
4.5e-3
0.3e12
5E+20

整型值和浮点型值类型都是number

> = type(3)
number
> = type(3.5)
number
> = type(3.0)
number
>

由于整型和浮点型都是number类型,所以它们可以互相转换,具有相同算术值的整型值和浮点值在 Lua 中是相等的:

> = 3.0 == 3
true
> = 0.2e3 == 200
true
>

少数情况下需要区分整型和浮点型的时候可以使用math.type(),由于我的 Lua 是低版本,所以并没有这个函数。

Lua 语言同样支持以0x开头的十六进制常量,与其他语言不同,Lua 还支持十六进制浮点数,这种浮点数以 小数部分 和 以p或'P'开头的指数部分组成。我的版本并没有这种特性。

0xff --> 225
0x1A3 --> 419
0x0.2 --> 0.125
0x1p-1 --> 0.5
0xa.bp2 --> 42.75

可以使用%a参数,通过函数string.format()对这种带p的格式进行格式化输出,但我的版本并没有%a的参数。


附 1:string.format 格式化参数

参数 含义
%c 接受一个数字,并将其转化为ASCII码表中对应的字符
%d, %i 接受一个数字并将其转化为有符号的整数格式
%o 接受一个数字并将其转化为八进制数格式
%u 接受一个数字并将其转化为无符号整数格式
%x 接受一个数字并将其转化为十六进制数格式,使用小写字母
%X 接受一个数字并将其转化为十六进制数格式,使用大写字母
%e 接受一个数字并将其转化为科学计数法格式,使用小写字母e
%E 接受一个数字并将其转化为科学计数法格式,使用大写字母E
%f 接受一个数字并将其转化为浮点数格式
%g(%G) 接受一个数字并将其转化为%e(%E,对应%G)即%f中较短的一种格式
%q 接受一个字符串并将其转化为可安全被 Lua 编译器读入的格式
%s 接受一个字符串并按照给定的参数格式化该字符串

为了进一步细化格式,可以在%后添加参数将以如下的顺序读入:

  1. 符号:一个+号表示其后的数字转义符将让正数显示正号,默认情况下只有负数才显示符号。
  2. 占位符:一个0,在后面制定了字串宽度时展位用,不填默认用空格占位。
  3. 对齐表示:在指定了字串宽度时,默认右对齐,增加一个-号可以改为左对齐。
  4. 宽度数值
  5. 小数位数/字串裁切:在宽度数值后增加的小数部分n,若后接f则设定该浮点数只保留n位,若后接s则设定该字符串只显示前n位。
> = string.format("%02d:%02d:%02d",os.date("*t").hour, os.date("*t").min, os.date("*t").sec)
11:38:06
>

3.2 算术运算

除了加减乘除运算外,Lua 语言还支持取整除法、取模和指数运算。
对于 Lua 5.3 引入的整型而言,主要的建议就是 “开发人员要么选择忽略整型和浮点型二者之间的不同,要么就完整地控制每一个数值表示。” 因此,不论时操作整型值还是浮点型值,结果都应该是一样的。
除除法外,任意两个整型运算结果应仍是整型,两个浮点型运算结果仍是浮点型,当有一个操作数是整型另一个是浮点型时,Lua 会先将整型转化为浮点型。
由于两个整数相除的结果不总是整数,所以除法运算的操作数永远会被转换为浮点型。

Lua 5.3针对整数除法引入了一个称为 floor 除法的新算术运算符//。顾名思义,floor除法会对得到的商向负无穷取整,从而保证结果是一个整数。这样,floor 除法就可以跟其他算术运算符遵守一样的规则,当两个操作数都是整数的时候,结果就是整型值,否则就是浮点型值。

以下公式是取模运算的定义:

a % b == a - ((a // b) * b)

如果操作数是整数,那么取模运算的结果也是整型值,否则是浮点型值。
取整的结果永远与第二个操作数的符号保持一致,对于任意指定的正常量K,不论x的正负,表达式 x % K的结果永远在[0,k-1]或者[k-1,0]之间(取决于K的正负):

> = 1 % -3
-2
> = 2 % -3
-1
> = 3 % -3
0
> = 4 % -3
-2
>

x - x%0.01恰好是保留两位小数的结果,x-x%0.001恰好是保留三位小数的结果,以此类推:

> x = 3.33333
> = x - x% 0.01
3.33
> = x- x%0.001
3.333
>

因此,x-x%1是获取整数部分,x%1是获取小数部分。

Lua 同样支持幂运算,使用符号^表示即可,我们可以用x^0.5来进行开方操作。


3.3 关系运算

Lua 语言提供了下列关系运算:

< > <= >= ~=

这些关系运算的结果都是Boolean类型。
==~=进行相等性判断,如果两个操作数不是同一个类型,Lua 语言会认为他们是不相等的(整型和浮点型都是number型):

> = 0.3 == "0.3"
false
> = 3.0 == 3
true
>

3.4 数学库

Lua 语言提供了数学库math。标准数学库由一组标准的数学函数组成,包括三角函数、指数函数、取整函数、最大和最小、随机数函数以及常量pihuge,huge指的是最大的可表示数。
所有的三角函数都以弧度为单位,并通过函数deg和rad进行角度和弧度的转换。

3.4.1 随机数发生器

函数math.random用于生成伪随机数,共有三种调用方式。不带参数时,该函数将返回一个在 [0, 1) 范围内均匀分布的伪随机实数。当使用一个带有整型值 n 的参数调用时,该函数将返回一个在 [1, n] 范围内的伪随机整数。当使用两个整型值 l 和 u ,该函数返回在 [l, u] 范围内的随机整数。
函数randomseed用于设置伪随机数发生器的种子,该函数的唯一参数就是数值类型的种子。在一个程序启动时,系统固定使用1为种子初始化伪随机数发生器。如果不设置其他的种子,每次程序运行时都会生成相同的随机数序列。所以通常用math.randomseed(os.time())来使用当前系统时间作为种子初始化随机数发生器。

3.4.2 取整函数

数学库提供了三个取整函数:floorceilmodf。其中floor向负无穷取整,ceil向正无穷取整,modf向零取整。取整结果能用整型表示时,返回结果为整型值,否则返回浮点型值(用浮点数表示的整数值),除了返回取整后的值以外,函数modf还会返回小数部分作为第二结果:

> = math.floor(3.3)
3
> = math.floor(-3.3)
-4
> = math.ceil(3.3)
4
> = math.ceil(-3.3)
-3
> = math.modf(3.3)
3       0.3
> = math.modf(-3.3)
-3      -0.3
> = math.floor(2^70)
1.1805916207174e+021
>

如果参数本身就是一个整型值,那么就会返回它本身。
如果想要将数值 x 向最近的整数取整,可以对 x + 0.5 调用floor函数。

function round(x)
  local f = math.floor(x)
  if x == f then return f
  else return math.floor(x + 0.5)
  end
end

3.5 表示范围

大多数编程语言使用某些固定长度的比特位来表达数值。因此,数值的表示范围和精度上都是有限制的。
标准 Lua 使用 64 个比特位来存储整型值,其最大值为263-1,约等于1019;精简 Lua 使用 32 个比特位,存储整型值,其最大值约为20亿,数学库中定义了整型值的最大值math.maxinteger和最小值math.mininteger
当我们在整型操作时出现比mininteger更小或者maxinteger更大二点数值时,就会出现 回环
由于我的版本较低,数学库中并没有这两个常量,所以无法在 lua 环境下进行测试

math.maxinteger + 1 == math.mininteger
math.mininteger - 1 == math.maxinteger

对于浮点数而言,标准 Lua 使用双精度。标准 Lua 使用 64 个比特位表示所有数值,其中11位为指数。双精浮点数可以表示具有大致16个有效十进制位的数,范围从 -10^308 到 10^308。精简 Lua 使用32个比特位表示单精度浮点数,可以表示具有大致7个有效十进制位,范围从 -10^38 到 10^38。


3.6 惯例

我们可以简单地通过增加 0.0 的方式将整型值转换为浮点型值(我的版本中并没有这样的效果)。

-3 + 0.0 --> -3.0
0x7fffffffffffffff +0.0 --> 9.2233720368548e+18

小于 2^53 的所有整型值的表示与双精度浮点型值的表示一样,对于绝对值超过了这个值的整型值而言,再将其转换为浮点型值时可能导致精度丢失:

9007199254740992 + 0.0 == 9007199254740992 --> true
9007199254740993 + 0.0 == 9007199254740993 --> false

通过与零进行按位或运算,可以把浮点型值强制转换为整型值(需要 Lua 5.3),但是小数不能与零进行按位或运算:

print(2^53) --> 9.007199254741e+15
print(2^53 | 0) --> 9007199254740992
print(3.3 | 0) --> 报错
print(2^64 | 0) --> 超出范围

因此对于小数必须显式调用取整函数floorceilmodf


3.7运算符优先级

在第一章 Lua 语言入门 习题 1.7 中已经介绍了运算符优先级,此处不再赘述。
在二元运算符中,除了幂运算和连接操作符是右结合外,其余运算符都是左结合。所谓右结合就是从右向左执行运算,例如:x^y^z等价于x^(y^z),而x+y+z等价于(x+y)+z
在不能确定某些表达式的运算符优先级的时候,应该显式地使用括号来指定顺序


3.8 兼容性

Lua 5.3 引入的整型值导致其相对于此前的 Lua 版本出现了一定的不兼容,但是如前所述,程序员基本上可以忽略整型值和浮点型值之间的不同。
Lua 5.2 和 Lua 5.3 之间最大的不同是整数的表示范围。Lua 5.2 支持的最大整数为 2^53,而 Lua 5.3 支持的最大整数为 2^63。在当作计数值使用时,它们之间的区别通常不会导致问题;然而,当把整型值当作通用的比特位使用时(例如,把 3 个 20-bit 的整型值放在一起使用),他们之间的区别则可能很重要。
虽然 Lua 5.2 不支持整型,但是几个场景下仍然会涉及整型问题。例如,C 语言实现的库函数通常使用整型参数,但 Lua 5.2 却并没有约定这些情况下浮点型值和整型值之间的转换方法:Lua 5.2 可能将 -3.2 转换为 -3,也可能转换成 -4。Lua 5.3则明确了这种转换规则。
由于 Lua 5.2 中的数值类型只有一种,所以并没有提供函数math.type。由于 Lua 5.2 中不存在整型的概念,所以也没有常量math.maxintegermath.mininteger。虽然可以实现,但 Lua 5.2 中也没有floor除法。
可能让人感到震惊的是,与整型引入相关的问题根源在于,Lua 语言将数值转换为字符串的方式。Lua 5.2将所有的整数值格式化为整型(即不带小数点),而 Lua 5.3则将所有的浮点数格式化为浮点型(带有十进制小数点或指数)。因此 Lua 5.2 会将 7.0 格式化为 ”7“ 输出而 Lua 5.3 则会将其格式化为 ”7.0“输出。虽然 Lua 语言从未说明过格式化数值的方式,但是很多程序员都默认的是早期版本的格式化输出行为。在将字符串转换为字符串时,我们可以通过显式的指明格式的方式来避免这种问题。


3.9 练习

  • 练习 3.1:以下哪些时有效的数值常量?它们的值分别是多少?
    .0e12 .e12 0.0e 0x12 0xABFG 0xA FFFF 0xFFFFFFFF
    0x 0x1P10 0.1e1 0x0.1p1

.0e12 --> 0
.e12 --> 报错
0.0e --> 报错
0x12 --> 18
0xABFG --> 报错(十六进制没有G)
0xA --> 10
FFFF --> 报错(十六进制需要加上0x)
0xFFFFFFFF --> 4294967295
0x --> 报错
0x1P10 --> 1024
0.1e1 --> 1
0x0.1p1 --> 报错

  • 练习 3.2:解释下列表达式之所以得出相应结果的原因。(注意:证书算术运算总是会回环。)
    math.maxinteger * 2 --> -2
    math.mininteger * 2 --> 0
    math.maxinteger * math.maxinteger --> 1
    math.mininteger * math.mininteger --> 0

math.maxinteger的二进制为除符号位外全1,乘以2相当于整体左移1位,最后两位为10,1被移到符号位,有符号数用补码表示,取反+1后为全零,最后两位10,符号位不变,结果是-2。
math.mininteger的二进制为除首位外全0,左移1位后首位被抛弃,结果是全0,取反加1仍为全0,所以结果是0。
math.maxinteger除首位外全1,相乘后得到除最后一位和首部外,中间全0,超过符号位以外的部分被抛弃,其余全为0,最后只剩最后一位的1,所以结果为1。
math.mininteger除首位外全0,超出符号位以外的数值均被抛弃,其余全为0,所以结果为0。

  • 练习 3.3:下列代码的输出结果是是什么?
    for i = -10, 10 do
    print(i, i % 3)
    end
> for i = -10, 10 do
>> print(i, i % 3)
>> end
-10     2
-9      0
-8      1
-7      2
-6      0
-5      1
-4      2
-3      0
-2      1
-1      2
0       0
1       1
2       2
3       0
4       1
5       2
6       0
7       1
8       2
9       0
10      1
>

可以考虑从0开始往下倒,就可以更方便的分析负数取余的结果。

  • 练习 3.4:表达式2^ 3^ 4的值是什么?表达式2^ -3^ 4呢?
> = 2^3^4
2.4178516392293e+024
> = 2^-3^4
4.1359030627651e-025
>

由于幂运算是右结合且优先级高于负号,所以均是从右往左运算。

  • 练习 3.5:当分母是 10 的整数次幂时,数值 12.7 与表达式 127/10相等。能否认为当分母是2的整数次幂时,这是一种通用规律?对于数值5.5情况又会怎样呢?

不懂

  • 练习3.6:请编写一个通过高、母线与轴线的夹角来计算正圆锥体体积的函数。

圆锥体体积的公式是V = 1/3 Sh 其中S为底面圆面积,h为高。轴线就是指圆锥的高。S = * r^2,假设夹角为,结果为 V = 1/3 * * tan^2 h^3

function ConeVolume(height, deg)    --给出高和夹角的角度值
    local rad = math.rad(deg)
    local volume = 1/3 * math.pi * (math.tan(rad))^2 * height^3
    return volume
end
  • 练习 3.7:利用函数math.random()写一个生成遵循正态分布的伪随机数发生器。
-- 标准正态分布随机数发生器
function StandardNormalDistribution(floor, ceil)        --给出范围
    local prob = 0      --f(x)
    local mean = 0      --数学期望
    local standardDeviation = 1     --标准差
    local x = 0
    local y = 0
    math.randomseed(os.time())
    repeat
        y = math.random()
        x = floor + (ceil - floor) * math.random()
        prob = 1/((2 * math.pi * standardDeviation^2)^0.5) * math.exp(-( x - mean)^2 / (2 * standardDeviation^2))
    until prob < y
    return x
end

你可能感兴趣的:(3 数值)