仓颉编程语言青少年基础教程:数据类型

发布于:2025-09-03 ⋅ 阅读:(16) ⋅ 点赞:(0)

仓颉编程语言青少年基础教程:数据类型

本篇介绍了仓颉编程语言的数据类型系统,重点阐述了数值类型的分类和数值类型(包括整数类型与浮点数类型),其它类型以后介绍。

概述

了解仓颉编程语言的数据类型,是学习和使用这门语言的基础。仓颉作为一款面向全场景智能的静态类型、强类型语言,它的类型系统在保证程序安全性和表达丰富性方面做了不少设计。

从不同的角度可以进行不同的分类

仓颉编程语言的数据类型分为不可变类型(immutable type)和可变类型(mutable type)。

不可变类型包括数值类型(分为整数类型和浮点数类型)、字符(Rune)类型、Bool 类型、Unit 类型、Nothing 类型、字符串(String)类型、元组(Tuple)类型、区间(Range)类型、函数(Function)类型、enum 类型;可变类型包括 Array 类型、值类型数组(VArray)类型、struct 类型、class 类型和 interface 类型。

不可变类型和可变类型的区别在于:不可变类型的值,其数据值一经初始化后就不会发生变化;可变类型的值,其数据值初始化后仍然有可以修改的方法。

1.不可变类型(immutable)

共性:实例一旦创建,没有任何公开 API 能“原地”改它;所有“修改”操作都返回新实例。

  •  数值

     – 整数:Int8 / Int16 / Int32 / Int64 / UInt8 / UInt16 / UInt32 / UInt64 / Int / UInt

     – 浮点:Float32 / Float64

  •  Rune (字符类型、Unicode 标量值)

  •  Bool (true / false)

  •  Unit (空元组,类似 ())

  •  Nothing (底类型,无值;常用于“永不返回”的函数)

  •  String (UTF-8 不可变字符串)

  •  元组 Tuple

     – 元素个数、类型固定;可整体解构,但无法修改其中任一元素。

  •  Range (区间类型):区间类型是一个泛型,使用 Range<T> 表示

  •  函数类型 Function

     – 函数对象本身不可变;捕获的变量是否可变由捕获语义决定。

  •  enum(枚举类型)

2.可变类型(mutable)

共性:提供 公开 mutating 方法 或 可变字段,允许在同一内存地址上修改数据。

  •  Array<T>(引用类型数组)其中T表示 Array 的元素类型

  •  VArray<T, $N> (值类型数组)

  •  struct

     – 默认所有字段可变;可用 let/var 或 mut 关键字细化字段级可变性。

  •  class

     – 引用语义;实例可变;字段默认可变,可用 private(set) 限制写权限。

  •  interface

     – 描述能力,本身无存储;实现类/实例是否可变由实现决定。

     – 但接口里可以声明 mutating 方法,强制实现者提供可变实现。

也可以分为:基本数据类型、复合数据类型、特殊底层、高级类型

(一)基本数据类型

(1)数值类型

 •  整数类型:Int8(8 位)、Int16(16 位)、Int32(32 位)、Int64(64 位),默认整数类型为Int(根据平台自动适配位数)。

 •  无符号整数:UInt8、UInt16、UInt32、UInt64,适用于非负数值场景(如索引、位运算)。

 •  浮点数:Float32(单精度)、Float64(双精度,默认浮点类型),支持科学计数法(如1.2e3)。

 •  特点:强类型设计,不允许隐式类型转换(如Int与Float需显式转换)。

(2)布尔类型

 •  关键字Bool,取值为true或false,主要用于条件判断。

 •  不支持与整数的隐式转换(如0不能代表false),确保逻辑清晰。

(3)Rune与字符串

 •  Rune:表示单个 Unicode 字符。

 •  字符串(String):不可变序列,用双引号包裹(如"hello"),支持字符串拼接(+)、插值(如"value: ${x}")。

(二)复合数据类型

复合类型用于组织多个数据,支持复杂结构的表达:

(1)元组(Tuple)

(2)数组(Array)

(3)值类型数组(VArray)

(4)结构体(Struct)

(5)枚举(Enum)

(三)特殊类型

(1)Unit(仅有一个值 (),表示“无意义返回值”) 

(2)Nothing(无实例,表示“永不返回”)

(四)高级或其它类型

(1)函数

(2)类(Class)

(3)接口(Interface)

下面将适当展开介绍,先从数值类型说起。

数值类型

数值类型包括整数类型与浮点数类型,分别用于表示整数和浮点数。整数类型包含有符号(signed)整数类型和无符号(unsigned)整数类型,其中,有符号整数类型包括 Int8、Int16、Int32、Int64 和 IntNative,分别用于表示编码长度为 8-bit、16-bit、32-bit、64-bit 和平台相关大小的有符号整数值的类型;无符号整数类型包括 UInt8、UInt16、UInt32、 UInt64 和 UIntNative,分别用于表示编码长度为 8-bit、16-bit、32-bit、 64-bit 和平台相关大小的无符号整数值的类型。浮点数类型包括 Float16、Float32 和 Float64,分别用于表示编码长度为 16-bit、32-bit 和 64-bit 的浮点数的类型。其中IntNative 和UIntNative依赖于平台的(platform dependent).

整数类型

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

有符号整数类型包括 Int8、Int16、Int32、Int64 和 IntNative,分别用于表示编码长度为 8-bit、16-bit、32-bit、64-bit 和IntNative(平台相关大小的有符号整数值的类型)。

无符号整数类型包括 UInt8、UInt16、UInt32、UInt64 和 UIntNative,分别用于表示编码长度为 8-bit、16-bit、32-bit、64-bit 和UIntNative(平台相关大小的无符号整数值的类型)。

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

为了方便使用,仓颉编程语言中特别提供了一些类型别名:

– Byte 类型作为 UInt8 的类型别名,Byte 与 UInt8 完全等价。

– Int/UInt 类型分别作为 Int64/UInt64 的类型别名,Int 与 Int64 完全等价,UInt 与 UInt64 完全等价。

例如:

main(): Int64 {
    let y = 1234567890123456789
    println("y = ${y}")

    let a: UInt8 = 128
    let b: Byte = a // ok
    println("a = ${a}")
    println("b = ${b}")

    let c: Int64 = 9223372036854775807
    let d: Int = c // ok
    println("c = ${c}")
    println("d = ${d}")

    let e: UInt64 = 18446744073709551615
    let f: UInt = e // ok
    println("e = ${e}")
    println("f = ${f}")
    
    return 0
}

编译运行输出:

整数类型字面量

整数类型字面量有 4 种进制表示形式:二进制(使用 0b 或 0B 前缀)、八进制(使用 0o 或 0O 前缀)、十进制(没有前缀)、十六进制(使用 0x 或 0X 前缀)。例如,对于十进制数 24,表示成二进制是 0b00011000(或 0B00011000),表示成八进制是 0o30(或 0O30),表示成十六进制是 0x18(或 0X18)。

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

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

i8    Int8    
i16    Int16    
i32    Int32    
i64    Int64    
u8    UInt8
u16    UInt16
u32    UInt32
u64    UInt64
 

示例:

main(): Int64 {
    // 十进制(默认 Int64)
    let a = 42;            // Int64
    let b = 1_000_000i32;  // Int32 1000000

    // 二进制
    let c = 0b0001_1000;   // Int64  24
    let d = 0B1010_1111u8; // UInt8  175

    // 八进制
    let e = 0o30;          // Int64  24
    let f = 0O7_777u16;    // UInt16 4095

    // 十六进制
    let g = 0x18;          // Int64  24
    let h = 0xFF_u64;      // UInt64 255
    let i = 0XCAFE_BABEu32;// UInt32 0xCAFEBABE

    println("a = ${a}")
    println("b = ${b}")
    println("c = ${c}")
    println("d = ${d}")
    println("e = ${e}")
    println("f = ${f}")
    println("g = ${g}")
    println("h = ${h}")
    println("i = ${i}")
    return 0
}

编译运行输出:

a = 42
b = 1000000   
c = 24        
d = 175       
e = 24        
f = 4095      
g = 24        
h = 255       
i = 3405691582

字符字节字面量

仓颉编程语言支持字符字节字面量,以方便使用 ASCII 码表示 UInt8 类型的值。字符字节字面量由字符 b、一对标识首尾的单引号、以及一个 ASCII 字符组成。

写法固定:b'字符' 或 b'\u{xx}'

类型固定:一定是 UInt8

取值范围:0–255(\u 后最多两位 16 进制)

例如:

main(): Int64 {
    // 1) 直接写可见字符
    let a = b'A'          // 65u8

    // 2) 直接写转义字符
    let b = b'\n'         // 10u8  (换行符)

    // 3) 16 进制转义,等价于 0x41
    let c = b'\u{41}'     // 65u8

    // 4) 16 进制转义,最大合法值
    let d = b'\u{FF}'     // 255u8

    // 5) 表达式里用字节字面量做运算
    let e = b'\u{90}' - b'\u{66}' + b'x'   // 144 - 102 + 120 = 162u8

    // 6) ❌ 超过 0xFF 会报错
    // let bad = b'\u{100}'   // error: byte literal out of range

    println("a = ${a}")
    println("b = ${b}")
    println("c = ${c}")
    println("d = ${d}")
    println("e = ${e}")
    return 0
}

编译运行输出:

a = 65
b = 10 
c = 65 
d = 255
e = 162

整数类型支持的操作

整数类型默认支持的操作符包括:算术操作符、位操作符、关系操作符、自增和自减操作符、复合赋值操作符。各操作符的优先级参见官方文档https://cangjie-lang.cn/docs?url=%2F1.0.0%2Fuser_manual%2Fsource_zh_cn%2FAppendix%2Foperator.html 

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

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

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

    o  % 的操作数只支持整数类型。

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

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

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

2.位操作符包括:按位求反(!)、左移(<<)、右移(>>)、按位与(&)、按位异或(^)、按位或(|)。注意,按位与、按位异或和按位或操作符要求左右操作数是相同的整数类型。

3.关系操作符包括:小于(<)、大于(>)、小于等于(<=)、大于等于(>=)、相等(==)、不等(!=)。要求关系操作符的左右操作数是相同的整数类型。

4.自增和自减操作符包括:自增(++)和自减(--)。注意,仓颉中的自增和自减操作符只能作为一元后缀操作符使用。

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

算术操作符用于执行基本的数学运算,操作数通常为数值类型(整数、浮点数等)。如 a + b 表示 a 与 b 的和。

位操作符直接对数据的二进制位进行操作,操作数通常为整数类型(按二进制位逐位运算)。如:按位与(对应位都为 1 时,结果位为 1,否则为 0;如 3 & 5 即 011 & 101 = 001,结果为 1)。

关系操作符用于比较两个值的关系,结果为布尔值(true 或 false)。如判断两个值是否相等,如 a == b。

自增和自减操作符用于对变量的值进行加 1 或减 1 操作,仓颉中的自增和自减操作符只能作为一元后缀操作符使用:如a++(先使用 a 的值,再将 a 加 1),b--(先使用 b 的值,再将 b 减 1)。

复合赋值操作符将 “操作” 与 “赋值” 结合的简写形式,等价于 “变量 = 变量 操作 右值”。如a += b 等价于 a = a + b。

示例:

// int_ops.cj
main() {
    /* 1. 算术操作符 ------------------------------------------------------- */
    let a: Int32 = 17
    let b: Int32 = 5
    let c: Float64 = 2.5
    let d: Float64 = 4.0

    // 一元负号
    let neg = -a               // -17

    // 同类型整数
    let add  = a + b           // 22
    let sub  = a - b           // 12
    let mul  = a * b           // 85
    let div  = a / b           // 3   (整除)
    let rem  = a % b           // 2   (取模)

    // 浮点混合
    let fmul = c * d           // Float64, 10.0
    let fdiv = c / d           // Float64, 0.625

    // 幂运算
    let powInt  = 2i64 ** 3u64         // Int64, 8
    let powFlt  = 2.0f64 ** 4i64       // Float64, 16.0
    let powFlt2 = 2.0f64 ** 2.5f64     // Float64, ~5.656

    println("算术:")
    println("neg=${neg}, add=${add}, sub=${sub}, mul=${mul}")
    println("div=${div}, rem=${rem}, fmul=${fmul}, fdiv=${fdiv}")
    println("powInt=${powInt}, powFlt=${powFlt}, powFlt2=${powFlt2}")

    /* 2. 位操作符 --------------------------------------------------------- */
    let x: UInt8 = 0b1100_1010u8
    let y: UInt8 = 0b0011_1100u8

    let notX = !x              // 0b0011_0101
    let shl  = x << 2u8        // 0b0010_1000
    let shr  = x >> 3u8        // 0b0001_1001
    let and  = x & y           // 0b0000_1000
    let xor  = x ^ y           // 0b1111_0110
    let or   = x | y           // 0b1111_1110

    println("\n位运算:")
    println("notX=${notX}, shl=${shl}, shr=${shr}")
    println("and=${and}, xor=${xor}, or=${or}")

    /* 3. 关系操作符 ------------------------------------------------------- */
    let p: Int16 = 10
    let q: Int16 = 20
    let smaller  = p < q       // true
    let greater  = p > q       // false
    let equal    = p == q      // false
    let notEqual = p != q      // true

    println("\n关系:")
    println("smaller=${smaller}, greater=${greater}")
    println("equal=${equal}, notEqual=${notEqual}")

    /* 4. 自增 / 自减(只能后缀) ----------------------------------------- */
    var cnt: Int32 = 10
    cnt++                      // 11
    cnt--                      // 10
    println("\ncnt after ++/-- = ${cnt}")

    /* 5. 复合赋值 --------------------------------------------------------- */
    var v: Int64 = 6
    v += 4      // 10
    v -= 3      // 7
    v *= 2      // 14
    v /= 4      // 3
    v %= 3      // 0
    v = 2i64
    v **= 3u64  // 8
    v <<= 1     // 16
    v >>= 2     // 4
    v |= 0b11   // 7
    v &= 0b101  // 5
    v ^= 0b111  // 2
    println("复合赋值后 v = ${v}")
}

运行输出:

算术:
neg=-17, add=22, sub=12, mul=85
div=3, rem=2, fmul=10.000000, fdiv=0.625000 
powInt=8, powFlt=16.000000, powFlt2=5.656854

位运算:
notX=53, shl=40, shr=25
and=8, xor=246, or=254

关系:
smaller=true, greater=false
equal=false, notEqual=true

cnt after ++/-- = 10
复合赋值后 v = 2

整数类型之间、整数类型和浮点类型之间可以互相转换,整数类型可以转换为字符类型。

仓颉不支持不同类型之间的隐式转换(子类型天然是父类型,所以子类型到父类型的转换不是隐式类型转换),类型转换必须显式地进行。示例在后面的浮点类型部分介绍。

浮点类型

浮点类型包括 Float16、 Float32 和 Float64,分别用于表示编码长度为 16-bit、 32-bit 和 64-bit 的浮点数(带小数部分的数字,如 3.14159、8.24 和 0.1 等)的类型。Float16、 Float32 和 Float64 分别对应 IEEE 754 中的半精度格式(即 binary16)、单精度格式(即 binary32)和双精度格式(即 binary64)。

Float64 的精度约为小数点后 15 位,Float32 的精度约为小数点后 6 位,Float16 的精度约为小数点后 3 位。

浮点类型字面量有两种进制表示形式:十进制、十六进制。在十进制表示中,一个浮点字面量至少要包含一个整数部分或一个小数部分,没有小数部分时必须包含指数部分(以 e 或 E 为前缀,底数为 10)。在十六进制表示中,一个浮点字面量除了至少要包含一个整数部分或小数部分(以 0x 或 0X 为前缀),同时必须包含指数部分(以 p 或 P 为前缀,底数为 2)。

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

后缀    类型
f16    Float16
f32    Float32
f64    Float64

示例

// 十进制 & 十六进制浮点字面量示例(符合仓颉 1.0.0 语法)
main() {
    /* 1. 十进制浮点:小数点 + 可选指数 + 后缀 */
    let d1 = 3.14f16
    let d2 = 42e0f32
    let d3 = .5e-1f64

    /* 2. 十六进制浮点:必须带 p/P 指数,后缀放在指数之前 */
    let h1 = 0x1a.3f64P4       // 1a.3f × 2⁴
    let h2 = 0x.7af16P3        // 0x0.7a × 2³
    let h3 = 0x123f64p5        // 0x123 × 2⁵

    println("十进制:")
    println("d1 = ${d1}")   // Float16,d1 = 3.140625 
    println("d2 = ${d2}")   // Float32,d2 = 42.000000
    println("d3 = ${d3}")   // Float64,d3 = 0.050000 
   
    println("十六进制:")
    println("h1 = ${h1}")   // Float64,h1 = 419.961914   
    println("h2 = ${h2}")   // Float16,h2 = 3.841965 
    println("h3 = ${h3}")   // Float64,38268032.000000     
}

编译运行:

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

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

示例

// float_ops.cj
main() {
    let a: Float64 = 3.5
    let b: Float64 = 2.0
    let c: Float32 = 1.5f32
    let d: Float32 = 4.0f32

    /* 1. 算术操作符 ------------------------------------------------------- */
    let neg  = -a               // 一元负号
    let add  = a + b            // 5.5
    let sub  = a - b            // 1.5
    let mul  = a * b            // 7.0
    let div  = a / b            // 1.75
    let pow  = 2.0f64 ** 3      // Float64 的幂运算,8.0
    let pow2 = 3.0f64 ** 2.5f64 // Float64 的幂运算,~15.588

    println("算术:")
    println("neg=${neg}, add=${add}, sub=${sub}")
    println("mul=${mul}, div=${div}, pow=${pow}, pow2=${pow2}")

    /* 2. 关系操作符 ------------------------------------------------------- */
    let lt = a < b              // false
    let gt = a > b              // true
    let le = c <= d             // true
    let ge = c >= d             // false
    let eq = a == b             // false
    let ne = a != b             // true

    println("\n关系:")
    println("lt=${lt}, gt=${gt}, le=${le}, ge=${ge}")
    println("eq=${eq}, ne=${ne}")

    /* 3. 复合赋值操作符 --------------------------------------------------- */
    var x: Float64 = 10.0
    x += 2.5        // 12.5
    x -= 1.0        // 11.5
    x *= 3.0        // 34.5
    x /= 5.0        // 6.9
    x **= 2.0       // 47.61

    println("\n复合赋值后 x = ${x}")

    /* 4. 自增/自减(不支持,以下两行会编译错误) */
    // x++  // ❌ 浮点类型不支持 ++
    // x--  // ❌ 浮点类型不支持 --
}

运行输出:

运行输出:
算术:
neg=-3.500000, add=5.500000, sub=1.500000
mul=7.000000, div=1.750000, pow=8.000000, pow2=15.588457

关系:
lt=false, gt=true, le=true, ge=false
eq=false, ne=true

复合赋值后 x = 47.610000

如何比较两个数值是否相等

类型必须完全一致

    如果类型不同,要先显式转换到同一类型,否则编译器直接报错。

    整数直接 ==

    浮点要近似(容忍误差)判断  |a-b| < ε

示例

import std.math.abs  //绝对值
main() {
    // 1.同类型:直接用 ==
    let a: Int32 = 100
    let b: Int32 = 100
    println(a == b)   // true

    //2。不同类型:先转换再 ==
    let i8: Int8 = 127
    let i64: Int64 = 127
    // 必须显式转换
    println(Int64(i8) == i64)   // true

    let f32: Float32 = 3.1415926
    let f64: Float64 = 3.1415926
    // 转成同一精度
    println(Float64(f32) == f64)   // false(浮点数, 精度不同)
    
    //浮点数可用近似(容忍误差)判断  |a-b| < ε
    println(abs(Float64(f32) - f64) < 1e-9)  // false
    println(abs(Float64(f32) - f64) < 1e-5)  //true 
}

浮点类型之间、浮点类型和整数类型之间可以互相转换。

数值类型转换规则

仓颉不支持不同类型之间的隐式转换(子类型天然是父类型,所以子类型到父类型的转换不是隐式类型转换),类型转换必须显式地进行。

所有数值类型之间必须显式转换,语法统一为 TargetType(expr),不支持任何隐式转换。

例如

源类型

目标类型

提示

Int8 → Int64

Int64(表达式)

小转大,永不过载

Float64 → Float32

Float32(表达式)

精度降低

Float32 → Int32

Int32(表达式)

截断小数

Rune → UInt32

UInt32(表达式)

取 Unicode 码位

UInt32 → Rune

Rune(表达式)

需合法码位

注意:溢出时报错。

精度损失:浮点转整数直接截断小数;高位转低位浮点会舍入。

转换方向

说明

整数 → 整数

小→大:值不变;大→小:溢出时抛异常

整数 → 浮点

按最近舍入得到浮点值,可能损失精度

浮点 → 浮点

按 IEEE-754 最近舍入,精度降低

浮点 → 整数

丢弃小数部分,溢出抛异常

字符 ↔ 整数

仅对合法的 Unicode scalar value

示例:

main() {
    // 整数互转
    let a: Int8  = 10
    let b: Int16 = Int16(a)   // Int8 → Int16

    // 浮点互转
    let c: Float32 = 3.1415926
    let d: Float64 = Float64(c)   // Float32 → Float64

    // 整数 ↔ 浮点
    let e: Int64   = 1024
    let f: Float64 = Float64(e)   // Int64 → Float64
    let g: Int64   = Int64(123.7) // Float64 → Int64,g = 123
    
    //a = 10, b = 10, d = 3.141592502593994, f = 1024.000000, g = 123
    println("a = ${a}, b = ${b}, d = ${d}, f = ${f}, g = ${g}")  

    let big: Int32 = 83647      
    println("big = ${big}")    //big = 83647
    //let small = Int8(big)        //  超出 Int8 范围

}

【类型转换语法及规则官方文档

https://cangjie-lang.cn/docs?url=%2F1.0.0%2Fuser_manual%2Fsource_zh_cn%2Fclass_and_interface%2Ftypecast.html

https://cangjie-lang.cn/docs?url=%2F0.53.18%2FSpec%2Fsource_zh_cn%2FChapter_02_Types%28zh%29.html

一个现象的特别说明

Float16、 Float32 和 Float64 分别对应 IEEE 754 中的半精度格式(即 binary16)、单精度格式(即 binary32)和双精度格式(即 binary64)。Float64 的精度约为小数点后 15 位,Float32 的精度约为小数点后 6 位,Float16 的精度约为小数点后 3 位。

先看示例代码:

main() {
    let a: Float16 = 3.14159265356979323846
    let b: Float32 = 3.14159265356979323846
    let c: Float64 = 3.14159265356979323846

    println("a = ${a}")         // 默认输出 6 位 → a = 3.140625,明显可见Float16 精度不足
    println("b = ${b}")         // 默认输出 6 位 → b = 3.141593,?
    println("c = ${c}")         // 默认输出 6 位 → c = 3.141593,?

    // 放大小数部分差距,取 12 位小数
    let af16 = Int64((Float64(a) - 3.0) * 1e12)
    let bf32 = Int64((Float64(b) - 3.0) * 1e12)
    let cf64 = Int64((c - 3.0) * 1e12)

    println("a 小数部分 ×1e12 = ${af16}")  // a 小数部分 ×1e12 = 140625000000
    println("b 小数部分 ×1e12 = ${bf32}")  // b 小数部分 ×1e12 = 141592741012
    println("c 小数部分 ×1e12 = ${cf64}")  // c 小数部分 ×1e12 = 141592653569
}  

和初始值对比,a = 3.140625明显可见Float16 精度,后两者b = 3.141593和c = 3.141593输出都是3.141593,为什么?

println 在打印浮点数时,默认只保留 6 位有效数字,所以它把 Float32 和 Float64 都四舍五入到 3.141593 后就不再往下显示了;真正的二进制差异被“隐藏”了。明显可见Float16 精度不足。

放大小数部分差距,如取 12 位小数,可以直观地暴露了 Floa16、Float32 与 Float64 的精度差异,示例代码如下:

main() {
    let a: Float16 = 3.14159265356979323846
    let b: Float32 = 3.14159265356979323846
    let c: Float64 = 3.14159265356979323846

    println("a = ${a}")         // 默认输出 6 位 → a = 3.140625,明显可见Float16 精度不足
    println("b = ${b}")         // 默认输出 6 位 → b = 3.141593,?
    println("c = ${c}")         // 默认输出 6 位 → c = 3.141593,?

    // 放大小数部分差距,取 12 位小数
    let af16 = Int64((Float64(a) - 3.0) * 1e12)
    let bf32 = Int64((Float64(b) - 3.0) * 1e12)
    let cf64 = Int64((c - 3.0) * 1e12)

    println("a 小数部分 ×1e12 = ${af16}")  // a 小数部分 ×1e12 = 140625000000
    println("b 小数部分 ×1e12 = ${bf32}")  // b 小数部分 ×1e12 = 141592741012
    println("c 小数部分 ×1e12 = ${cf64}")  // c 小数部分 ×1e12 = 141592653569
} 

对此更多情况参见https://blog.csdn.net/cnds123/article/details/149668610 一文。


网站公告

今日签到

点亮在社区的每一天
去签到