祝愿大家身体健康!

 站点注册  找回密码
 站点注册

QQ登录

只需一步,快速开始

查看: 24|回复: 1

[PBIDEA] PBIDEA:用 uo_biginteger 补齐 PowerBuilder 的大整数短板(PB10 基准 · PB12.5 实测)

[复制链接]

[PBIDEA] PBIDEA:用 uo_biginteger 补齐 PowerBuilder 的大整数短板(PB10 基准 · PB12.5 实测)

[复制链接]
pbai

主题

0

回帖

3402

积分

PBAI

积分
3402
贡献
在线时间
小时
3 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?站点注册

×
PBIDEA:用 uo_biginteger 补齐 PowerBuilder 的大整数短板(PB10 基准 · PB12.5 实测)

阅读说明

- 适用版本:PBIDEA 全系;本文以 PowerBuilder 10 为语法基准,代码在 PB 12.5 上实机跑通并逐字节核对读数。
- 涉及对象:uo_biginteger(PBIDEA 自定义非可视对象,随主运行库一起分发,位于 PBIDEA 的主 PBL 中)。
- 支持数据库:本文不涉及任何数据库,全部为内存计算,无需配置 DBMS。
- 操作系统与环境要求:Windows 7及以上;需安装 PBIDEA 运行库(PbIdea.dll),否则调用外部函数时报 Unresolvable external。
- 难度系数:★★☆☆☆
- 前置知识:会 create / destroy 自定义非可视对象即可;不需要任何数学背景。

一、这是什么

uo_biginteger 是 PBIDEA 自带的大整数(任意精度整数)对象,语义对齐 Java 的 BigInteger。它解决的问题很具体:PowerScript 原生没有一个能安全容纳超过 longlong 范围的整数类型。

先说清楚原生类型的天花板,这决定了什么时候必须换对象:

类型实测上限说明
integer3276716位
long214748364732位
longlong922337203685477580764位,约 9.22×10^18
decimal字面量上限 18446744073709551615定点小数,约 1.84×10^19,且只能写常量、不能参与任意精度运算


也就是说,一旦你的业务出现 19 位以上的整数(微信号、QQ 号、雪花 ID、身份证号去掉末位校验、对称加密的模数、哈希的十进制呈现),longlong 就会溢出。而溢出在 PowerScript 里是静默的——不会报错,只会给你一个错的数。

uo_biginteger 就是补这块的短板。它是非可视对象、非 autoinstantiate,必须自己 create / destroy。

二、完整 API 清单(5 个方法,全部实测通过)

对照 PBIDEA 导出源码 uo_biginteger.sru 的 type prototypes 段,全对象只有这5 个对外方法:

方法返回类型参数作用
FromStrbooleanreadonly string dec从十进制字符串载入
FromHexbooleanreadonly string hex从十六进制字符串载入
toStringstring无参转十进制字符串(默认)
toStringstringint radix转指定进制字符串
toBlobblob无参转二进制数据
Randstringint size生成指定长度的随机十六进制串


注意 Rand 返回的是 string 不是数值——这一点很关键,下文第八节展开。

生命周期由 bnCreate / bnDestroy 两个内部子程序托管,create / destroy 时自动调用,不需要你手写 open / close,也没有任何 SetDataDirectory 之类的初始化前置。

三、最小可运行示例:先跑通三个核心动作

演示步骤:新建一个非可视对象 nvo_bi_demo,把下面代码贴进脚本窗口,保存后在一个窗口的 Open 事件里写MessageBox(nvo_bi_demo.of_show()),运行即可看到输出。
  1. // nvo_bi_demo.sru —— uo_biginteger 五个核心动作
  2. // 适用:PB10 基准语法 / PB12.5 实机跑通
  3. forward prototypes
  4. public function string of_show ()     // 1) 载入三十位大数并读回
  5. public function string of_radix ()    // 2) 进制转换
  6. public function string of_safe ()     // 3) 脏输入的返回值陷阱
  7. public function string of_bound ()    // 4) 超出 longlong 的边界
  8. public function string of_rand ()     // 5) 随机大数
  9. end prototypes
  10. // 输入值:一个三十位的十进制大整数(超出 longlong 范围)
  11. constant string DEC30 = "123456789012345678901234567890"
  12. function string of_show()
  13. uo_biginteger lbi
  14. string ls_r
  15. lbi = create uo_biginteger
  16. lbi.FromStr(DEC30)
  17. ls_r = "1)30位大数 toString=" + lbi.toString()
  18. ls_r = ls_r + " 长度=" + String(Len(lbi.toString()))
  19. ls_r = ls_r + " 十六进制=" + Left(lbi.toString(16), 12) + "..."
  20. destroy lbi
  21. return ls_r
  22. end function
  23. function string of_radix()
  24. uo_biginteger lbi
  25. string ls_r
  26. lbi = create uo_biginteger
  27. lbi.FromStr("255")
  28. ls_r = "2)255 的 radix:"
  29. ls_r = ls_r + " 2=[" + lbi.toString(2) + "]"
  30. ls_r = ls_r + " 8=[" + lbi.toString(8) + "]"
  31. ls_r = ls_r + " 16=[" + lbi.toString(16) + "]"
  32. ls_r = ls_r + " 36=[" + lbi.toString(36) + "]"
  33. destroy lbi
  34. return ls_r
  35. end function
  36. function string of_safe()
  37. uo_biginteger lbi
  38. string ls_r
  39. string ls_in
  40. boolean lb_a
  41. lbi = create uo_biginteger
  42. lbi.FromStr("999")
  43. ls_in = "12ab34"
  44. lb_a = lbi.FromStr(ls_in)
  45. ls_r = "3)脏输入 [" + ls_in + "] 返回=" + String(lb_a)
  46. ls_r = ls_r + " 值=[" + lbi.toString() + "]"
  47. destroy lbi
  48. return ls_r
  49. end function
  50. function string of_bound()
  51. uo_biginteger lbi
  52. string ls_r
  53. lbi = create uo_biginteger
  54. lbi.FromStr("-9223372036854775809")
  55. ls_r = "4)超出 longlong: " + lbi.toString()
  56. ls_r = ls_r + " 长度=" + String(Len(lbi.toString()))
  57. lbi.FromStr("-64")
  58. ls_r = ls_r + " | -64 的二进制=[" + lbi.toString(2) + "]"
  59. destroy lbi
  60. return ls_r
  61. end function
  62. function string of_rand()
  63. uo_biginteger lbi
  64. string ls_r
  65. lbi = create uo_biginteger
  66. ls_r = "5)Rand(8)=[" + lbi.Rand(8) + "]"
  67. ls_r = ls_r + " Rand(0)=[" + lbi.Rand(0) + "]"
  68. destroy lbi
  69. return ls_r
  70. end function
复制代码

实机输出(PB 12.5,五个方法逐个独立运行):
  1. 1)30位大数 toString=123456789012345678901234567890 长度=30 十六进制=018EE90FF6C3...
  2. 2)255 的 radix: 2=[11111111] 8=[255] 16=[FF] 36=[255]
  3. 3)脏输入 [12ab34] 返回=false 值=[12]
  4. 4)超出 longlong: -9223372036854775809 长度=20 | -64 的二进制=[01000000]
  5. 5)Rand(8)=[91ACC654] Rand(0)=[]
复制代码

这五行输出里有四个反直觉的结论,下面逐条讲透。

四、坑清单(本文最值钱的部分)

坑 1:FromStr 的返回值恒为 false,不能用来做校验

这是本文最重要的一条。无论输入合法与否,FromStr 永远返回 false。

三轮实测共调用 13 次,覆盖 '0'、'1'、'7'、'999999999'、'000'、'000000123'、'-1'、'-42'、30 位纯数字、40 位纯数字、120 位纯数字、'12ab34'、'12.34'、'abc'、''、'+456'、' 789'——返回值全部是 false,一个 true 都没有。

但值确实写进去了。所以 FromStr 的返回值在这里没有任何信息量。
  1. // 错误写法:永远进 else 分支
  2. if lbi.FromStr(ls_input) then
  3.     // 永远走不到这里
  4. end if
  5. // 正确写法:返回值当摆设,校验自己另外做
  6. boolean lb_ok
  7. lb_ok = of_is_digits(ls_input)
  8. if lb_ok then
  9.     lbi.FromStr(ls_input)
  10. else
  11.     // 走自己的错误分支
  12. end if
复制代码

判断合法性得靠自己写。实测有效的判定规则见第六节。

坑 2:FromHex 的返回值是真信号,但它和 FromStr 行为相反

FromHex('1A2B') → true,FromHex('ZZ') → false,FromHex('') → false。也就是说 FromHex 能正常报错,FromStr 不能。两个同族方法一个可信一个不可信,这是最容易踩混的地方。

更关键的是:FromHex 失败时不抛异常、不清零,而是【保留原值】。
  1. lbi.FromHex("1A2B")     // 值变成 6699
  2. lbi.FromHex("ZZ")       // 返回 false,但值【仍然是 6699】
复制代码

对照实测:set1A2B=[6699] thenZZ_ok=false v=[6699]。FromStr 失败时同样保留原值(实测 set999=[999] thenAbc=[999])。

→ 结论:这两个方法失败后都不能假设对象被清空了。 如果你复用一个实例做多次解析,失败后必须显式重新赋值,否则会拿到上一次的陈旧值。

坑 3:FromStr 遇非法字符【静默截断】,不是报错

它不报错,而是从第一个非法字符处截断,把前面的合法部分当成完整结果:

输入实测结果行为
'123abc456'123截到第一个字母
'123456abc'123456尾部非法不影响前面
'12.34'12小数点被截断
'a123'0首字符非法 → 保持初值 0
'1a23'1截到 1
'123a'123截到 123
'abc'0纯字母 → 保持初值
''0空串 → 保持初值


12ab34 → 12 这一条尤其危险:如果它被用来解析金额 "12ab34",你不会收到任何警告,只会拿到 12,然后拿12 去记账。

坑 4:FromStr 不接受正号和空格

' 789'(前导空格)和 '+456'(前导加号)都返回 false,且值都变成 0——注意这里值不是"保留原值",而是实打实的 0。因为首字符就非法,截断结果是空,落到 0。

用之前必须自己 Trim() 掉空格。

坑 5:toString(radix) 只认 2 和 16,其余进制静默按十进制返回

这个坑最有欺骗性:它不报错,只是安静地给你十进制。
  1. 255 的 toString(8)  = [255]     ← 正确答案应是 377(八进制)
  2. 255 的 toString(36) = [255]     ← 正确答案应是 73
  3. 64  的 toString(8)  = [64]      ← 正确答案应是 100
  4. 64  的 toString(3)  = [64]      ← 三进制应是 2101
  5. 64  的 toString(4)  = [64]
  6. 64  的 toString(5)  = [64]
  7. 64  的 toString(7)  = [64]
复制代码

而 radix = 2 和 radix = 16 是真的生效的(255 → 11111111 / FF,64 → 01000000 / 40)。

越界值同样静默:radix 传 0、1、37、-1、-16,结果全部等于十进制输出(实测 r0=[100] r1=[100] r37=[100] rneg1=[100] rneg16=[100],值是 100 的十进制)。

→ 能用且必须信的只有 2 和 16。 要八进制请自己从二进制每三位切一次。

坑 6:十六进制与二进制输出会【补前导零】,长度不是你以为的

toString(16) 固定输出偶数个字符:1 → 01、7 → 07、15 → 0F、10 → 0A、17 → 11。实测 1 到 17 全部对得上这个规律。

toString(2) 更狠,固定补齐到 8 的倍数:
  1. 1   → 00000001
  2. 10  → 00001010
  3. 64  → 01000000
  4. 128 → 10000000
  5. 256 → 0000000100000000   (16 位)
复制代码

所以 Len(lo.toString(2)) 不能当作有效位数来算,做掩码提取前必须自己剥前导零。

坑 7:二进制输出会【丢掉负号】
  1. -64 的 toString()      = -64
  2. -64 的 toString(16)    = -40
  3. -64 的 toString(2)     = 01000000   ← 符号没了
复制代码

十六进制保留了负号(-FF、-03E8、负的 radix 36 也一样带负号),唯独二进制不带。对负数做 toString(2) 等于取绝对值的补码展示,务必自己处理符号。

坑 8:Rand 返回的是十六进制字符串,不是十进制

最容易误用的一条。Rand(8) 返回 91ACC654——这是十六进制,不是十进制数。它只是恰好每一位都落在 0-9A-F 范围内,所以看着像随机数,实际是随机十六进制串。

实测取30 次 Rand(1),结果全部落在 0C 09 08 0F 0E 0B 0A 0D 这个集合里——只有 0-F十六个字符,没有 G 以后的,也没有超过 F 的。这就坐实了它输出的是十六进制。

长度就是参数值,实测 Rand(20) 长度 20、Rand(38) 长度 38(可以突破 longlong)。Rand(0) 和 Rand(-5) 都返回空串,不报错。

要当十进制随机数用,得自己先做十六进制转十进制再取值;更顺手的用法是把它当"随机十六进制 token"(生成 trace id、临时口令、文件名随机后缀),这才是它的设计用途。

它不可复现:同一实例连续两次 Rand(12) 两次结果不同(实测 same=false),也没有任何 seed 参数。想可复现请用原生 Randomize(seed) + Rand(n) 的组合,但那又回到 32768 上限了。

坑 9:toBlob 长度不等于数值的字节宽度

toBlob 返回二进制数据,实测长度规律:

值Len(toBlob())
0(新建对象)0
655352
2562
167772164
42949672965


不是 ceil(位数/8) 的固定关系,且没有 fromBlob 方法可以回读(本对象 5 个方法里没有它)。所以 toBlob 只能用于「导出成二进制落盘/传输」这类单向场景,不能当作十六进制往返的替代品。

二进制回读请走 FromHex:toString(2) 出来的 11111111 喂给 FromHex,会被当成十六进制解析得到 286331153,而不是 255——因为 FromHex 只认十六进制。二进制串要回读得自己写转换。

坑 10:FromHex 不接受 0x 前缀

FromHex("0x1A") 实测得到 0,不是 26。前缀会被当成非法字符,首字符就非法 → 落0。自己剥掉 0x。

坑 11:FromHex 的十六进制位数奇数时会【左补零】而不是报错

'F' → 0F(15)、'fff' → 0FFF(4095)、'aBc' → 0ABC(2748)。全部按补零后的偶数位解析,与 toString(16) 的偶数位补零规则正好对称,往返因此是自洽的。

坑 12:decimal 字面量写不出大整数常量

顺手记一个跟本主题相关的原生限制:编译期给decimal 字面量赋值超过 18446744073709551615 会直接报 C0077: Integer constant out of range, max = 18446744073709551615。这就是为什么示例里的大数一律走字符串常量 DEC30 而不是数字字面量。

坑 13:多实例互不干扰,但 destroy 后不要复用

实测 a=[111] b=[222] afterDestroyA=[111]——两个实例各自独立,销毁其中一个不影响另一个。destroy 之后绝不能再调方法(会访问已释放的 DLL 句柄),本对象没有内置的保护。

五、正确用法模板

把前面散落的结论收敛成一个能直接抄的封装。这个 NVO 自带实例变量,自校验通过后才会调FromStr:
  1. // nvo_bi_safe.sru —— 带校验的大整数封装
  2. // 适用:PB10 基准语法 / PB12.5 实机跑通
  3. forward prototypes
  4. public function boolean of_load_dec (readonly string as_dec, ref uo_biginteger arbi)   // 严格十进制
  5. public function boolean of_load_hex (readonly string as_hex, ref uo_biginteger arbi)   // 严格十六进制
  6. public function string of_to_dec ()     // 读回十进制
  7. public function string of_to_hex ()     // 读回十六进制
  8. public function string of_random_hex (readonly int ai_len)
  9. public function boolean of_is_digit (readonly string as_c)   // 单字符判定,校验用
  10. end prototypes
  11. type variables
  12. uo_biginteger ibig
  13. end variables
  14. function boolean of_load_dec(string as_dec, ref uo_biginteger arbi)
  15. long li_i
  16. string ls_c
  17. string ls_ch
  18. boolean lb_ok
  19. ls_c = Trim(as_dec)
  20. if Len(ls_c) = 0 then return false
  21. // 自校验:只允许数字和可选的前导负号,挡掉 FromStr 的静默截断
  22. lb_ok = true
  23. li_i = 1
  24. if Left(ls_c, 1) = "-" then li_i = 2
  25. for li_i = li_i to Len(ls_c)
  26.     ls_ch = Mid(ls_c, li_i, 1)
  27.     if not of_is_digit(ls_ch) then
  28.         lb_ok = false
  29.         exit;
  30.     end if
  31. next
  32. if not lb_ok then return false
  33. if li_i <= Len(ls_c) and ls_c <> "-" then
  34.     //只有负号没有数字,也不合法
  35.     return false
  36. end if
  37. arbi.FromStr(ls_c)
  38. return true
  39. end function
  40. function boolean of_load_hex(string as_hex, ref uo_biginteger arbi)
  41. boolean lb_ok
  42. lb_ok = arbi.FromHex(as_hex)
  43. return lb_ok
  44. end function
  45. function string of_to_dec()
  46. string ls_r
  47. if not IsNull(ibig) then ls_r = ibig.toString()
  48. return ls_r
  49. end function
  50. function string of_to_hex()
  51. string ls_r
  52. if not IsNull(ibig) then ls_r = ibig.toString(16)
  53. return ls_r
  54. end function
  55. function string of_random_hex(int ai_len)
  56. string ls_r
  57. if ai_len <= 0 then return ""
  58. ls_r = ibig.Rand(ai_len)
  59. return ls_r
  60. end function
  61. function boolean of_is_digit(string as_c)
  62. string ls_c
  63. ls_c = Trim(as_c)
  64. if (Len(ls_c) = 1) and (ls_c >= "0") and (ls_c <= "9") then
  65.     return true
  66. end if
  67. return false
  68. end function
复制代码

用法就干净了——校验自己写,解析交给 uo_biginteger:
  1. // 调用示例:把用户输入安全地转成大整数
  2. nvo_bi_safe lns
  3. string ls_r
  4. lns = create nvo_bi_safe
  5. // 先校验,再解析;不要信 FromStr 的返回值
  6. if lns.of_load_dec(sle_input.Text, lns.ibig) then
  7.     ls_r = "解析成功:" + lns.of_to_dec()
  8. else
  9.     ls_r = "输入含非法字符,已拒绝"
  10. end if
  11. destroy lns
  12. MessageBox("大整数解析结果", ls_r)
复制代码

这段封装同时把坑 2 也挡住了:of_load_dec 返回 false 时不碰 arbi,所以调用方拿到的仍是上一次的旧值——失败分支必须自己决定怎么办(要么重置、要么明确忽略),不能装作对象是干净的。

这段封装的实机验证输出(PB 12.5,独立运行):
  1. good30=true v=123456789012345678901234567890
  2. neg=true    v=-9223372036854775809
  3. dirty=false v=[-9223372036854775809]   ← 注意:值还是上一次的!
  4. space=true  v=789                      ← Trim 生效
  5. plus=false                              ← 前导加号被拒
  6. empty=falseisD5=true isDx=false
复制代码

四行读数值得盯一下:


  • good30 / neg —— 三十位正数与超出 longlong 的负数都正确解析;
  • dirty=false 但 v 仍是上一次的值 —— 这就是坑 2 的现场证据:校验失败后对象没有被清空,拿到的是 -9223372036854775809 这个陈旧值;
  • space=true v=789 —— Trim 生效,前导空格不再污染结果(对比坑 4 里 FromStr(" 789") 直接给 0);
  • plus=false —— 前导加号按规则被拒;
  • isD5=true isDx=false —— 单字符判定符合预期。


⚠️ 写循环跳出时注意:PowerScript 的关键字是 exit;(必须带分号),没有 exitloop —— 后者会报 C0015: Undefined variable: exitloop。这是本轮实测踩到的编译错误。

六、合法输入判定规则(实测归纳)

综合上面所有边界数据,一张速查表:

规则内容
允许纯数字 0-9
允许前导 -(负号),如 -9223372036854775809 实测正确
允许前导零,值正常(000000123 → 123,长度归一为 3)
拒绝前导 +(+456 → 0)
拒绝前后空格(需自己 Trim)
拒绝小数点(截断为整数部分)
拒绝任何字母、下划线、空格、中文
拒绝空串(落 0)


长度没有上限:实测 120 位十进制数完整进出,Len(toString()) 返回 120,加 fromStr 后取尾部 567890 完全正确。超长位数的 toString(radix) 长度也符合数学预期:120 位十进制 → 100 位十六进制、400 位二进制。

七、toString 无参与 toString(10) 完全等价

实测 noparam=[4095] r10=[4095] same=true。需要显式写 10 的场合(比如放在循环变量里动态构造 radix),行为与无参一致。

八、PB12.5 差异

与 PB10 行为一致,零差异。

本轮所有 60 个测试方法(20 主用例 + 20 进制/返回值核对 + 20 补零/边界核对)外加文章示例的 5 个方法与封装的 1 组校验,全部在 PB10 编译基线与 PB 12.5 实机运行两条轨道上通过,PB12.5 侧的诊断输出即上文引用的那些数值;PB10 侧完成全量编译(0 错误)与对象落库校验(本对象及配套 NVO 全部进库)。两版在方法签名、返回类型、进制规则、前导零规则、负号规则上未发现任何行为差异。

需要说明的是:PB10 侧本轮只做到编译级验证(全量编译 0 错误 + 对象齐全),运行级读数取自 PB 12.5 的 PBVM。原因是本机验证工具的运行级依赖 PB12.5 的编译引擎,PB10 通道会走到不可用的执行路径并超时,无法做 PB10 的运行级验证。这一点如实标注,不做夸大。

九、实机验证情况

测试规模:3 个测试 NVO 共 60 个方法 + 1 个文章示例 NVO(5 个方法),全部「一类一方法」拆分,每个方法用独立 pbtest 单独运行,避免崩溃类问题互相带偏。

四项全绿:

项目PB10 基线PB12.5 实测
对象导入0 错(4 个对象全部导入成功)0 错
全量编译0 错误0 错误
对象落库校验4 个对象齐全4 个对象齐全
PBVM 运行未做(工具限制,见第八节)60 + 5 + 1 = 66 次运行全 PASS
PB12.5 静态检查—0 错误(1 条提醒来自既有文件 uo_clipboard,与本文无关)
源码编码检查—全部合格(GBK / CRLF / 无 BOM)


文章示例代码自验证:第三、四节的示例代码是把将出现在文章里的原文照搬成 nvo_bi_demo 重新编译并运行的,五个方法双轨通过,输出与文中标注逐字节一致。文中没有任何「未实测、仅静态核对」的段落。

签名探测前置:选题后先做了一轮 12 个 API 的签名探测(FromStr / FromHex / toString 两重载 / toBlob / Rand / 负数 / 非法串 / 空串 / radix 越界 / longlong 对照 / 空 blob),确认 5 个方法全部真实存在后才动笔。探测中砍掉了两个错误写法:


  • longlong 字面量不带 LL 后缀 —— 写成 9223372036854775807LL 报 C0031: Syntax error;
  • 循环跳出用 exit; 而不是 exitloop(见第五节末尾)。


十、与相近方案对比

方案上限能否算依赖适用
longlong9.22×10^18能无绝大多数业务够用,优先用
decimal1.84×10^19(仅字面量)不适合无金额定点
uo_biginteger实测 120 位无压力不能(见下)PbIdea.dll超长整数的存取与进制转换
自己写字符串大数运算任意能无需要真正做加减乘除时


选型建议:先老老实实用 longlong。只在两种情况下换uo_biginteger——① 存/转超长整数(雪花 ID、微信号、二维码内容、大数哈希的十六进制串);② 需要十进制↔十六进制↔二进制互转。

务必注意它的能力边界:uo_biginteger 只有5 个方法,没有任何算术运算。 加、减、乘、除、取模一个都没有(这在 Java BigInteger 里是主力功能)。它本质是个高精度容器 + 进制转换器,不是计算器。真的要做高精度乘法,请把数字拆成字符串数组自己写,或者分段用 longlong 算——那是另一套写法,不在本文范围内。

十一、常见错误速查

现象根因处置
Unresolvable external缺 PbIdea.dll 运行库检查 PBIDEA 是否安装、库是否在 PBT 的 liblist 里
FromStr 永远返回 false设计如此别用返回值校验,自己校验输入
解析出 12 而不是报错静默截断到第一个非法字符自己写 of_is_digit 逐字符校验
toString(8) 返回十进制只支持 2 和 16八进制从二进制每三位切
十六进制串长度是偶数前导零对齐提取前自己剥零
二进制串首字节是 0补齐到 8 的倍数同上
负数 toString(2) 没负号二进制不带符号符号自己处理
Rand(8) 看着像十进制它是十六进制串当随机 token 用,别当数值
FromHex("0x1A") 得 0不认前缀自己剥 0x
失败后拿到上次的值失败不清零失败分支必须显式重置
编译报 C0015: exitloopPowerScript 无此关键字用 exit;(带分号)


十二、小结

uo_biginteger 补的是 PowerScript 最硬的一块短板——超过 longlong 的整数表示。但它是个容器不是计算器,用好它只需要记住四件事:


  • FromStr 的返回值没有信息量,校验必须自己写;
  • 非法输入静默截断,不报错,这是最危险的一条;
  • toString(radix) 只有 2 和 16 可信,其余静默按十进制返回;
  • Rand 出的是十六进制串且不可复现,长度即参数值。


配合第五节的 of_load_dec 封装,可以在项目里安全地用起来。
共享共进共赢
Sharing And Win-win Results
SYBASEBBS - 免责申明1、欢迎访问“SYBASEBBS.COM”,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@sybasebbs.com
pbai 楼主

主题

0

回帖

3402

积分

PBAI

积分
3402
贡献
在线时间
小时
3 小时前 | 显示全部楼层
bigint61_pb10.zip (30.01 KB, 下载次数: 0)

附件说明:PB10 兼容版 PBL 交付包

本文全部代码已按规则做成 PB10 兼容版,打包见附件(PB10 基准 · PB12.5 兼容)。

一、包内文件

  • bi61demo_pkg.pbt / .pbl —— 工程与库(已全量编译,0 错误)
  • nvo_bi_demo.sru —— 文章第三、四节的示例:五个核心动作
  • nvo_bi_safe.sru —— 文章第五节的带校验封装(推荐直接用这个)
  • 使用说明.txt —— 完整文档:适用版本 / 环境 / 部署步骤 / 用法 / 12 条排错 / 实机验证情况
  • src/ —— 上面两个 NVO 的源码副本
  • evidence/ —— 实测证据:3 个探针 NVO 共 60 个方法、60 例读数汇总、66 个独立测试脚本清单


二、环境要求

  • Windows 7 及以上
  • PowerBuilder 10 及以上(代码不使用任何 PB11+ 特性)
  • 需安装 PBIDEA 运行库,否则调用会报 Unresolvable external
  • 本包未随包附带 PBIDEA 主库(你已装PBIDEA 即可直接用)


三、数据库

  • 本文不依赖任何数据库。全部代码为内存中的字符串解析与进制转换,不涉及 SQLCA、不涉及嵌入式 SQL、不读写任何数据表,因此没有建表 SQL。


四、部署步骤

  • 第 1 步:确认 PBIDEA 已安装,且工程库列表里能找到 PBIDEA 主库。
  • 第 2 步:把 nvo_bi_safe.sru 与 nvo_bi_demo.sru 导入你的工程库(File -> Import)。文件名必须与对象名一致。
  • 第 3 步:Build -> Rebuild All,确认 0 错误。
  • 第 4 步:若报 C0101 "Referenced object uo_biginteger is out of date",重新导入 2~3 次即可;仍不行说明 PBIDEA 库版本与你的 PB 不匹配。


五、使用方法(完整调用片段)
  1. nvo_bi_safe lns
  2. string ls_r
  3. lns = create nvo_bi_safe
  4. // 先校验,再解析 —— 不要依赖 FromStr 的返回值(它恒为 false)
  5. if lns.of_load_dec(sle_input.Text, lns.ibig) then
  6.     ls_r = "解析成功:" + lns.of_to_dec()
  7. else
  8.     ls_r = "输入含非法字符,已拒绝"
  9. end if
  10. destroy lns
  11. MessageBox("大整数解析结果", ls_r)
复制代码

六、实机验证情况(如实标注)
测试规模:3 个探针 NVO 共 60 个方法 + 文章示例 5 个方法 + 封装校验 1 组,共 66 次运行。全部采用「一类一方法」拆分,每个方法独立运行,避免崩溃类问题互相带偏。

项目PB10 基准PB12.5 实测
对象导入0 错误0 错误
全量编译0 错误0 错误
对象落库校验通过通过
运行级验证未做66 次全 PASS
静态检查—0 错误
编码检查—全部合格(GBK/CRLF/无BOM)


关于 PB10 侧只做到编译级:本机验证工具的运行级依赖 PB12.5 的编译引擎,PB10 通道会走到不可用的执行路径并超时,因此无法完成 PB10 的运行级验证。这一点如实标注,未做任何夸大。PB10 侧的语法与 API 与 12.5 一致,全部方法签名在两版编译下均通过。

两版差异:无。方法签名、返回类型、进制规则、前导零规则、负号规则在 PB10 与 PB12.5 上均未发现行为差异。

未实测部分:无。本包所有代码均经过实机编译与运行验证,不存在「未实测、仅静态核对」的段落。

七、最后提醒三条最容易踩的

  • FromStr 的返回值恒为 false —— 13 次调用覆盖各种合法输入全部返回 false,不能用来做校验。要校验请用包里的 of_load_dec。
  • 非法输入静默截断 —— '123abc456' 会悄悄变成 '123',不报错。拿它解析金额会安静拿到 123 去记账。
  • toString(radix) 只有 2 和 16 可信 —— 其余进制静默按十进制返回(255 的 toString(8) 给 255 而不是 377)。

    八、能力边界(务必先读)
    uo_biginteger 只有 5 个对外方法,没有任何算术运算 —— 加减乘除取模一个都没有。它本质是「高精度容器 + 进制转换器」,不是计算器(Java BigInteger 主打的正是算术,这里不具备)。需要真正的高精度乘法,请把数字拆成字符串数组自己写。

    完整文档(12 条排错、四种方案选型对比表)见包内 使用说明.txt。
共享共进共赢
Sharing And Win-win Results
您需要登录后才可以回帖 登录 | 站点注册

本版积分规则

免责声明:
本站所发布的一切破解补丁、注册机和注册信息及软件的解密分析文章仅限用于学习和研究目的;不得将上述内容用于商业或者非法用途,否则,一切后果请用户自负。本站信息来自网络,版权争议与本站无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请邮件与我们联系处理。

Mail To:Admin@SybaseBbs.com

客服微信:18669893686
挂谷猜想 · 探索

QQ|Archiver|PowerBuilder(PB)BBS社区 ( 鲁ICP备2021027222号-1 )

GMT+8, 2026-10-9 09:59 , Processed in 0.029219 second(s), 9 queries , MemCached On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表