3427 字
17 分钟
仓颉文档阅读-开发指南III: 基础数据类型(II) - 整型、浮点型和布尔类型
NOTE

阅读文档版本:

语言规约 Cangjie-0.53.18-Spec

具体开发指南 Cangjie-LTS-1.0.3

在阅读 了解仓颉的语言规约时, 难免会涉及到一些仓颉的示例代码, 但 我们对仓颉并不熟悉, 所以可以用 仓颉在线体验 快速验证

有条件当然可以直接 配置Canjie-SDK

WARNING

博主在此之前, 基本只接触过C/C++语言, 对大多现代语言都没有了解, 所以在阅读过程中遇到相似的概念, 难免会与C/C++中的相似概念作类比, 见谅

且, 本系列是文档阅读, 而不是仓颉的零基础教学, 所以如果要跟着阅读的话最好有一门编程语言的开发经验

WARNING

在阅读仓颉编程语言的开发指南之前, 已经大概阅读了一遍 仓颉编程语言的语言规约, 已经对仓颉编程语言有了一个大概的了解

所以在阅读开发指南时, 不会对类似: 类、函数、结构体、接口等解释起来较为复杂名称 做出解释

此样式内容, 表示文档原文内容

基础数据类型#

整数类型#

整数类型分为有符号(signed)整数类型和无符号(unsigned)整数类型

有符号整数类型包括Int8Int16Int32Int64IntNative, 分别用于表示编码长度为8-bit16-bit32-bit64-bit和平台相关大小的有符号整数值的类型

无符号整数类型包括UInt8UInt16UInt32UInt64UIntNative, 分别用于表示编码长度为8-bit16-bit32-bit64-bit和平台相关大小的无符号整数值的类型

对于编码长度为N的有符号整数类型, 其表示范围为: 2N12N11 −2 ^ {N−1} ∼ 2 ^ {N−1} − 1

对于编码长度为N的无符号整数类型, 其表示范围为: 02N1 0 ∼ 2 ^ {N} −1

下表列出了所有整数类型的表示范围:

类型表示范围
Int827271(128127)−2 ^ {7} ∼ 2 ^ {7} − 1 (−128 ∼ 127)
Int162152151(32,76832,767)−2 ^ {15} ∼ 2 ^ {15} − 1 (−32,768 ∼ 32,767)
Int322312311(2,147,483,6482,147,483,647)−2 ^ {31} ∼ 2 ^ {31} − 1 (−2,147,483,648 ∼ 2,147,483,647)
Int642632631(9,223,372,036,854,775,8089,223,372,036,854,775,807)−2 ^ {63} ∼ 2 ^ {63} − 1 (−9,223,372,036,854,775,808 ∼ 9,223,372,036,854,775,807)
IntNativePlatform dependent
UInt80281(0255)0 ∼ 2 ^ {8} − 1 (0 ∼ 255)
UInt1602161(065,535)0 ∼2 ^ {16} − 1 (0 ∼ 65,535)
UInt3202321(04,294,967,295)0 ∼ 2 ^ {32} − 1 (0 ∼ 4,294,967,295)
UInt6402641(018,446,744,073,709,551,615)0 ∼ 2 ^ {64} − 1 (0 ∼ 18,446,744,073,709,551,615)
UIntNativePlatform dependent

程序具体使用哪种整数类型, 取决于该程序中需要处理的整数的性质和范围

Int64类型适合的情况下, 首选Int64类型, 因为Int64的表示范围足够大

并且 整数类型字面量 在没有类型上下文的情况下默认推断为Int64类型, 可以避免不必要的类型转换

仓颉中整数类型根据位数和有无符号共分为10种, 其中两种IntNativeUIntNative与平台相关

仓颉中的整型字面量, 在没有上下文时, 默认为Int64类型, 即: 类似let value = 20, value就是Int64类型

整数类型字面量#

整数类型字面量有4种进制表示形式: 二进制(使用0b0B前缀)、八进制(使用0o0O前缀)、十进制(没有前缀)、十六进制(使用0x0X前缀)

例如, 对于十进制数24, 表示成二进制是0b00011000(或0B00011000), 表示成八进制是0o30(或0O30), 表示成十六进制是0x18(或0X18)

在各进制表示中, 可以使用下划线_充当分隔符的作用, 方便识别数值的位数, 如0b0001_1000

对于整数类型字面量, 如果它的值超出了上下文要求的整数类型的表示范围, 编译器将会报错

let x: Int8 = 128 // Error, 128 out of the range of Int8
let y: UInt8 = 256 // Error, 256 out of the range of UInt8
let z: Int32 = 0x8000_0000 // Error, 0x8000_0000 out of the range of Int32

在使用整数类型字面量时, 可以通过加入后缀来明确整数字面量的类型, 后缀与类型的对应为:

后缀类型后缀类型
i8Int8u8UInt8
i16Int16u16UInt16
i32Int32u32UInt32
i64Int64u64UInt64

加入了后缀的整数字面量可以通过以下方式使用:

var x = 100i8 // x 是 100 Int8 类型
var y = 0x10u64 // y 是 16 UInt64 类型
var z = 0o432i32 // z 是 282 Int32 类型

仓颉中的整型字面量支持: 二进制、八进制、十进制和十六进制

并且, 可以使用_进行分割, 不影响数值, 同时可以使用后缀明确类型:

// 二进制 需要加前缀 0b/0B
0b00001111
0b00001111i8
0b00001111_i8
0b0000_1111_i8
0b0000_1111_u8
// 八进制 需要加前缀 0o/0O
0o76543
0o76543i16
0o76543_i16
0o76_543_i16
0o76_543_u16
// 十进制 无前缀
12345678
12345678i32
12345678_i32
12_345_678_i32
12_345_678_u32
// 十六进制 需要加前缀 0x/0X
0xFE
0xFEi8
0xFE_i8
0xFE_DC_i16
0xFE_DC_u16

字符字节字面量#

仓颉编程语言支持字符字节字面量, 以方便使用ASCII码表示UInt8类型的值

字符字节字面量由字符b、一对标识首尾的单引号、以及一个ASCII字符组成, 例如:

var a = b'x' // a is 120 with type UInt8
var b = b'\n' // b is 10 with type UInt8
var c = b'\u{78}' // c is 120 with type UInt8
c = b'\u{90}' - b'\u{66}' + c // c is 162 with type UInt8

b'x'表示类型为UInt8大小是120的字面值

另外还可以通过b'\u{78}'这种转义形式表示类型为UInt8, 16进制大小为0x7810进制大小为120的字面值

需要注意的是, \u内部最多有两位16进制数, 并且值必须小于256(十进制)

仓颉中存在字符字节字面量, 不是字符字面量, 而不是字符串字面量

而是一b'ASCII'形式的字符字节字面量, ''包裹的内容必须是ASCII表中的字符, 或\u{二位十六进制 < 256}

整数类型支持的操作#

整数类型默认支持的操作符包括: 算术操作符、位操作符、关系操作符、自增和自减操作符、复合赋值操作符

各操作符的优先级参见附录中的操作符

  1. 算术操作符包括: 一元负号(-)、加法(+)、减法(-)、乘法(*)、除法(/)、取模(%)、幂运算(**)

    • 除了一元负号(-)和幂运算(**), 其他操作符要求左右操作数是相同的类型

    • *, /, +-的操作数可以是整数类型或浮点类型

    • %的操作数只支持整数类型

    • **的左操作数只能为Int64类型或Float64类型, 并且:

      • 当左操作数类型为Int64时, 右操作数只能为UInt64类型, 表达式的类型为Int64

      • 当左操作数类型为Float64时, 右操作数只能为Int64类型或Float64类型, 表达式的类型为Float64

  2. 位操作符包括: 按位求反(!)、左移(<<)、右移(>>)、按位与(&)、按位异或(^)、按位或(|)

    注意, 按位与、按位异或和按位或操作符要求左右操作数是相同的整数类型

  3. 关系操作符包括: 小于(<)、大于(>)、小于等于(<=)、大于等于(>=)、相等(==)、不等(!=)

    要求关系操作符的左右操作数是相同的整数类型

  4. 自增和自减操作符包括: 自增(++)和自减(--)

    注意, 仓颉中的自增和自减操作符只能作为一元后缀操作符使用

  5. 复合赋值操作符包括:+=-=*=/=%=**=<<=>>=&=^=|=

整数类型之间、整数类型和浮点类型之间可以互相转换, 整数类型可以转换为字符类型, 具体的类型转换语法及规则请参见[数值类型之间的转换]

注意:

本章所提及的某个类型支持的操作, 均是指在没有操作符重载的前提下

仓颉的整数类型、浮点类型之间可以相互显式转换, 整数类型也可以转换成目标字符类型, 但 都需要遵循类型转换的语法规则

浮点类型#

浮点类型包括Float16Float32Float64, 分别用于表示编码长度为16-bit32-bit64-bit的浮点数(带小数部分的数字, 如3.141598.240.1等)的类型

Float16Float32Float64分别对应IEEE 754中的半精度格式(即binary16)、单精度格式(即binary32)和双精度格式(即binary64)

Float64的精度(有效数字位)约为15位, Float32的精度(有效数字位)约为6位, Float16的精度(有效数字位)约为3

使用哪种浮点类型, 取决于代码中需要处理的浮点数的性质和范围

在多种浮点类型都适合的情况下, 首选精度高的浮点类型, 因为精度低的浮点类型的累计计算误差很容易扩散, 并且它能精确表示的整数范围也很有限

浮点数, 即小数

目前, 绝大多数的现代编程语言对于浮点数的规定都遵循IEEE 754标准

针对不同的浮点精度, 仓颉中存在三种浮点类型:Float16``Float32``Float64, 分别对应 半精度、单精度和双精度

对应的有效位精度为3位、6位和15

这是什么意思呢?

精度的位数, 表示浮点数可靠位数, 包括整数位和小数位

如果存在一个浮点数字面量:123.1234567890123456789

那么, 将此字面量赋值到三种不同的浮点类型时, 数据位的可靠程度是不同的:

  1. Float16:

    let half: Float16 = 123.1234567890123456789
    println(half.format(".20"))
  2. Float32:

    let single: Float32 = 123.1234567890123456789
    println(single.format(".20"))
  3. Float64:

    let double: Float64 = 123.1234567890123456789
    println(double.format(".20"))

这三个变量打印的结果是不同的:

打印结果截图

可以看到:

  1. 对于Float16, 对于整个十进制数, 只有5位是准确的:3位整数, 2位小数

  2. 对于Float32, 对于整个十进制数, 有8位是准确的:3位整数, 5位小数

  3. 对于Float64, 对于整个十进制数, 有16位是准确的:3位整数, 13位小数

为什么与文档不保持一致呢?

答案其实很简单, 因为文档的精度, 说明的是绝对准确的位数, 而打印的数据后面几位可能并不是绝对的精准, 显示只是在不精准之后, 恰好”精准”了

如果数据变了, 可能打印出来的”精度”可能就会变化, 但 3``6``15位的精度是绝对的

所以使用浮点数时, 要按照精度进行使用

精度并不仅表示小数位精度, 而是指整个完整的浮点数, 包括整数部分和小数部分

NOTE

仓颉中, 使用printprintln直接打印浮点类型数据, 默认最多保留6位小数:

let value = 123.1234567890
println(value)

但可以通过FloatN.format()接口, 调整输出小数位

不过在使用前需要导入std.convert.Formattable接口

import std.convert.Formattable
let value = 123.1234567890
println(value.format(".10"))

浮点类型字面量#

浮点类型字面量有两种进制表示形式: 十进制、十六进制

在十进制表示中, 一个浮点字面量至少要包含一个整数部分或一个小数部分, 没有小数部分时必须包含指数部分(以eE为前缀, 底数为10)

在十六进制表示中, 一个浮点字面量除了至少要包含一个整数部分或小数部分(以0x0X为前缀), 同时必须包含指数部分(以pP为前缀, 底数为2)

下面的例子展示了浮点字面量的使用:

let a: Float32 = 3.14 // a is 3.140000 with type Float32
let b: Float32 = 2e3 // b is 2000.000000 with type Float32
let c: Float32 = 2.4e-1 // c is 0.240000 with type Float32
let d: Float64 = .123e2 // d is 12.300000 with type Float64
let e: Float64 = 0x1.1p0 // e is 1.062500 with type Float64
let f: Float64 = 0x1p2 // f is 4.000000 with type Float64
let g: Float64 = 0x.2p4 // g is 2.000000 with type Float64

在使用十进制浮点数字面量时, 可以通过加入后缀来明确浮点数字面量的类型, 后缀与类型的对应为:

后缀类型
f16Float16
f32Float32
f64Float64

加入了后缀的浮点数字面量可以像下面的方式来使用:

let a = 3.14f32 // a is 3.140000 with type Float32
let b = 2e3f32 // b is 2000.000000 with type Float32
let c = 2.4e-1f64 // c is 0.240000 with type Float64
let d = .123e2f64 // d is 12.300000 with type Float64

仓颉的浮点类型字面量, 除了一般的十进制小数之外, 十进制和十六进制浮点数均可分为: 整数部分、小数部分和指数部分, 指数部分与前两部分相乘

十进制, 还可以使用e/E表示指数部分(没有小数部分时, 指数部分为必须), eN表示以10为底的N次幂, N为整数

十六进制, 则必须存在使用p/P表示指数部分, pN表示以2为底的N次幂, N为整数

浮点类型支持的操作#

浮点类型默认支持的操作符包括: 算术操作符、关系操作符、复合赋值操作符

浮点类型不支持自增和自减操作符

浮点类型之间、浮点类型和整数类型之间可以互相转换, 具体的类型转换语法及规则请参见数值类型之间的转换

布尔类型#

布尔类型使用Bool表示, 用来表示逻辑中的真和假

布尔类型字面量#

布尔类型只有两个字面量:truefalse

下面的例子展示了布尔字面量的使用:

let a: Bool = true
let b: Bool = false

布尔类型支持的操作#

布尔类型支持的操作符包括: 逻辑操作符(逻辑非!, 逻辑与&&, 逻辑或||)、部分关系操作符(==!=)、部分复合赋值操作符(&&=||=)

布尔类型支持的操作, 基本都是关系和逻辑操作符

布尔类型禁止与其他类型发生类型转换