一个 bytes32 装一个复数:ERC-5850 的劈半存储与读回函数
合约开发里 bytes32 是个万能格子:哈希、地址补零、短字符串都往里装。2022 年 10 月 29 日创建的 ERC-5850 想给复数也找一个格子:不新建结构体,直接把一个 bytes32 从正中间劈开,一半放实数、一半放虚数。按 ercs 仓库记录,这份标准状态为 Stagnant。格子选得不起眼,规则却定得较真,值得当作“存储布局类提案”的样本读一遍。
劈半与位置约定
标准的描述方式很物理:把三十二字节的空间上下均分,实部取最低的十六字节,虚部取最高的十六字节。参考实现 cnNew(int128 _Real, int128 _Imag) 收两个 int128:各自转成 bytes16 后,实数右移一百二十八位、再与虚数按位或,拼成一个 bytes32。读回用 RealIm(bytes32 _cn),借助内联汇编把值写入内存相邻位置,取出高低两段再转回 int128。为什么实数必须在低位?设计说明解释得很直白:只要实部是小于二的第一百二十七次方的正整数,低位摆放让 uint128 到 bytes32 的转换零成本;反过来从 bytes32 直接当整数读回则被明令不推荐,因为复数可能带虚部、实部也可能是负数,必须走 RealIm 拆分。

一个格子,多种写法
标准声称这套布局能兼容笛卡尔、极坐标、指数等多种复数表示,示例代码只给了笛卡尔形式。它还指出复数的加减乘除、小数位数都不在本文范围内,留给其他提案讨论——只定义“怎么放”,不定义“怎么算”,是它保持小体量的自觉选择。小数方面,Solidity 当时对定点浮点支持尚不成熟,标准建议可以搭配 prb-math 这类十八位定点库或 abdk 浮点库使用,同时声明只要小数表示塞得进十六字节,本约定不挑具体方案。
省下的钱与省不掉的事
为什么是复数:数学动机与一个用库的巧思
动机部分列的清单暴露了目标读者:傅里叶变换、特征函数、交流电路、纳维-斯托克斯方程——作者想要的用户是往合约里搬数值计算的工程师。复数在这些场景里不是装饰,而是一等数据结构,可 Solidity 没有原生复数类型。提案的算盘是:与其让每个项目自定义两个字段的结构体,不如把约定收敛进一个现成的 bytes32,一半实部一半虚部,存储开销直接减半,还能借用海量现存以 bytes32 为键的映射——在复平面上做索引,代码可读性顺带提升。
用库的巧思是另一半看点。标准点名了 Using ... for 语法:库名叫 Complex 的话,任何 bytes32 变量就能写成“它做加法”的链式风格,只要库里实现了对应函数。这意味着这份标准可以不要求任何协议改动状态布局,只在工具层给普通 bytes32 装上复数语义——一种典型的“约定即标准”路线。小数问题被明确挂起:当时 Solidity 尚无原生定点浮点,提案建议暂配十八位精度的 prb-math 或 abdk 浮点库,同时声明只要小数表示塞得进十六字节,本约定不挑剔具体方案。最后再补一句风险提示的另一面:正整数小于二的第一百二十七次方时实部落低位可零成本直转,反方向则被明令别想直接当整数读——高低位可以搬回,语义永远要靠 RealIm 确认。
动机部分算的账很实在:两个数装进一个 bytes32,存储成本近乎减半;以 bytes32 为键的映射可以顺势把复平面当索引,代码读起来更自然;Using ... for 语法还能挂上复数库。它同时保证向后兼容——bytes32 继续干原来的活,两种用途井水不犯河水。但也要清楚:这份标准没有给复数类型起注册标识,链上看到的仍是普通 bytes32,光凭数值本身无法判断“这格子按 5850 排过队”;混用方必须靠约定文档而非链上元数据对齐。一份存储布局提案能否成活,最终取决于同读这段字节码的各方是否照办。本文为机制说明,不构成任何投资建议。
发表评论
还没有评论,来说两句吧。
评论区为展示样式,提交不会被处理。