English
Rathee 三部曲:CrypTFlow2,SIRNN与SecFloat
Background
Notation: 记安全参数为 λ , 秘密共享的整数长度为 l -bit.
MPC方面,假设我们拥有以下基础知识:
可以使用基于公钥密码等方式实现 1-out-of-k OT l ,单次通信量 2 λ + k l ,使用 IKNP 可以做到 λ + k l 。
基于 2-party 的加法 secret sharing 的加法与标量乘几乎免费(无通信开销)。
可以使用 beaver triple 实现 2-party 的安全乘法操作。一次安全乘法,总通信量为 2 l 。
在浮点数储存方面,我们使用 IEEE754 Float32(单精度标准):
( − 1 ) S × ( 1. M ) × 2 E − b i a s
符号位 S 1位、指数为 E 7 位、尾数 M 为 23 位、b i a s = 127
四舍六入五成双(round-to-nearest-ties-to-even)
IEEE 754 加法
不失一般性,设 x ≥ y ,为了计算 z = x + y ,其中:
x = ( − 1 ) S x ⋅ ( 1. M x ) ⋅ 2 E x , y = ( − 1 ) S y ⋅ ( 1. M y ) ⋅ 2 E y 则步骤如下:
指数对齐。若 E x > E y ,将 y 的尾数右移: M y ← 1. M y 2 E x − E y , E ← E x , M x ← ( 1. M x ) .
尾数相加。考虑S x , S y 的符号情况。若同号,则计算 M = M x + M y 。若异号,则用大者减小者得到差的绝对值M = | M x − M y | ,并附上正确的符号S 。
规范化。显然 max { M x + M y , | M x − M y | } < 4 ,故M 右移最多只需一次(对应指数E 加1即可)。若 M = 0 ,则直接跳到第四步。其余情况反复左移并自减E ,直到M ∈ [ 1 , 2 ) 时停止。
写入结果。最后将 M 的首位 1 剔除,将最后的 S , E , M 重新作为新的 32 位浮点结果。
在 M y ← 1. M y 2 E x − E y 、M = M x + M y 、M = | M x − M y | 以及对M 右移一位这四个步骤中,会涉及到舍入问题。
IEEE 754 乘法
乘法由于不涉及数的大小比较,因此步骤相对比较简单。
符号位异或。S = S x ⊕ S y .
指数相加。E = E x + E y − b i a s .
尾数相乘。M = ( 1. M x ) ⋅ ( 1. M y ) ,结果在 [ 1 , 4 ) 范围内。
规范化。如果结果 ≥ 2 ,将 E 自增并将 M 右移一位,最后剔除 M 最高位的 1.
在 M = ( 1. M x ) ⋅ ( 1. M y ) 与 M 右移一位这两步涉及到舍入操作。
舍入方法
我们令 d , g , f 分别为 M 的最低位(decision bit),被丢弃的最高位(guard bit)和被丢弃的剩余位(sticky bit)。则“四舍六入五成双”的规则可以形式化表述为如下公式:
c = g ∧ ( d ∨ f ) 简单的解释一下,就是只有当 g = 1 时才可能进位。此时如果 f = 1 (肯定大于0.5)或者 d = 1 (刚好0.5 但要是偶数),则进位(c = 1 )。
注意舍入之后有可能需要再次规范化。
Building Blocks
下面用自底向上的方式,讲清楚整个协议是如何构建的。
F M U X
我们需要实现的理想功能是,MUX(b,x)=(b==1?x:0)。即对于布尔 secret share [ c ] = ( c 0 , c 1 ) , c i ∈ { 0 , 1 } 和算术 secret share [ a ] = ( a 0 , a 1 ) , a ∈ Z n ,输出 [ a ⋅ c ] 。步骤如下:
SETUP: P 0 持有 a 0 , c 0 ,P 1 持有 a 1 , c 1 。
P 0 与 P 1 各自生成随机数 r 0 , r 1 ∈ Z n 。
P 0 根据 c 0 的值设置 ( s 0 , s 1 ) 。若 c 0 = 0 ,令 ( s 0 , s 1 ) = ( − r 0 , − r 0 + a 0 ) ,否则为 ( − r 0 + a 0 , − r 0 ) 。
P 0 作为发送方与 P 1 做一轮 1-out-of-2 OT,P 0 提供两条消息 ( s 0 , s 1 ) ,P 1 提供 c 1 并最终获得 x 1 = s c 1 。
P 1 根据 c 1 的值设置 ( t 0 , t 1 ) 。若 c 1 = 0 ,令 ( t 0 , t 1 ) = ( − r 1 , − r 1 + a 1 ) ,否则为 ( − r 1 + a 1 , − r 1 ) 。
再做一轮 OT,P 0 获得 x 0 = t c 0 。
P 0 贡献 r 0 + x 0 ,P 1 贡献 r 1 + x 1 。加起来是 ( r 0 + r 1 ) + ( x 0 + x 1 ) 。
我们列举出四种可能的 ( c 0 , c 1 ) 情况:
( s 0 , s 1 ) = ( − r 0 + c 0 a 0 , − r 0 + a 0 ( 1 − c 0 ) ) ,( t 0 , t 1 ) = ( − r 1 + c 1 a 1 , − r 1 + a 1 ( 1 − c 1 ) )
x 0 = t c 0 , x 1 = s c 1
c 0
c 1
x 0
x 1
( r 0 + r 1 ) + ( x 0 + x 1 )
0
0
− r 1 + c 1 a 1
− r 0 + c 0 a 0
0
0
1
− r 1 + c 1 a 1
− r 0 + a 0 ( 1 − c 0 )
a
1
0
− r 1 + a 1 ( 1 − c 1 )
− r 0 + c 0 a 0
a
1
1
− r 1 + a 1 ( 1 − c 1 )
− r 0 + a 0 ( 1 − c 0 )
0
结果刚好等于 [ a ⋅ c ] ,因此协议正确。
通信量为两轮 IKNP-OT 的开销,也就是 2 ( λ + 2 l ) = 2 λ + 4 l 。但 CryptoFlow2 的 3.1.1 节可以将通信开销优化到 2 λ + 2 l 。
F A N D
这是显然的,直接使用 beaver triple 实现,通信量为 ( λ + 16 ) + 4 。(见 CryptoFlow2 的附录 A1 节)
F O R
我们有 [ x ∨ y ] = [ x ⊕ y ] ⊕ [ x ∧ y ] 。因此令 [ x ∧ y ] = ( z 0 , z 1 ) ,因此每一方直接计算 x i ⊕ y i ⊕ z i 即可。通信量与 F A N D 相同,也为 λ + 20 。
F E Q
我们将 x , y 按照 m -bit 进行分块。考虑对某个块 x j , y j 进行比较。我们使用 1-out-of-2 m OT:
P 0 随机选取 ( e q 0 , j ) 0 ,并对 k ∈ [ 0 , 2 m − 1 ] 准备消息 t j , k = ( e q 0 , j ) 0 ⊕ ( x j == k ) .
P 0 将 2 m 个消息作为 OT 的输入,P 1 输入 y j ,并获得 ( e q 0 , j ) 1 .
当且仅当 x j = y j 时,我们有 ( e q 0 , j ) 0 ⊕ ( e q 0 , j ) 1 = 1 ,这就完成了 x j , y j 的秘密EQ判断。
我们可以通过树状结构与 F A N D ,计算 ( e q 1 , j ) i = ( e q 0 , j ) i ∧ ( e q 0 , j + m ) i ,将 m -bit 比较拓展到 2 m -bit,最后完成整个长度的比较。通信量为 ⌈ l m ⌉ ( 2 λ + 2 m ) + ⌈ l m ⌉ ( λ + 20 ) ,轮数为 log l .
F G T / L T
仍然是分块的思路,首先计算块长度为 m 的结果 1 { x j < y j } ,还是可以使用 1-out-of-2 m OT 完成。P 0 只需随机生成 ( l t 0 , j ) 0 , 并准备 2 m 个消息 t j , k = ( l t 0 , j ) 0 ⊕ 1 { x j < k } .
然后合并的时候高位优先,1 { x < y } = 1 { x H < y H } ⊕ ( 1 { x H = y H } ∧ 1 { x L < y L } ) .
通信成本小于 λ ( 4 q ) + 2 m ( 2 q ) + 22 q ,取 m = 4 , q = l / 4 时为 λ l + 13.5 l ,轮数 log l .
F L U T
假设 LUT 有 2 m 项,每一项有 n -bit.
SETUP: P 0 随机取索引 r ∈ { 0 , 1 } m ,和 LUT L 混淆后的 share T 0 [ i ] ∈ Z 2 n , ∀ i ∈ { 0 , 1 } m .
P 0 对每个 s ∈ { 0 , 1 } m ,构造 M s [ i ] = L [ i ⊕ r ⊕ s ] ⊕ T 0 [ i ] , ∀ i ∈ { 0 , 1 } m .
P 0 将这 2 m 条长度为 n -bit 的消息与 P 1 (选取 s )作 1-out-of-2 m OT n ,成本为 2 λ + 2 m n .
P 1 令 T 1 ← M s 。现在 P 0 持有 ( T 0 , r ) ,P 1 持有 ( T 1 , s ) .
在线阶段,P 0 发送 u = x 0 ⊕ r , P 1 发送 v = x 1 ⊕ s ,双方同时计算 i ∗ = u ⊕ v = x ⊕ r ⊕ s . 最后 P 0 , P 1 分别保存 T 0 [ i ∗ ] , T 1 [ i ∗ ] ,显然合起来是 L [ x ] 。通信成本 2 m 可忽略。
F W r a p
如果我们要计算 1 { a + b > 2 n − 1 } ,这等同于计算 1 { 2 n − 1 − a < b } 。因此直接使用 F G T / L T 即可。
F B 2 A
P 0 , P 1 分别持有布尔共享的一位 c = c 0 ⊕ c 1 , c ∈ { 0 , 1 } ,并最后得到 d = d 0 + d 1 ( mod 2 n ) 且 d = c 。
P 0 随机选取 x ∈ Z 2 n ,生成二元组 ( x , c 0 + x ) , 并与 P 1 (持有输入 c 1 )执行 1-out-of-2 COT n ,P 1 拿到结果 y 1 。P 0 设置 y 0 = 2 n − x .
双方本地线性修正,P 0 计算 d 0 = c 0 − 2 y 0 ,P 1 计算 d 1 = c 1 − 2 y 1 .
验证一下结果,d 0 + d 1 = c 0 + c 1 − 2 ( y 0 + y 1 ) 。
当 c 1 = 0 时,y 0 + y 1 = ( 2 n − x ) + x = 2 n ,故 d 0 + d 1 = c 0 + c 1 ( mod 2 n ) 。
当 c 1 = 1 时,y 0 + y 1 = ( 2 n − x ) + ( c 0 + x ) = 2 n + c 0 ,故 d 0 + d 1 = c 0 + c 1 − 2 c 0 = 1 − c 0 . 但此时 c = c 0 ⊕ 1 = 1 − c 0 ,因此 d = c 仍然成立。
通信开销为一次 1-out-of-2 COT n ,成本为 λ + n .
F Z E x t
我们有了前面的 F W r a p 和 F B 2 A ,构造 F Z E x t 便是自然的事情。对于 m -bit 的加法共享,我们尝试将其零扩展到 n -bit ( n > m ) 。首先,我们要 check 这两个 share 是否有进位(使用 F W r a p ),然后将得到的布尔进位 w 使用 F B 2 A 转为 Z 2 n − m 的算术值。
但我们只是做零扩展操作,两个 share 相加,不能在第 m 位产生进位,因此双方要在 Z 2 n 减掉一个 2 m 的 share,由于 m 公开,可以直接本地完成。
成本为 Comm ( F W r a p + F B 2 A ) = λ m + 14 m + λ + ( n − m ) = λ ( m + 1 ) + 13 m + n .
F T R
既然我们有了从小到大的 F Z E x t ,那自然也有反过来的 F T R 。我们假设从 l -bit 截断低位的 s -bit,并输出最终的高位 l − s -bit(这本质上也等同于 x >> s):
SETUP: P b 将原来的 share x b 拆成 u b | | v b ,前者为 l − s 位,后者为 s 位,可以证明:
T R ( x , s ) = u 0 + u 1 + W r a p ( v 0 , v 1 , s ) 开销为 Comm ( F W r a p + F B 2 A ) = ( λ s + 14 s ) + ( λ + ( l − s ) ) = λ ( s + 1 ) + 13 s + l .
F C r o s s T e r m
F C r o s s T e r m 与用 beaver triple 的安全乘法比较相近,但区别是后者 P 0 和 P 1 都知道 x 和 y 的一部分 share。而 F C r o s s T e r m 的适用条件是 P 0 独占 x ,P 1 独占 y ,最后各自获得长度 l = m + n 的 x ∗ y 的 share.
P 0 将自己的 x 写成二进制 x = ∑ i = 0 m − 1 x i 2 i , x i ∈ { 0 , 1 } .
对 i ∈ [ 0 , m − 1 ] , 调用 1-out-of-2 COT l − i :P 0 持有 x i ,P 1 持有 y ,生成 ⟨ t i ⟩ l − i 满足 t i = x i ⋅ y .
双方本地计算 ⟨ z ⟩ l = ∑ i = 0 m − 1 ⟨ t i ⟩ l − i ,显然两个share加起来就是乘积x ⋅ y .
总通信成本为 ∑ i = 0 m − 1 ( λ + ( l − i ) ) = m λ + m l − m ( m − 1 ) 2 = O ( m λ + m n ) .
F U M u l t
语义是双方持有 x = x 0 + x 1 ( mod 2 m ) , y = y 0 + y 1 ( mod 2 n ) ,目标是计算 z = x ⋅ y ∈ Z 2 m + n .
这里还是不能直接做 beaver triple,因为它是 ring agnostic 的,可能会带来未知的 wrap 问题。当然一个办法是将 x , y 都扩展到足够大的环,做安全乘法后又截断回去。当然这里有两个问题,一是 F T R 是高位截断而不是低位截断,协议需要稍微改一改;二是这种办法运算成本较高,不如接下来介绍的专用 F U M u l t 协议。
我们现在要计算( x 0 + x 1 ) ( y 0 + y 1 ) = x 0 y 0 + x 1 y 1 + x 0 y 1 + x 1 y 0 ,前两者可以分别由 P 0 , P 1 离线完成。而后两者就涉及到刚才的 F C r o s s T e r m 了。总之,我们有了在 P 0 存储的完整 x 0 y 0 ,P 1 存储的 x 1 y 1 ,以及双方都拥有的 ⟨ x 0 y 1 ⟩ , ⟨ x 1 y 0 ⟩ 的一部分。F U M u l t 的作用就是把这 4 项安全地拼起来,并且处理 wrap 的问题。
首先,我们要考虑 x 0 + x 1 , y 0 + y 1 是否溢出的问题,因此需要 F W r a p 进行检测得到两个溢出位 w x , w y 。
然后我们可以利用 F M U X 计算 g=w_y?x:0 与 h=w_x:y:0的 share ⟨ g ⟩ , ⟨ h ⟩ ,处理可能的溢出差值。
最终 P b 输出 x b y b + ⟨ x 0 y 1 ⟩ b + ⟨ x 1 y 0 ⟩ b − 2 n ⟨ g ⟩ b − 2 m ⟨ h ⟩ b .
尝试把两个 share 加起来看看:∑ b ( x b y b + ⟨ x 0 y 1 ⟩ b + ⟨ x 1 y 0 ⟩ ) = ( x 0 + x 1 ) ( y 0 + y 1 )
而 ∑ b ( − 2 n ⟨ g ⟩ b − 2 m ⟨ h ⟩ b ) = − 2 n g − 2 m h = − 2 n ( w y ⋅ x ) − 2 m ( w x ⋅ y )
合在一起,z = ( x 0 + x 1 ) ( y 0 + y 1 ) − w y ⋅ ( x ⋅ 2 n ) − w x ⋅ ( y ⋅ 2 m )
= ( x + w x 2 m ) ( y + w y 2 n ) − w y ⋅ ( x ⋅ 2 n ) − w x ⋅ ( y ⋅ 2 m ) = x y ,刚好抵消。
设ν = max ( m , n ) , l = m + n ,论文给出了具体的开销,数量级为 O ( λ ν + ν 2 ) 。相比于 beaver triple 做法(含生成乘法三元组)的 O ( λ l + l 2 ) 数量级相同,但论文声称 F U M u l t 通信少 1.5 × .
F S M u l t 与 F U M u l t 原理类似,通信量完全相同,这里就略过了。
F D i g D e c
作用是将一个 l -bit 的 share ⟨ x ⟩ ,按 d -bit 分块,拆分成 c = ⌈ l d ⌉ 个 { ⟨ z i ⟩ } i = 0 c − 1 ,其中 x = z c − 1 | | z c − 2 | | ⋯ | | z 0 . 显然,本地进行这个操作会导致 wrap 问题。
当然解决这个问题的思路也容易想到,首先使用 F w r a p 判断 ⟨ x [ 0 , d − 1 ] ⟩ 的两个 share 是否溢出,并得到溢出结果的 share ⟨ c 0 ⟩ ,令 ⟨ z 0 ⟩ = ⟨ x [ 0 , d − 1 ] ⟩ − 2 d ⟨ c 0 ⟩ ,并将目前双方的高位 ⟨ x [ d , l − 1 ] ⟩ 加上 ⟨ c 0 ⟩ ,最后递归执行这一过程即可。
复杂度相当于 ( c − 1 ) 次 F w r a p 的成本,也就是 ( c − 1 ) ( λ d + 14 d ) .
F M S N Z B − P
检测第 i 个 d -bit 分块中最高非零位对应的全局 index,也可以理解成 ⌊ log 2 ( z i ) ⌋ + i ⋅ d ,这里的具体实现方法是直接 F L U T 解决。但有一个问题是当 z i = 0 时右边这个式子与左边实现的语义并不相同,作者这里选择让 z i = 0 的结果未定义,交给上层协议来解决。
这一点我不是很理解,z i = 0 的情况也可以在 F L U T 的情况直接解决啊,又不会增加通信成本。
总之,通信量等价于一个 1-out-of-2 d OT ⌈ log 2 l ⌉ 实现,因此通信量为 2 λ + 2 d ⌈ log 2 l ⌉ .
F Z e r o s , F O n e H o t
前者的意思是判断一个长度为 d 的向量(或者 d -bit 数)是否为零。而后者是将一个 ⟨ k ⟩ ∈ [ 0 , l − 1 ] 的 share,转化为一个长度为 l 的向量 share,满足和为 ( 0 , 0 , ⋯ , 1 , ⋯ , 0 ) ,其中仅有第 k 个向量为 1.
这两个分别可以用 1-out-of-2 d OT 和 1-out-of-l OT l 实现,成本分别为 2 λ + 2 d 和 2 λ + l 2 。虽然后者成本较高,但只在父协议 F M S N Z B 的最后调用一次,因此总成本还能接受。
F M S N Z B
含义是给定一个输入 ⟨ x ⟩ l ,计算出 l 的最高零位的值 k ,输出向量 share 满足和为 ( 0 , 0 , ⋯ , 1 , ⋯ , 0 ) ,其中仅有第 k 个向量为 1。 为了简单起见,这里我假设 F M S N Z B − P 是定义良好的(也就是处理了 z i = 0 的情况)。令 ι = ⌈ log 2 l ⌉ :
首先计算输入 ⟨ x ⟩ l 做 F D i g D e c 得到 ⟨ y i ⟩ d ,然后对每个 i 调用 F M S N Z B − P 得到 ⟨ u i ⟩ ι .
调用 F Z e r o s 得到布尔分享 ⟨ v i ⟩ ,用 F A N D (也就是 beaver triple)对于任意 i = c − 2 , ⋯ , 0 做 w i = w i + 1 ∧ v i + 1 . 这样 w i = ∏ j > i v j . 这样最高非零位的值便是满足 w i = 1 的从大到小的最后一个。
对最高位 digit,令 ⟨ z c − 1 ′ ⟩ ι = ⟨ u c − 1 ⟩ ι ,对于剩下 i = c − 2 , ⋯ , 0 ,z i ′ = MUX ( w i , u i ) .
最后本地算出 z ~ = ∑ i = 0 c − 1 z i ′ ,并调用 F O n e H o t 得到最终的布尔向量 { ⟨ z k ⟩ } k ∈ [ 0 , l − 1 ] 。
总结
原语
依赖的原语
功能
通信开销
F M U X
( 2 1 ) − OT l
长度l 位的三目运算符
2 λ + 2 l
F O R
F A N D , i.e. beaver triple
逻辑或
λ + 20
F E Q
( 2 m 1 ) − OT , F A N D
长度l 位的算术相等
< 3 4 λ l + 9 l
F L T / G T
( 2 m 1 ) − OT , F A N D
长度l 位的算术比较
< λ l + 14 l
F L U T
( 2 m 1 ) − OT n
长度m 位,结果 n 位的查找表
2 λ + 2 m n
F Z E x t
F W r a p , F B 2 A
m 位零扩展到 n 位
λ ( m + 1 ) + 13 m + n
F T R
F W r a p , F B 2 A
l 位截取高 l − s 位
λ ( s + 1 ) + l + 13 s
F U M u l t , F S M u l t
F C r o s s T e r m , F W r a p , F M U X
长度m , n 位的无/有符号乘
O ( λ l + l 2 )
F M S N Z B
等 F D i g D e c , F M S N Z B − P , F O n e H o t 等
长度l 位的最高非零位index
≤ λ ( 5 l − 4 ) + l 2
Primitives
我们终于构建了 secfloat 论文对应的所有基础协议,现在我们转入本篇论文构造的重要原语。
F F P c h e c k p , q
用来检查浮点数在浮点参数为 p , q (IEEE754 默认 p = 8 , q = 23 )的情况下是否上溢或者下溢。这是本文的一个创新点,就是可以自己选取浮点参数 ,不一定遵照 IEEE754 的格式。
α = (z, s, e, m)
if 1{e > 2^{p-1} - 1} then
m = 2^q; e = 2^{p-1}
if 1{z = 1} ∨ 1{e < 2 - 2^{p-1}} then
m = 0; e = 1 - 2^{p-1}; z = 1
Return (z, s, e, m)
这个逻辑符合论文对溢出的处理方式,这里大概描述一下协议是怎么跑的:
第二行由于 p 是公开的,这是一个 F G T / L T 操作;第四行还有一个 F E Q 操作。
第三行和第五行的赋值操作,由于赋值常数c 是公开的,不存在隐私问题,因此直接 P 0 = 0 , P 1 = c 即可。
if 分支本质就是 F M U X 的具体语义。
F T R S l , s
F T R S 在 F T R 的基础上,增加了对浮点数粘滞位 S 的进位判定。当被舍去的低 s 位只要有任意一位为 1 ,且 L S B ( x ) = 0 ,则执行进位操作。其功能等同于 (x >> s) | (x & (2**s - 1) != 0)。右移还是用 F T R 搞定(或者说是结合 F W r a p 与 F B 2 A ),但之后还要做一个 F Z e r o s 的操作,成本较高。
由于 F Z e r o s 也等价于判断两个 share 的和是否为 2 s ,或者两者都为0 ,因此其本质也是比较操作(或者 F W r a p 操作)。因此作者选择将 F W r a p 和 F Z e r o s 捆绑实现成 F W r a p & A l l 0 s ,成本仅仅相当于一个比较操作和一个 F A N D 的成本(这里论文有 typo,不是两个 F A N D 的成本)。
F R N T E l
F R N T E 等价于带舍入的右移 x ≫ R r 。在前文中我们讲过 IEEE 的舍入逻辑如下:
c = g ∧ ( d ∨ f ) 我们首先用 TRS(x, r-2) 确认 f 后面的位是否为 1 ,如果是就加回来以确保粘滞位判定逻辑正确。此时 x 的后三位就分别是d , g , f 。然后用 F L U T 将这个 8-bit 的舍入逻辑硬编码到结果 c 中。最后再调用 TR(x, 2) 并加上舍入结果 c 即可。
F R o u n d ∗ p , q , Q
给出浮点数 α = ( z , s , e , m ) 中的 ( e , m ) ,将已规格化的 尾数 m 从 Q 位精度舍入到 q 位精度,并处理舍入导致的进位溢出。注意这里 m 采用了定点数形式,也就是尾数 1. M 对应定点数 m = ( 1. M ) × 2 Q 。因此 m 的取值范围是 [ 2 Q , 2 Q + 1 ) 。协议逻辑如下所示:
if 1{m >= 2^{Q+1} − 2^{Q−q−1}} then
Return (e+1, 2^q)
else
Return (e, m >>R (Q−q))
以下对该代码逻辑的正确性进行说明。尾数分为两种情况:
其一,当尾数的前 q 位都为1,并且第 q + 2 位(也就是对应了 2 Q − q − 1 )也为1时。这刚好满足 RNTE 的最小舍入条件,因此这整个 Q 位都将舍入到 2.0 。做一次规范化后 e = e + 1 ,并且尾数 m 回到原来的 q 位精度规范化形式 2 q 。
其二,当不存在四舍五入后需要再次规范化的大多数情况,直接按照先前的 F R N T E 协议进行带舍入右移 Q − q 位即可。
F F P A d d p , q
在前置知识一节,我们已经概括了普通浮点加法的办法。下面我们将该方法与伪代码对应:
指数对齐。若 E x > E y ,将 x 的尾数左移: M x ← ( 1. M x ) 2 E x − E y , E ← E y .
尾数相加。考虑S x , S y 的符号情况。若同号,则计算 M = M x + M y 。若异号,则用大者减小者得到差的绝对值M = | M x − M y | ,并附上正确的符号S 。
这里考虑同号与异号的情况。同号时,β 1 与 β 2 的符号位异或为 0 ,否则为 1 。因此当 β 1 . s ⊕ β 2 . s = 1 时,将 m 2 变号完成相减操作。
规范化。这里我们找到加法结果 m 中最高有效位,为了保证有足够的空间舍入,协议在第一步时将精度为 q 的 m 1 , m 2 零扩展为 2 q + 2 位(但注意精度是 2 q + 1 )。假设 MSNZB 的结果为 k ,要将高位的1 规范化(也就是对齐后面 Round* 协议的 2 q + 1 位)需要左移 2 q + 1 − k 位,对应乘 K = 2 2 q + 1 − k 。
至于指数位的计算,由于先左移了 2 q + 1 − k 位,同时又因为精度变化又增加 q + 1 ,因此 e = e − ( 2 q + 1 − k ) + ( q + 1 ) = e + k − q 。
写入结果。最后做一轮 F R o u n d ∗ p , q , 2 q + 1 的舍入,由于 | m 1 | > | m 2 | ,符号位一定由 β 1 . s 决定。最后检查规范化的结果是否在浮点数有效范围内。
F F P M u l p , q
符号位异或。S = S x ⊕ S y .(第7行)
指数相加。E = E x + E y − b i a s .(第1行,论文这里令 b i a s = 0 )
尾数相乘。M = M x ⋅ M y ,结果在 [ 2 2 q , 2 2 q + 2 ) 范围内。(第2行)
规范化。如果结果 ≥ 2 2 q + 1 ,将 E 自增并将 M 右移一位,最后剔除 M 最高位的 1.
结果 ≥ 2 2 q + 1 走第 6 行 else 分支,否则走第四行 if 分支。
在 M = M x ⋅ M y 与 M 右移一位这两步涉及到舍入操作。(也就是第4、6行的 F R N T E l )
Math Functions
在《学数学,就这么简单》这本书中,有这样一个桥段:
F F P sin π 8 , 23
基于这样的思路,我们实际上可以细化出计算机是如何计算 sin x 的。
首先利用三角函数诱导公式,将 x 的范围缩减到 [ 0 , π / 2 ] . (Range reduction)
这是论文的做法,但实际上舍入到 [ − π / 4 , π / 4 ] 再利用二倍角公式,误差会小一个数量级。
使用泰勒展开进行近似,例如 sin x ≈ x − 1 6 x 3 + 1 120 x 5 . (Polynomial Evaluation)
使用秦九韶算法 x − 1 6 x 3 + 1 120 x 5 = ( ( 1 120 x 2 − 1 6 ) x 2 + 1 ) x ,减少乘法次数并降低误差。
这些运算就只需要浮点加法和乘法可以搞定,至于多项式的系数由于相对固定,可以通过查表解决。
我们现在对应到具体的原语 F F P sin π 8 , 23 上,也就是计算 sin π x 。步骤如下:
首先处理特殊情况:当 | x | > 2 23 时,由于 q = 23 ,超过了尾数的精度,因此 x 一定是整数。sin π x = 0 。另一种特殊情况是当 | x | < 2 − 14 时,sin π x ≈ π x ,其误差在 x 本身浮点表示的误差以内。
range reduction 步骤:目的是从输入 α 算出奇偶位 a ∈ { 0 , 1 } 以及区间 δ 。令 | α | = 2 K + a + n ,若 n < 0.5 ,则 δ = n ,否则 δ = 1 − n 。故 sin π α = ( − 1 ) a ⊕ 1 { α < 0 } sin π δ .
m = α.m * 2^{α.e + 14}
a = TR(m, q+14); n = m mod 2^{q+14}
将 α 变成一个整数。由于 | α | = 2 α . e ⋅ α . m 2 q ,故 m = α . m ⋅ 2 α . e + 14 ≈ | α | ⋅ 2 q + 14 . 则 a 为 | α | 的整数部分, n 为 | α | 的小数部分。
f = (n > 2^{q+13} ? 2^{q+14} - n : n)
k,K = MSNZB(f); f = f * K
z = 1{f=0}; e = (z ? -2^{p-1}+1 : k - q - 14)
第一行保证 f = δ ⋅ 2 q + 14 。第二行找到 f 的最高有效位,从而将 f 规范化,并在第三行设定正确的指数位 e 。
当 f = 0 时,sin π α = 0 。将零位 z 设为 1 ,并按照论文约定将指数位 e 设为 − 2 p − 1 + 1 .
δ = (z, 0, e, TR(f, q+14−Q))
最后重新将定点数 f 以 Q 位精度存储,与其他参数合并成浮点数 δ 。
polynomial evaluation 步骤,注意论文使用了 Remez 等精度更高的逼近方法,而不是泰勒展开:
if 1{δ.e < −14} then
µ = Float_{p,Q}(π) ⊗_{p,Q} δ
先前 | δ | ≤ 2 − 14 时的特殊情况,sin π x ≈ π x .
if 1{δ.e < −5} then
idx1 = δ.e + 14 mod 2^4
(θ1, θ3, θ5) = GetC_{4,9,5,p,Q}(idx1, Θ1_sin, K1_sin)
考虑 2 − 14 < | δ | < 2 − 5 时的情况,这里 GetC 函数提供了一个查表操作,通过选取 K1_sin 这一常数类,在 Θ1_sin 这个表中,通过 index idx1(也就是 δ.e+14 的低4位)获取对应的参数 (θ1, θ3, θ5)。
4 是表的 bit 数,9 是 spline(系数对)的个数,5 是拟合的多项式的次数。
idx2 = 32 · (δ.e + 5 mod 27)
idx2 = idx2 + ZXt(TR(δ.m, Q−5) mod 32, 7)
idx2 = 1{δ.e = −1} ? 127 : idx2
(θ1, θ3, θ5) = GetC_{7,34,5,p,Q}(idx2, Θ2_sin, K2_sin)
在 2 − 5 ≤ | δ | < 0.5 这个区间,用δ.e + 5的低2位和δ.m的高5位进行查表的区分(这是论文的第二个创新点:分段索引 ),来调用对应误差最小的 (θ1, θ3, θ5)。对 | δ | = 0.5 的区间,进行特判。
使用秦九韶算法求出对应的浮点值:
Δ = δ ⊗_{p,Q} δ
µ = ((θ5 ⊗ Δ) ⊞* θ3) ⊗ Δ
µ = (µ ⊞* θ1) ⊗ δ
Return (µ.z, a ⊕ α.s, Round*(µ.e,µ.m))
Δ = δ 2
μ = ( θ 5 Δ + θ 3 ) Δ = θ 5 δ 4 + θ 3 δ 2
μ = ( μ + θ 1 ) δ = θ 5 δ 5 + θ 3 δ 3 + θ 1 δ ≈ sin π δ
前文说过:sin π α = ( − 1 ) a ⊕ 1 { α < 0 } sin π δ ,故 a ⊕ α.s 确定符号位,最后舍入即可得到最终结果。
大概总结一下,F F P sin π 8 , 23 先对输入做范围归约,把问题化到0到0.5的小区间并记录符号补偿;对极大或极小输入走快速分支。随后按指数和尾数位确定分段,用查表选出对应多项式系数,采用秦九韶算法计算近似值,最后做符号恢复并舍入到单精度输出。
这个方法一个潜在的缺点是,这个 GetC 函数用的表是 ad-hoc 的,与前面浮点四则运算的协议不同,如果想要任意指定p , q ,或者迁移到 SecDouble ,这个系数表需要进行重新计算才能使得误差小于 1ULP。论文为了追求性能与准确度,牺牲了算法的灵活性。
F F P log 2 8 , 23
令 α = m ⋅ 2 N , m ∈ [ 1 , 2 ) , N = α . e ,令 a = 1 { N = − 1 } ,则分为两种情况:
N ≠ − 1 ,令 δ = m − 1 ,则 δ ∈ [ 0 , 1 ) ,则 log 2 α = log 2 m + N = log 2 ( 1 + δ ) + N .
N = − 1 ,此时若 m ≈ 2 ,log 2 m + N 会趋近于零,两个大数相减,精度将会受到影响。因此作者在此变形为 log 2 α = log 2 ( m / 2 ) = log 2 ( 1 − ( 1 − m / 2 ) ) 。令 δ ′ = 1 − m / 2 ,则 δ ′ ∈ ( 0 , 0.5 ] ,再计算 log 2 ( 1 − δ ′ ) ,这样就不存在两数相减导致的误差问题了。
log 2 ( α ) = { N + log 2 ( 1 + δ ) a = 0 , log 2 ( 1 − δ ′ ) a = 1. 然后,分别按照 a = 0 与 a = 1 构造两组近似多项式 ( θ 0 a , θ 1 a , θ 2 a , θ 3 a ) ,( θ 0 b , θ 1 b , θ 2 b , θ 3 b ) ,还是按照分段索引法对指定范围的 δ . e 与 δ . m 进行查表。
然后通过秦九韶算法(Horner's method)计算出 log 2 ( 1 + ⋅ ) 的值。对于最后加 N 的计算,首先通过查表方式将 N 转化为浮点数,再调用 F F P A d d p , q 协议即可。
range reduction 步骤:
a = 1{N = −1}
f = a ? (2^{q+1} − α.m) : (α.m − 2^q)
k,K = MSNZB(f); f = f *_{q+1} K
e = a ? (k − q − 1) : (k − q)
当 N = − 1 时(此时 a = 1 ),f = 1 − m / 2 ,否则(此时 a = 0 )f = m − 1 ,并将 f 规范化。
z = 1{f = 0}; e = (z ? −2^{p−1}+1 : e);
N = α.e; δ = (z,0,e, f *_{Q+1} 2^{Q−q})
通过 f 构造出浮点数 δ ,并将精度从 q = 23 位提高到 Q = 27 位,为后文 evaluation 准备。
polynomial evaluation 步骤
if 1{δ.z} then
µ = Float_{p,Q}(0)
当 δ = 0 时,log 2 ( 1 + ⋅ ) = log 2 1 = 0 ,故返回 μ = 0 。
if 1{δ.e < −5} then
idx1 = (δ.e + 24) mod 2^5
(θa0,θa1,θa2,θa3) = GetC_{5,19,4,p,Q}(idx1, Θ1_log, K1_log)
(θb0,θb1,θb2,θb3) = GetC_{5,18,4,p,Q}(idx1, Θ3_log, K3_log)
当 2 − 24 ≤ δ < 2 − 5 时,使用 δ.e + 24 的低5位作为 index,对 a = 1 的情况查表 Θ1_log,对 a = 0 的情况查表 Θ3_log,这里为了避免数据依赖必须两个都做。
else
idx2 = 16 · (δ.e + 5 mod 2^7)
idx2 = idx2 + ZXt(TR(δ.m, Q−4) mod 16, 7)
(θa0,θa1,θa2,θa3) = GetC_{7,20,4,p,Q}(idx2, Θ2_log, K2_log)
(θb0,θb1,θb2,θb3) = GetC_{7,32,4,p,Q}(idx2, Θ4_log, K4_log)
当 2 − 5 ≤ δ < 1 时,使用 δ.e + 5 的低3位与 δ.m 的高4位作为 index,对 a = 1 的情况查表 Θ2_log,对 a = 0 的情况查表 Θ4_log。
(θ0,θ1,θ2,θ3) = a ? (θa0,θa1,θa2,θa3) : (θb0,θb1,θb2,θb3)
根据前文对 a 值的分类讨论,选择最终的多项式系数。
秦九韶算法(Horner)步骤及最后加N
µ = ((θ3 ⊗ δ) ⊞* θ2) ⊗ δ
µ = ((µ ⊞* θ1) ⊗ δ) ⊞* θ0
计算出三次多项式 μ = θ 3 δ 3 + θ 2 δ 2 + θ 1 δ + θ 0 = log 2 ( 1 + ⋅ ) 的值。
β = LInt2Float(N)
β′ = (β.z,β.s,β.e, β.m *_{Q+1} 2^{Q-6})
将 N 使用小 LUT,将 N 转化为低精度(尾数为6位)的浮点形式 β ,再转化为高精度 Q 的形式 β ′ 。
γ = a ? µ : (µ ⊞* β′)
Return (γ.z, γ.s, Round*(γ.e, γ.m))
最后执行浮点加法:
若 a = 0 执行 γ = μ + β ′ = log 2 ( 1 + ⋅ ) + N ,
若 a = 1 执行 γ = μ = log 2 ( 1 − ⋅ ) 。
最终对 γ 舍入得到协议结果。