0.12.14.1 2026-11-02 calvin
* 完善了文档中的对象函数的异常
* 修正了一些异常的设置错误
* 引入打包成安装程序交付
0.12.14.0 2026-10-29 calvin
* 支持声明变量时非缺省构造+赋值引用
* 修复了定点小数格式化打印的一个BUG
* 修复了定点小数的标准输入输出的一些BUG
0.12.13.1 2026-10-27 calvin
* 修复了database库各数据库驱动的一些BUG
0.12.13.0 2026-10-26 calvin
* VS2019工程集中增加dbdriver_postgresql、dbdriver_sqlite工程
0.12.12.0 2026-10-22 calvin
* database库和相关驱动新增支持tiny、fixed类型
* 修复了database库MySQL驱动的blob类型处理中的BUG
0.12.11.0 2026-10-16 calvin
* 重构数学库math代码以提高性能
* 数学函数支持定点小数了
* 数据库新增函数DegreeToRadian、RadianToDegree
0.12.10.0 2026-10-15 calvin
* fixed、ufixed新增函数FormatString、IsBetween、Rescale,新增四舍五入功能
0.12.9.0 2026-10-13 calvin
* 现在允许声明全局基本变量了
* 基本类型库新增定点小数类型fixed、ufixed,可以派生金额、利率等对象
0.12.8.1 2026-10-12 calvin
* 解决了一些问题
定点小数
定点小数类型
几乎所有业务系统都绕不开金额、费率、计息、对账,而编程语言中的 float/double 二进制浮点数类型天然存在小数无法精确表示的问题,是对账差错的经典来源。
zlang预置了定点小数类型fixed,即固定小数点后位数的小数类型,提供给开发者在需要固定小数位数的场景,比如金融、传感器采集等。
声明(克隆)一个定点小数对象:
fixed fx ;
未设置小数位数的声明对象会自动设置位数为自动(首次赋值数据时会自动根据数据小数位数设置位数)。
声明时直接显式设置小数位数:
fixed fx(2) ;
未设置舍入模式会自动设置为不舍入/截断(fixed.ROUNDMODE_NONE)。
声明时直接显式设置小数位数与舍入模式:
fixed fx(2,fixed.ROUNDMODE_HALF_UP) ;
声明后可以调用void Rescale(int);来后续设置小数位数;声明后可以调用void SetRoundMode(int);来后续设置舍入模式。
对定点小数的四则运算和其它基本类型一样,直接用+、-、*、/、%即可。
-
示例代码:
fixed-point-number.z
import stdtypes stdio ;
function int main( array args )
{
fixed fx1(2) = 12.34 ;
fixed fx2(2) = 56.78 ;
fixed fx3(2) = 34.56 ;
fixed fx4(2) = 78.90 ;
fixed fx5(2) ;
fx5 = fx1 - fx2 * fx3 / fx4 ;
stdout.Println( fx5 );
return 0;
}
执行
$ zlang fixed-point-number.z
-12.53
-
示例代码:
calc-time-deposit-interest.z,该示例演示了一年期存款利息计算。 -
根据利息计算公式:
利息 = 本金 * 年利率
import stdtypes stdio ;
fixed amount(2,fixed.ROUNDMODE_HALF_UP);
fixed rate(4);
function int main( array args )
{
amount deposit_balance = 5678.00 ;
rate deposit_rate = 1.2/100 ;
amount deposit_interest ;
deposit_interest = deposit_balance * deposit_rate ;
stdout.Println( deposit_interest );
return 0;
}
执行
$ zlang calc-time-deposit-interest.z
68.14
此外,格式化打印、标准输入输出、数学函数等都直接支持定点小数基础类型,写法和其它基础类型使用无二致。
zlang定点小数类型内部用一个大整数、一个小数位数值来实现。
数据库DDL对应类型及操作
在数据库应用开发中,可以用DECIMAL、NUMERIC等类型定义定点小数类型,比如DECIMAL(12,2),用以存放金额类等数值,zlang语言用fixed基础类型与DDL中的定点小数类型相对应,存取表记录。
-
示例代码:
test_database_insert_1_1.z,该示例附带了建表DDL和插入记录代码操作。
import stdtypes stdio database datetime ;
/* test ddl
MySQL :
CREATE TABLE `mytable` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`my_int8` tinyint(3) DEFAULT NULL,
`my_int16` smallint(6) DEFAULT NULL,
`my_int32` int(11) DEFAULT NULL,
`my_int64` bigint(20) DEFAULT NULL,
`my_float` float DEFAULT NULL,
`my_double` double DEFAULT NULL,
`my_decimal` decimal(12,4) DEFAULT NULL,
`my_char` char(3) DEFAULT NULL,
`my_varchar` varchar(30) DEFAULT NULL,
`my_date` date DEFAULT NULL,
`my_time` time DEFAULT NULL,
`my_datetime` datetime DEFAULT NULL,
`my_timestamp` timestamp NULL DEFAULT NULL,
`my_null` varchar(45) DEFAULT NULL,
`my_blob` blob DEFAULT NULL,
`my_longblob` longblob DEFAULT NULL,
`my_emptyblob` blob DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB ;
PostgreSQL :
CREATE TABLE mytable (
id int NOT NULL,
my_int8 smallint DEFAULT NULL,
my_int16 smallint DEFAULT NULL,
my_int32 int DEFAULT NULL,
my_int64 bigint DEFAULT NULL,
my_float real DEFAULT NULL,
my_double double precision DEFAULT NULL,
my_decimal decimal(12,4) DEFAULT NULL,
my_char char(3) DEFAULT NULL,
my_varchar varchar(30) DEFAULT NULL,
my_date date DEFAULT NULL,
my_time time DEFAULT NULL,
my_datetime timestamp DEFAULT NULL,
my_timestamp timestamp NULL DEFAULT NULL,
my_null varchar(45) DEFAULT NULL,
my_blob bytea DEFAULT NULL,
my_longblob bytea DEFAULT NULL,
my_emptyblob bytea DEFAULT NULL,
PRIMARY KEY (id)
) ;
Sqlite :
$ sqlite3 /tmp/calvindb.db
CREATE TABLE `mytable` (
`id` int(11) NOT NULL,
`my_int8` tinyint(3) DEFAULT NULL,
`my_int16` smallint(6) DEFAULT NULL,
`my_int32` int(11) DEFAULT NULL,
`my_int64` bigint(20) DEFAULT NULL,
`my_float` float DEFAULT NULL,
`my_double` double DEFAULT NULL,
`my_decimal` decimal(12,4) DEFAULT NULL,
`my_char` char(3) DEFAULT NULL,
`my_varchar` varchar(30) DEFAULT NULL,
`my_date` date DEFAULT NULL,
`my_time` time DEFAULT NULL,
`my_datetime` datetime DEFAULT NULL,
`my_timestamp` timestamp NULL DEFAULT NULL,
`my_null` varchar(45) DEFAULT NULL,
`my_blob` blob DEFAULT NULL,
`my_longblob` longblob DEFAULT NULL,
`my_emptyblob` blob DEFAULT NULL,
PRIMARY KEY (`id`)
) ;
*/
function int main( array args )
{
dbsession dbsess ;
dbresult dbres ;
string sql ;
int nret = 0 ;
if( args.Get(1) == "PostgreSQL" )
dbsess = database.Connect( args.Get(1) , "localhost" , 5432 , "calvin" , "calvin" , "calvindb" ) ;
else if( args.Get(1) == "Sqlite" )
dbsess = database.Connect( args.Get(1) , "" , 0 , "" , "" , "/tmp/calvindb.db" ) ;
else
dbsess = database.Connect( "MySQL" , "localhost" , 3306 , "calvin" , "calvin" , "calvindb" ) ;
if( dbsess == null )
{
stderr.FormatPrintln( "connect db failed[%d]-[%d][%s]-[%s]" , database.GetLastErrno() , database.GetLastNativeErrno() , database.GetLastNativeError() , database.GetLastSqlState() );
return 1;
}
if( args.Get(1) == "PostgreSQL" )
sql = "INSERT INTO mytable ( id , my_int8 , my_int16 , my_int32 , my_int64 , my_float , my_double , my_decimal , my_char , my_varchar , my_date , my_time , my_datetime , my_timestamp , my_blob , my_longblob , my_emptyblob ) VALUES( " + datetime.GetNow().SecondStamp().ToString() + " , NULL , 23 , 456 , 7890 , 1.2 , 34.56 , 78.90 , 'ABC' , 'calvin' , TO_DATE('2000-01-02','YYYY-MM-DD') , TO_TIMESTAMP('12:34:56','HH24:MI:SS') , TO_TIMESTAMP('2000-01-02 12:34:56','YYYY-MM-DD HH24:MI:SS') , TO_TIMESTAMP('2000-01-02 12:34:56','YYYY-MM-DD HH24:MI:SS') , 'blob' , 'long-blob' , '' );" ;
else if( args.Get(1) == "Sqlite" )
sql = "INSERT INTO mytable ( id , my_int8 , my_int16 , my_int32 , my_int64 , my_float , my_double , my_decimal , my_char , my_varchar , my_date , my_time , my_datetime , my_timestamp , my_blob , my_longblob , my_emptyblob ) VALUES( " + datetime.GetNow().SecondStamp().ToString() + " , 1 , 23 , 456 , 7890 , 1.2 , 34.56 , 78.90 , 'ABC' , 'calvin' , '2000-01-02' , '12:34:56' , '2000-01-02 12:34:56' , '2000-01-02 12:34:56' , 'blob' , 'long-blob' , '' )" ;
else
sql = "INSERT INTO mytable ( my_int8 , my_int16 , my_int32 , my_int64 , my_float , my_double , my_decimal , my_char , my_varchar , my_date , my_time , my_datetime , my_timestamp , my_blob , my_longblob , my_emptyblob ) VALUES( 1 , 23 , 456 , 7890 , 1.2 , 34.56 , 78.90 , 'ABC' , 'calvin' , STR_TO_DATE('2000-01-02','%Y-%m-%d') , STR_TO_DATE('12:34:56','%H:%i:%S') , STR_TO_DATE('2000-01-02 12:34:56','%Y-%m-%d %H:%i:%S') , STR_TO_DATE('2000-01-02 12:34:56','%Y-%m-%d %H:%i:%S') , 'blob' , 'long-blob' , '' )" ;
dbres = dbsess.Execute( sql ) ;
if( dbres == null )
{
stderr.FormatPrintln( "execute sql[%s] failed[%d]-[%d][%s]-[%s]" , sql , database.GetLastErrno() , database.GetLastNativeErrno() , database.GetLastNativeError() , database.GetLastSqlState() );
return 1;
}
stdout.FormatPrintln( "records %d affected" , dbres.GetAffectedRows() );
stdout.FormatPrintln( "insert id %d" , dbres.GetInsertId() );
dbsess.Disconnect();
return 0;
}
-
示例代码:
test_database_select_row2entiobj_1.z,该示例演示了读出DECIMAL类型字段自动映射到fixed类型的对象属性中。
import stdtypes stdio database datetime ;
object Record
{
int id ;
tiny my_int8 ;
short my_int16 ;
int my_int32 ;
long my_int64 ;
float my_float ;
double my_double ;
fixed my_decimal ;
string my_char ;
string my_varchar ;
string my_varchar2 ;
datetime my_date ;
datetime my_time ;
datetime my_datetime ;
datetime my_timestamp ;
string my_null ;
string my_blob ;
string my_longblob ;
string my_emptyblob ;
}
function int main( array args )
{
try
{
if( args.Length() > 0 && args.Get(1) == "PostgreSQL" )
dbsess = database.Connect( args.Get(1) , "localhost" , 5432 , "calvin" , "calvin" , "calvindb" ) ;
else if( args.Length() > 0 && args.Get(1) == "Sqlite" )
dbsess = database.Connect( args.Get(1) , "" , 0 , "" , "" , "/tmp/calvindb.db" ) ;
else
dbsess = database.Connect( "MySQL" , "localhost" , 3306 , "calvin" , "calvin" , "calvindb" ) ;
sql = "SELECT * FROM mytable" ;
dbres = dbsess.Execute( sql ) ;
row_count = dbres.GetRowCount() ;
for( r = 1 ; r <= row_count ; r++ )
{
Record o ;
b = dbres.RowToEntityObject( r , <object>o ) ;
if( b != true )
{
stdout.Println( "RowToObject failed" );
break;
}
stdout.FormatPrintln( "--- ROW %d ---" , r ) ;
stdout.FormatPrintln( " id : %?" , o.id );
stdout.FormatPrintln( " my_int8 : %?" , o.my_int8 );
stdout.FormatPrintln( " my_int16 : %?" , o.my_int16 );
stdout.FormatPrintln( " my_int32 : %?" , o.my_int32 );
stdout.FormatPrintln( " my_int64 : %?" , o.my_int64 );
stdout.FormatPrintln( " my_float : %?" , o.my_float );
stdout.FormatPrintln( " my_double : %?" , o.my_double );
stdout.FormatPrintln( " my_decimal : %?" , o.my_decimal );
stdout.FormatPrintln( " my_char : %s" , o.my_char );
stdout.FormatPrintln( " my_varchar : %s" , o.my_varchar );
stdout.FormatPrint ( " my_date : " ); stdout.Println( o.my_date );
stdout.FormatPrint ( " my_time : " ); stdout.Println( o.my_time );
stdout.FormatPrint ( " my_datetime : " ); stdout.Println( o.my_datetime );
stdout.FormatPrint ( "my_timestamp : " ); stdout.Println( o.my_timestamp );
stdout.FormatPrintln( " my_null : %s" , o.my_null );
stdout.FormatPrintln( " my_blob : %s" , o.my_blob );
stdout.FormatPrintln( " my_longblob : %s" , o.my_longblob );
stdout.FormatPrintln( "my_emptyblob : %s" , o.my_emptyblob );
}
catch
{
if( zexception.GetExceptionId() == zexception.START_COMMONPOOL_FAILED )
{
stdout.FormatPrintln( "connect database failed[%d]-[%d][%s]-[%s]" , database.GetLastErrno() , database.GetLastNativeErrno() , database.GetLastNativeError() , database.GetLastSqlState() );
}
else if( zexception.GetExceptionId() == zexception.EXECUTE_SQL )
{
stdout.FormatPrintln( "INSERT INTO failed[%d]-[%d][%s]-[%s]" , database.GetLastErrno() , database.GetLastNativeErrno() , database.GetLastNativeError() , database.GetLastSqlState() );
}
else
{
stdout.FormatPrintln( "* exception catched" );
stdout.FormatPrintln( "* exception : [%d][%s]" , zexception.GetExceptionId() , zexception.GetExceptionDesc() );
stdout.FormatPrintln( "* exception source : %s:%d,%d" , zexception.GetExceptionSourceFilename() , zexception.GetExceptionSourceRow() , zexception.GetExceptionSourceColumn() );
stdout.FormatPrintln( "* exception function : [%s][%s]" , zexception.GetExceptionObjectName() , zexception.GetExceptionFunctionName() );
stdout.Print( zexception.GetStackTrace() );
}
}
finally
{
dbsess.Disconnect();
stdout.Println( "Disconnect" );
}
}
return 0;
}
执行
$ zlang test_database_select_row2entiobj_1.z
--- ROW 1 ---
id : 1
my_int8 : 1
my_int16 : 23
my_int32 : 456
my_int64 : 7890
my_float : 1.200000
my_double : 34.560000
my_decimal : 78.9000
my_char : ABC
my_varchar : calvin
my_date : 2000-01-02 00:00:00
my_time : 0000-00-00 00:00:00
my_datetime : 2000-01-02 12:34:56
my_timestamp : 2000-01-02 12:34:56
my_null : (null)
my_blob : blob
my_longblob : long-blob
my_emptyblob :
Disconnect
-
示例代码:
test_database_select_row2obj_1.z,或者根据表字段类型自动构造记录对象。
import stdtypes stdio database datetime ;
function int main( array args )
{
try
{
if( args.Length() > 0 && args.Get(1) == "PostgreSQL" )
dbsess = database.Connect( args.Get(1) , "localhost" , 5432 , "calvin" , "calvin" , "calvindb" ) ;
else if( args.Length() > 0 && args.Get(1) == "Sqlite" )
dbsess = database.Connect( args.Get(1) , "" , 0 , "" , "" , "/tmp/calvindb.db" ) ;
else
dbsess = database.Connect( "MySQL" , "localhost" , 3306 , "calvin" , "calvin" , "calvindb" ) ;
sql = "SELECT * FROM mytable" ;
dbres = dbsess.Execute( sql ) ;
row_count = dbres.GetRowCount() ;
for( r = 1 ; r <= row_count ; r++ )
{
o = dbres.RowToObject(r) ;
if( o == null )
{
stdout.Println( "RowToObject failed" );
break;
}
stdout.FormatPrintln( "--- ROW %d ---" , r ) ;
stdout.FormatPrintln( " id : %?" , o.id );
stdout.FormatPrintln( " my_int8 : %?" , o.my_int8 );
stdout.FormatPrintln( " my_int16 : %?" , o.my_int16 );
stdout.FormatPrintln( " my_int32 : %?" , o.my_int32 );
stdout.FormatPrintln( " my_int64 : %?" , o.my_int64 );
stdout.FormatPrintln( " my_float : %?" , o.my_float );
stdout.FormatPrintln( " my_double : %?" , o.my_double );
stdout.FormatPrintln( " my_decimal : %?" , o.my_decimal );
stdout.FormatPrintln( " my_char : %s" , o.my_char );
stdout.FormatPrintln( " my_varchar : %s" , o.my_varchar );
stdout.FormatPrint ( " my_date : " ); stdout.Println( o.my_date );
stdout.FormatPrint ( " my_time : " ); stdout.Println( o.my_time );
stdout.FormatPrint ( " my_datetime : " ); stdout.Println( o.my_datetime );
stdout.FormatPrint ( "my_timestamp : " ); stdout.Println( o.my_timestamp );
stdout.FormatPrintln( " my_null : %s" , o.my_null );
stdout.FormatPrintln( " my_blob : %s" , o.my_blob );
stdout.FormatPrintln( " my_longblob : %s" , o.my_longblob );
stdout.FormatPrintln( "my_emptyblob : %s" , o.my_emptyblob );
}
catch
{
if( zexception.GetExceptionId() == zexception.START_COMMONPOOL_FAILED )
{
stdout.FormatPrintln( "connect database failed[%d]-[%d][%s]-[%s]" , database.GetLastErrno() , database.GetLastNativeErrno() , database.GetLastNativeError() , database.GetLastSqlState() );
}
else if( zexception.GetExceptionId() == zexception.EXECUTE_SQL )
{
stdout.FormatPrintln( "INSERT INTO failed[%d]-[%d][%s]-[%s]" , database.GetLastErrno() , database.GetLastNativeErrno() , database.GetLastNativeError() , database.GetLastSqlState() );
}
else
{
stdout.FormatPrintln( "* exception catched" );
stdout.FormatPrintln( "* exception : [%d][%s]" , zexception.GetExceptionId() , zexception.GetExceptionDesc() );
stdout.FormatPrintln( "* exception source : %s:%d,%d" , zexception.GetExceptionSourceFilename() , zexception.GetExceptionSourceRow() , zexception.GetExceptionSourceColumn() );
stdout.FormatPrintln( "* exception function : [%s][%s]" , zexception.GetExceptionObjectName() , zexception.GetExceptionFunctionName() );
stdout.Print( zexception.GetStackTrace() );
}
}
finally
{
dbsess.Disconnect();
stdout.Println( "Disconnect" );
}
}
return 0;
}
执行
$ zlang test_database_select_row2obj_1.z
--- ROW 1 ---
id : 1
my_int8 : 1
my_int16 : 23
my_int32 : 456
my_int64 : 7890
my_float : 1.200000
my_double : 34.560000
my_decimal : 78.9000
my_char : ABC
my_varchar : calvin
my_date : 2000-01-02 00:00:00
my_time : 1900-01-01 12:34:56
my_datetime : 2000-01-02 12:34:56
my_timestamp : 2000-01-02 12:34:56
my_null : (null)
my_blob : blob
my_longblob : long-blob
my_emptyblob :
Disconnect