我的Go+语言初体验——(7)Go+ 分数型有理数数据类型

YouCans 社区中级贡献者
Python领域优质创作者
2021-12-11 15:23:28

我的Go+语言初体验——(7)Go+ 分数型有理数数据类型

“我的Go+语言初体验” | 征文活动进行中…

> Go+ 语言使用后缀 'r' 表示有理数,支持分数型有理数数据类型。分数型有理数的定义、运算比较复杂,本文进行了较为系统的测试。
> 分数型有理数本质上是分子、分母均为整型有理数的数据结构。对分数型有理数变量赋值,可以分子为整型数而分母为整型有理数,或分子为整型有理数而分母为整型数,或分子和分母都是整型有理数。
> 分数型有理数变量可以与分数型有理数类型的常数或变量进行加减乘除运算,也可以与整型常数、整型有理数常数进行混合运算,不能与其它类型变量进行混合运算。
> 由于有理数常量的表示中带有 "r" 和 "<<" 符号,要特别注意其前后运算符号、常数的结合关系、运算优先次序。推荐使用括号 "()" 将其与前后表达式进行隔离,以减少出错。


1. 有理数数据类型

有理数是整数和分数的集合,整数可以表示为分母为 1 的分数,因此可以用分数来表示任意的有理数。

Go+ 语言支持有理数(Rational number)数据类型,使用后缀 'r' 表示有理数,支持整型、分数型、浮点型三种有理数数据类型:

bigint:整型有理数。支持 64位以上的大整数,例如 "1r<<65" 的值等于 2 的 65 次幂,"1r<<65 - 100" 的值等于 2 的 65 次幂减去 100 的结果。

bigrat:分数型有理数。支持分数形式表示的有理数,例如 "4/5r" 的值等于分数 4/5。

bigfloat:浮点型有理数0支持浮点数形式表示的有理数。

分数型有理数本质上是分子、分母均为整型有理数的数据结构。

 

2. 分数型有理数的变量声明与赋值

2.1 变量声明与赋值

Go+ 语言使用 var 关键字声明变量,分数型有理数(bigrat)的变量声明方法如下:

var varname bigrat // 声明变量 varname 为 bigrat 类型

Go+ 语言允许对分数型有理数变量只声明不赋值,但系统并不自动为其设置初始值,如果直接调用将发生错误,因此推荐在对分数型有理数变量声明时赋初始值。

分数型有理数本质上是分子、分母均为整型有理数的数据结构。因此,对分数型有理数赋值,可以分子为整型而分母为整型有理数,或分子为整型有理数而分母为整型,或分子和分母都是整型有理数。

 

【例程 1】变量声明

import "reflect"
​
var brat1 bigrat = 4/5r
var brat2 bigrat = 4r / 5
var brat3 bigrat = (1r << 36) / (3r << 65)
​
var brat4 bigrat // 这句没有报错,但不推荐使用
​
println reflect.TypeOf(brat1), ", brat1 =: ", brat1
println reflect.TypeOf(brat2), ", brat2 =: ", brat2
println reflect.TypeOf(brat3), ", brat3 =: ", brat3
println reflect.TypeOf(brat4), ", brat4 =: ", brat4 // brat4 尚未赋初值,这句发生错误
​
/* Running results:
builtin.Gop_bigrat , brat1 =:  4/5
builtin.Gop_bigrat , brat2 =:  4/5
builtin.Gop_bigrat , brat3 =:  1/1610612736
builtin.Gop_bigrat , brat4 =:  %!v(PANIC=String method: runtime error: invalid memory address or nil pointer dereference)
*/

程序说明:

(1)在分数型有理数变量声明时,可以同时对变量赋值,如 brat1~brat3。
(2)对分数型有理数变量赋值,可以分子为整型而分母为整型有理数(如brat1),或分子为整型有理数而分母为整型(如brat2),或分子和分母都是整型有理数(如brat3)。
(3)对分数型有理数变量赋值,系统自动对分数表达式通分化简,如 brat3。注意 "(1r << 36) / (3r << 65)" 不能写成 "1r << 36 / 3r << 65",后者是非法的。
(4)对分数型有理数变量 brat4 只声明而没有赋值,系统并不会对变量 brat2 设置初始值,如果直接使用 brat2 将发生错误。

 

2.2 不指定变量类型

Go+ 语言允许不通过 var 声明变量,在不指定变量类型而直接对变量进行初始化并赋值,编译器将自动推导变量类型。

  • 如果初始化的数值为整数且没有后缀 'r',系统将其识别为整型变量 int;

  • 如果初始化的数值表达为分数形式且没有后缀 'r',系统将对其取整后识别为整型变量 int;

  • 如果初始化的数值为整数且带有后缀 'r',系统将其识别为整型有理数变量 bigint;

  • 如果初始化的数值表达为分数形式,且带有后缀 'r',系统都将其识别为分数型有理数变量 bigrat;

  • 如果初始化的数值表达为分数形式的表达式,且带有后缀 'r',系统自动对分数表达式进行运算并化简。

【例程 2】变量初始化时不指定类型

// Example 2: variable initialization without specified type
import "reflect"
​
brat3 := 36
brat4 := 6 / 5
brat5 := 306r
brat6 := -4/16r
brat7 := -4/1r
brat8 := 0/1r
brat9 := 1r<<20 + 4/5r
brat10 := -(1r<<15 + 4) / (5r<<25 - 15)
​
println reflect.TypeOf(brat3), ", brat3 =: ", brat3
println reflect.TypeOf(brat4), ", brat4 =: ", brat4
println reflect.TypeOf(brat5), ", brat5 =: ", brat5
println reflect.TypeOf(brat6), ", brat6 =: ", brat6
println reflect.TypeOf(brat7), ", brat7 =: ", brat7
println reflect.TypeOf(brat8), ", brat8 =: ", brat8
println reflect.TypeOf(brat9), ", brat9 =: ", brat9
println reflect.TypeOf(brat10), ", brat10 =: ", brat10
​
/* Running results:
int , brat3 =:  36
int , brat4 =:  1
builtin.Gop_bigint , brat5 =:  306
builtin.Gop_bigrat , brat6 =:  -1/4
builtin.Gop_bigrat , brat7 =:  -4/1
builtin.Gop_bigrat , brat8 =:  0/1
builtin.Gop_bigrat , brat9 =:  5242884/5
builtin.Gop_bigrat , brat10 =:  -32772/167772145
*/

程序说明:

(1)赋值的数值不带后缀 'r',编译器识别变量为整型变量 int,如 brat3, brat4。
(2)赋值的数值表示为整数形式,带有后缀 'r',编译器识别变量为整型有理数变量 bigint,如 brat5。
(3)赋值的数值表示为分数形式,带有后缀 'r',识别变量为分数型有理数变量 bigrat,如 brat6~brat10。
(4)赋值的数值表示为分数 表达式,编译器自动对编译器进行计算、化简。

 

2.3 用表达式对变量赋值

Go+ 语言允许在通过 var 声明变量时对变量赋值,也允许不指定变量类型而直接对变量进行初始化并赋值,这两种情况下都可以使用表达式对变量赋值。

  • 使用表达式对变量赋值,编译器自动对表达式进行计算;

  • 使用表达式对变量赋值,编译器自动对表达式的计算结果进行通分化简,使分子分母没有公约数;

  • 使用表达式对变量赋值,允许使用整型常数、整型有理数常数,与分数型有理数常数或变量混合运算表达式。

【例程 3】使用表达式对分数型有理数变量赋值

// Example 3: assigning values to variables using expressions
import "reflect"
​
var brat1 bigrat = 4/5r
var brat2 bigrat = 0/1r
var brat3 bigrat = brat1 - 16/4r
var brat4 bigrat = 1080 + 3/6r
var brat5 bigrat = 1080r + 3/6r
var brat6 bigrat = 1080 + 0/1r
var brat7 bigrat = 1r<<36 + 6/10r
var brat8 bigrat = (1r<<100 - 1) / (5r<<36 + 1)
​
println reflect.TypeOf(brat1), ", brat1 =: ", brat1
println reflect.TypeOf(brat2), ", brat2 =: ", brat2
println reflect.TypeOf(brat3), ", brat3 =: ", brat3
println reflect.TypeOf(brat4), ", brat4 =: ", brat4
println reflect.TypeOf(brat5), ", brat5 =: ", brat5
println reflect.TypeOf(brat6), ", brat6 =: ", brat6
println reflect.TypeOf(brat7), ", brat7 =: ", brat7
println reflect.TypeOf(brat8), ", brat8 =: ", brat8
​
/* Running results:
builtin.Gop_bigrat , brat1 =:  4/5
builtin.Gop_bigrat , brat2 =:  0/1
builtin.Gop_bigrat , brat3 =:  -16/5
builtin.Gop_bigrat , brat4 =:  2161/2
builtin.Gop_bigrat , brat5 =:  2161/2
builtin.Gop_bigrat , brat6 =:  1080/1
builtin.Gop_bigrat , brat7 =:  343597383683/5
builtin.Gop_bigrat , brat8 =:  422550200076076467165567735125/114532461227
*/

程序说明:

(1)可以用表达式对分数型有理数变量进行赋值,表达式可以混合使用带有后缀 'r' 的整型有理数常数和不带后缀 'r' 的整型常数。
(2)编译器自动对表达式进行计算,如 brat3~brat8。
(3)编译器自动对表达式的计算结果进行通分化简,使分子分母没有公约数,如 brat4, brat5, brat7。
(4)使用表达式对分数型有理数变量赋值,允许使用带有整型常数、整型有理数常数的混合运算表达式,如 brat4~brat6。
(5)使用表达式对分数型有理数变量赋值,允许使用带有分数型有理数常数或变量的表达式,如 brat3, brat8。


 

3. 分数型有理数的运算

分数型有理数变量可以与分数型有理数类型的常数或变量进行加减乘除运算,也可以与整型常数、整型有理数常数进行混合运算,不能与其它类型变量进行混合运算。

3.1 分数型有理数运算式接受的数据类型

分数型有理数的运算表达式中,

  • 运算式中允许使用整型常数、整型有理数常数;

  • 运算式中允许使用分数型有理数常数,允许使用分数型有理数变量;

  • 运算式中允许使用 "分数",不允许使用浮点数常数或变量。

注意:Go+ 语言中并没有 "分数" 数据类型。虽然 "4/5" 看起来好像是一个分数,实际上这是一个整数表达式:整数 4 除以 整数 5。

【例程 4】分数有理数计算式接受的数据类型

// Example 4: Accepted data type in operation of fractional rational numbers
​
import "reflect"
​
var brat1 bigrat = 4/5r
var brat2 bigrat = (1r<<100 - 1) / 12345
var brat3 bigrat = (1r<<100 - 1) / (5r<<36 + 1)
println reflect.TypeOf(brat1), ", brat1 =: ", brat1
println reflect.TypeOf(brat2), ", brat2 =: ", brat2
println reflect.TypeOf(brat3), ", brat3 =: ", brat3
​
brat4 := brat1 + 1024
brat5 := brat1 + 4/5
brat6 := brat1 + 1r<<65
brat7 := brat1 + 306/1025r
brat8 := brat1 + brat2
​
println reflect.TypeOf(brat4), ", brat4 =: ", brat4
println reflect.TypeOf(brat5), ", brat5 =: ", brat5
println reflect.TypeOf(brat6), ", brat6 =: ", brat6
println reflect.TypeOf(brat7), ", brat7 =: ", brat7
println reflect.TypeOf(brat8), ", brat8 =: ", brat8
​
/*
builtin.Gop_bigrat , brat1 =:  4/5
builtin.Gop_bigrat , brat2 =:  84510040015215293433113547025/823
builtin.Gop_bigrat , brat3 =:  422550200076076467165567735125/114532461227
builtin.Gop_bigrat , brat4 =:  5124/5
builtin.Gop_bigrat , brat5 =:  4/5
builtin.Gop_bigrat , brat6 =:  184467440737095516164/5
builtin.Gop_bigrat , brat7 =:  1126/1025
builtin.Gop_bigrat , brat8 =:  422550200076076467165567738417/4115
*/

 

【例程 5】分数有理数运算不接受的数据类型

// Example 5: Unaccepted data type in operation of fractional rational numbers
​
import "reflect"
​
var xint int = 1024
var ybint bigint = 1r << 65
var brat1 bigrat = 1024 / (5r << 36)
​
brat4 := brat1 + 0.8 // 报错
//invalid operation: brat1 + 0.8 (mismatched types github.com/goplus/gop/builtin.Gop_bigrat and untyped float)
​
brat5 := xint + 320/7027r // 报错
//invalid operation: xint + 320/7027r (mismatched types int and github.com/goplus/gop/builtin.Gop_untyped_bigrat)
​
var brat6 bigrat = ybint + 320/7027r // 报错
//panic: gox.fatalMsg("don't call End(), please use EndInit() instead") [recovered]
​
brat7 := ybint + 320/7027r                          // 这句没有报错,但不推荐使用
println reflect.TypeOf(brat7), ", brat7 =: ", brat7 // 这句报错
//panic: TODO: can't init bigint from bigrat

程序说明:

(1)分数型有理数的运算表达式,不允许使用浮点数常数或变量,如 brat4 的运算式是错误的。
(2)分数型有理数的运算表达式,不允许整型变量与分数分数型有理数运算,如 brat5 的运算式是错误的。
(3)分数型有理数变量申明时使用表达式赋值,不允许使用整型有理数变量,如 brat6 是错误的。
(4)在分数型有理数的运算表达式中,使用整型有理数变量与分数型有理数常数进行运算,虽然没有直接报错,但计算结果是不合法的,因此也要避免使用。

说明:关于分数型有理数的数据类型,有些地方容易混淆,下文将进行进一步的分析。

 

3.2 分数型有理数的四则运算

分数型有理数的基本运算,如加减乘除,其计算方法和规则与通常的四则运算相同,运算结果表示为通分化简的分数型有理数。

【例程 6】分数型有理数的基本运算

// Example 6: : Basic operations with constants of rational numbers
import "reflect"
​
var brat1 bigrat = 4/5r
var brat2 bigrat = (1r<<100 - 1) / 12345
var brat3 bigrat = (1r<<100 - 1) / (5r<<36 + 1)
​
println "brat1 + brat2 = ", brat1+brat2
println "brat1 - brat2 = ", brat1-brat2
println "brat1 * brat2 = ", brat1*brat2
println "brat1 / brat2 = ", brat1/brat2
​
println "brat1 + brat2 / brat3 = ", brat1+brat2/brat3
println "(brat1 + brat2) / brat3 = ", (brat1+brat2)/brat3
println "brat1 + (brat2 / brat3) = ", brat1+(brat2/brat3)
​
/* Running results:
brat1 + brat2 =  422550200076076467165567738417/4115
brat1 - brat2 =  -422550200076076467165567731833/4115
brat1 * brat2 =  67608032012172234746490837620/823
brat1 / brat2 =  3292/422550200076076467165567735125
brat1 + brat2 / brat3 =  114532464519/4115
(brat1 + brat2) / brat3 =  48395714406674320425927525589687130857659/1738794073313054662386311230039375
brat1 + (brat2 / brat3) =  114532464519/4115
*/

程序说明:

(1)分数型有理数的基本运算,如加减乘除,其计算方法和规则与通常的四则运算相同,运算结果表示为通分化简的分数型有理数。
(2)分数型有理数的基本运算,按照先乘除、后加减的顺序进行运算。
(3)分数型有理数运算式的运算结果,表示为通分化简的分数型有理数。

 

3.3 分数型有理数变量与整型变量的混合运算

Go+ 语言中分数型有理数变量可以与整型变量进行混合运算,运算结果为分数有理数;分数型有理数不能与整型有理数进行混合运算。

【例程 7】分数型有理数变量与整型变量的运算

// Example 7: Mixed operation of fractional rational number variable and integer variable
var xint int = 1024
var ybint bigint = 1r << 36
​
var brat1 bigrat = 4/5r
var brat2 bigrat = (1r<<100 - 1) / 12345
var brat3 bigrat = (1r<<100 - 1) / (5r<<36 + 1)
​
println brat1+xint   // brat1 + xint =  5124/5
println brat2-2*xint // brat2 - 2*x =  84510040015215293433111861521/823
println brat1*xint   // brat1 * xint =  4096/5
println brat2/xint   // brat2 /xint =  84510040015215293433113547025/842752
​
println brat1+ybint  // 错误:invalid operation
println brat2-2*ybint // 错误:invalid operation
println brat1*ybint  // 错误:invalid operation
println brat2/2*ybint // 错误:invalid operation

程序说明:

(1)分数型有理数变量可以与整型变量进行混合运算,运算结果为分数有理数。
(2)分数型有理数不能与整型有理数进行混合运算——这一点有些意外,也需要特别注意。

 

3.4 分数型有理数常量与整型变量的混合运算

分数型有理数常量与整型变量、整型有理数变量进行混合运算,往往容易发生类型错误,建议尽量避免。

【例程 8】整型有理数与浮点数的运算

// Example 8: Mixed operation of fractional rational number constant and integer variable
​
var xint int = 1024
var ybint bigint = 1r << 36
var brat1 bigrat = 4/12345r
​
println brat1+xint+320/7027r //3978508/86748315
println xint+320/7027r //错误:invalid operation: xint+320/7027r
​
println brat1-ybint+320/7027r //错误:invalid operation: brat1-ybint
println ybint+320/7027r //错误:panic: TODO: can't init bigint from bigrat

 

3.5 分数型有理数常量与整型常量的混合运算

分数型有理数常量可以与整型常量、整型有理数常量进行混合运算,运算结果为分数型有理数。

一般来说,分数型有理数常量不能与浮点数常量进行混合运算;特殊情况如果浮点数的值是整数,可以自动转换为整型,可以运算,但鉴于这种混合运算很容易发生类型错误,建议尽量避免。

【例程 9】整型有理数与浮点数的运算

// Example 9: Mixed operation of fractional rational number constant and integer constant

println 320/7027r+1024     // 7195968/7027
println 320/7027r*1024/5   // 65536/7027
println 320/7027r*1024/5.0 // 65536/7027
//println 320/7027r*1024/5.5  // 错误:invalid operation: 320/7027r*1024/5.5

println 320/7027r+1024r     // 7195968/7027
println 320/7027r*1024r/5   // 65536/7027
println 320/7027r*1024r/5.0 // 65536/7027
//println 320/7027r*1024r/5.5 // 错误:invalid operation: 320/7027r*1024r/5.5

//println 320/7027r*(1r<<24)/5.0 // 1073741824/7027
//println 320/7027r*(1r<<24)/5.5 // 错误:invalid operation: 320/7027r*(1r<<24)/5.5

println 320/7027r+1r<<24   // 117893497152/7027
println 320/7027r*(1r<<24) // 5368709120/7027
//println 320/7027r*1r<<24   // 错误:invalid operation: 320/7027r*1r<<24

println 320/7027r+1r<<24/5   // 117893498432/35135
println 320/7027r+1r<<(24/5) // 112752/7027

程序说明:

(1)由于有理数常量的表示中带有 "r" 和 "<<" 符号,要特别注意其前后运算符号、常数的结合关系、运算优先次序。例如,"320/7027r*(1r<<24)"可以正确运算,而"320/7027r*1r<<24" 发生错误。

(2)注意由于运算优先次序的不同,"320/7027r+1r<<24/5"与"320/7027r+1r<<(24/5)"的结果不同。

因此,对于整型有理数常数和分数型有理数常数,推荐使用括号 "()" 将其与前后表达式进行隔离,以减少出错。

 


 

4. 总结

  • Go+ 语言使用后缀 'r' 表示有理数,支持分数型有理数数据类型(Rational number)。

  • Go+ 语言允许在通过 var 声明变量时对变量赋值,也允许不指定变量类型而直接对变量进行初始化并赋值,这两种情况下都可以使用表达式对变量赋值。

  • Go+ 语言允许对分数型有理数变量只声明不赋值,但系统并不自动为其设置初始值,如果直接调用将发生错误,因此推荐在对分数型有理数变量声明时赋初始值。

  • 分数型有理数本质上是分子、分母均为整型有理数的数据结构。对分数型有理数变量赋值,可以分子为整型数而分母为整型有理数,或分子为整型有理数而分母为整型数,或分子和分母都是整型有理数。

  • 对分数型有理数变量赋值,系统自动对表达式进行运算和通分化简。

  • 分数型有理数变量可以与分数型有理数类型的常数或变量进行加减乘除运算,也可以与整型常数、整型有理数常数进行混合运算,不能与其它类型变量进行混合运算。

  • 分数型有理数变量可以与整型变量进行混合运算,运算结果为分数有理数;分数型有理数不能与整型有理数进行混合运算。

  • 分数型有理数常量可以与整型常量、整型有理数常量进行混合运算,运算结果为分数型有理数。

  • 由于有理数常量的表示中带有 "r" 和 "<<" 符号,要特别注意其前后运算符号、常数的结合关系、运算优先次序。推荐使用括号 "()" 将其与前后表达式进行隔离,以减少出错。

     

5. 附录:出错的代码汇总

以下是本文中出现程序错误的代码,供大家参考。

// 错误代码汇总

var brat4 bigrat // 这句没有报错,但不推荐使用

brat4 := brat1 + 0.8 // 报错:invalid operation
brat5 := xint + 320/7027r // 报错:invalid operation
brat6 := ybint + 320/7027r  // 这句没有报错,但不推荐使用

var brat6 bigrat = ybint + 320/7027r // 报错:panic

println brat1+ybint  // 错误:invalid operation
println brat2-2*ybint // 错误:invalid operation
println brat1*ybint  // 错误:invalid operation
println brat2/2*ybint // 错误:invalid operation

println xint+320/7027r //错误:invalid operation: xint+320/7027r
println brat1-ybint+320/7027r //错误:invalid operation: brat1-ybint
println ybint+320/7027r //错误:panic: TODO: can't init bigint from bigrat

println 320/7027r*1024/5.5  // 错误:invalid operation: 320/7027r*1024/5.5

println 320/7027r*1024r/5.0 // 这句没有报错,但不推荐使用
println 320/7027r*1024r/5.5 // 错误:invalid operation: 320/7027r*1024r/5.5

println 320/7027r*(1r<<24)/5.0 // 这句没有报错,但不推荐使用
println 320/7027r*(1r<<24)/5.5 // 错误:invalid operation: 320/7027r*(1r<<24)/5.5

println 320/7027r*(1r<<24) // 正确:5368709120/7027
println 320/7027r*1r<<24   // 错误:invalid operation: 320/7027r*1r<<24

 


【本节完】

 

版权声明:

原创作品,转载必须标注原文链接

Copyright 2021 youcans, XUPT

Crated:2021-12-10

 

欢迎关注『我的Go+语言初体验』系列,持续更新中…

> [我的Go+语言初体验——(1)超详细安装教程](https://blog.csdn.net/youcans/article/details/121584358)
> [我的Go+语言初体验——(2) IDE 详细安装教程](https://blog.csdn.net/youcans/article/details/121601845)
> [我的Go+语言初体验——(3)Go+ 数据类型](https://blog.csdn.net/youcans/article/details/121619284)
> [我的Go+语言初体验——(4)零基础学习 Go+ 爬虫](https://blog.csdn.net/youcans/article/details/121644252)
> [我的Go+语言初体验——(5)Go+ 基本语法之 Switch](https://blog.csdn.net/youcans/article/details/121722748)
> [我的Go+语言初体验——(6)整型有理数数据类型](https://blog.csdn.net/youcans/article/details/121755262)

...全文
297 1 打赏 收藏 举报
写回复
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
xushiweizh 2021-12-11
  • 打赏
  • 举报
回复
写得挺好
相关推荐
发帖
Go+ 开发者社区

906

社区成员

Go+ 官方开发者社区。我们希望向广大的开发者和数据科学家介绍 Go+ 的定位和意义,并邀请更多开发者一起贡献代码、共建 Go+ 生态。 Go+ 官网:https://goplus.org/
其他 企业社区
社区管理员
  • Go+
  • 杨东杰
加入社区
帖子事件
创建了帖子
2021-12-11 15:23
社区公告

本社区为 Go+ 官方开发者社区。我们希望向广大的开发者和数据科学家介绍 Go+ 的定位和意义,并邀请更多开发者一起贡献代码、共建 Go+ 生态。

Go+ 官网:https://goplus.org/
GitHub地址:https://github.com/goplus/gop