LAMMP 4.2.0
Lamina High-Precision Arithmetic Library
载入中...
搜索中...
未找到
lglg.h
浏览该文件的文档.
1/**
2 * Copyright (C) 2026 HJimmyK(Jericho Knox)
3 *
4 * This file is part of LAMMP.
5 *
6 * LAMMP is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License (LGPL) as published
8 * by the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed WITHOUT ANY WARRANTY.
12 *
13 * See <https://www.gnu.org/licenses/>.
14 */
15
16#ifndef __LAMMP_LGLG_H__
17#define __LAMMP_LGLG_H__
18
19#include "longlong.h"
20#include "../lmmp.h"
21#include <stdint.h>
22
23
24// 索引i处对应的数值为:round(log2(1+(i+1)/512) * 2^32)
25// 除了最后一个元素(最后一个即log2(2)*2^32,受限于32位整数,取为2^32-1)
26extern const uint32_t log2_fix32_q9[512];
27
28#define tab(i) log2_fix32_q9[i - 1]
29#define H 0x400000 // 插值点距离
30#define adj_H 0x200000
31
32/*
33 为了不让你读这里的代码时,发出fuck的声音,我写下这段注释帮助你理解。
34
35 首先,主要有两类计算:
36 1. 计算x*log2(n)的ceil值
37 2. 计算log2(gamma(n))的ceil和floor值
38 由于是用于估计64位字的缓冲区长度。所以只需要上界不低估,下界不高估即可。
39
40 log2_fix32_q9 数组,存储了log2(1+(i+1)/512) * 2^32的值,其中i从0到511。
41 除了最后一位,我们向下舍入到0xffffffff,因为log2(1+(511+1)/512) * 2^32
42 的实际值为0x100000000。我们假设要计算log2(n),我们假定已经把n移位至MSB(n)=1
43 我们使用log2_fix32_q9[idx]来进行线性插值,当然为了保证不低估,我们采用割线插值。
44 n的形式为:1.xxxxxxxxx... * 2^31 其中我们根据xxx段(9bit),匹配到log2_fix32_q9[idx]。
45 找到临近的插值点,靠近左边,则使用左边两个插值点进行插值(由于log2(x)是凹函数,
46 在插值点右边的n一定大于实际值);靠近右边,则使用右边两个插值点进行插值。同时,
47 我们对两端边界情况也进行了处理。
48
49 对于gamma函数,我们使用斯特林公式 gamma(x) ~ sqrt(2pi*x)*(x/e)^x
50 log2(gamma(x)) ~ (x+1/2)*log2(x) - x*log2(e) + log2(sqrt(2pi))
51 由于斯特林公式产生的相对误差是随x的增大而减小,且我们最终需要取整,尾部的常数
52 我们直接忽略即可。
53 实际计算式为:log2(gamma(x)) = {(2x+1)*log2(x) - 2*[x + x*(log2(e)-1)] + log2(2pi)} / 2
54
55 中间变量最大值产生在(2x+1)*log2(x),其中log2(x)最多为5bit,乘2需要1个bit,同时,
56 由于插值公式本身就存在误差,所以中间计算时,我们只需要保留26个bit的信息即可。
57*/
58
59/**
60 * @brief 计算x*log2(n)的ceil值
61 * @param n 底数
62 * @param x 乘数
63 * @warning MSB(n)=1
64 * @note 大约产生10^-7相对误差
65 * @return n*log2(x)的ceil值
66 */
67static inline uint64_t xlog2n_ceil(uint32_t x, uint32_t n) {
68 /*
69 此函数虽然大部分计算和log2n_2n1_ceil相同,但后者由于有其他函数配合误差控制,
70 在这个函数中,为了保证不低估,我们使用了更加严格的计算,比如使用adj_H来调控
71 除以H时的除法误差,并且末尾的调整也更加严格。
72 */
73 n &= 0x7fffffff;
74 uint32_t idx = n >> 22;
75
76 uint32_t idx1 = (idx) << 22;
77 uint32_t idx2 = (idx + 1) << 22;
78
79 uint64_t r;
80 uint64_t x64 = (uint64_t)x;
81 if ((idx != 511 && 2 * n >= (idx1 + idx2)) || idx == 0 || idx == 1) {
82 // 使用右边两个插值点进行插值
83 uint64_t y1 = tab(idx + 1);
84 uint64_t y2 = tab(idx + 2);
85 idx1 = (idx + 1) << 22;
86 idx2 = (idx + 2) << 22;
87 int32_t x2x = idx2 - n;
88 int32_t x1x = idx1 - n;
89 r = (y1 * x2x - y2 * x1x);
90 r += adj_H;
91 r /= H;
92 } else {
93 // 使用左边两个插值点进行插值
94 uint64_t y1 = tab(idx - 1);
95 uint64_t y2 = tab(idx);
96 idx1 = (idx - 1) << 22;
97 idx2 = (idx) << 22;
98 int32_t x2x = n - idx2;
99 int32_t x1x = n - idx1;
100 r = (y2 * x1x - y1 * x2x);
101 r += adj_H;
102 r /= H;
103 // 这是插值结果可能高过log2(2),我们直接截断数据
104 r = (r >= 0x100000000) ? 0x100000000 : r;
105 }
106 r *= x;
107 r >>= 6;
108 x64 *= 31;
109 x64 <<= 26;
110 r += x64;
111 int adj = (r & 0x3FFFFFF) ? 2 : 1;
112 return (r >> 26) + adj;
113}
114
115/**
116 * @brief 计算(2n+1)*log2(n)的ceil值
117 * @param n 底数
118 * @warning n>1
119 * @return (2n+1)*log2(n)的定点数格式,低位26位为小数
120 */
121static inline uint64_t log2n_2n1_ceil(uint32_t n) {
122 uint32_t x = n;
123 int msb;
124 clz_shl_u32(n, n, msb);
125 n &= 0x7fffffff;
126 uint32_t idx = n >> 22;
127
128 uint32_t idx1 = (idx) << 22;
129 uint32_t idx2 = (idx + 1) << 22;
130
131 uint64_t r, r2;
132 if ((idx != 511 && 2 * n >= (idx1 + idx2)) || idx == 0 || idx == 1) {
133 // 使用右边两个插值点进行插值
134 uint64_t y1 = tab(idx + 1);
135 uint64_t y2 = tab(idx + 2);
136 idx1 = (idx + 1) << 22;
137 idx2 = (idx + 2) << 22;
138 int32_t x2x = idx2 - n;
139 int32_t x1x = idx1 - n;
140
141 r = (y1 * x2x - y2 * x1x);
142 r /= H;
143 } else {
144 // 使用左边两个插值点进行插值
145 uint64_t y1 = tab(idx - 1);
146 uint64_t y2 = tab(idx);
147 idx1 = (idx - 1) << 22;
148 idx2 = (idx) << 22;
149 int32_t x2x = n - idx2;
150 int32_t x1x = n - idx1;
151
152 r = (y2 * x1x - y1 * x2x);
153 r /= H;
154 r = (r >= 0x100000000) ? 0xffffffff : r;
155 }
156 /*
157 n = x = n' * 2^k,k = 31 - msb
158 我们要计算的是 (2n+1)log2(n) 即 (2x+1)log2(n') + (2x+1)*k
159 */
160 r2 = r >> 6;
161 r *= x;
162 r >>= 6;
163 uint64_t ret = 2 * r + r2;
164 uint64_t x64 = (uint64_t)x * 2 + 1;
165 x64 *= (31 - msb);
166 x64 <<= 26;
167 return ret + x64;
168}
169
170/**
171 * @brief 计算(2n+1)*log2(n)的floor值
172 * @param n 底数
173 * @warning n>1
174 * @return (2n+1)*log2(n)的定点数格式,低位26位为小数
175 */
176static inline uint64_t log2n_2n1_floor(uint32_t n) {
177 uint32_t x = n;
178 int msb;
179 clz_shl_u32(n, n, msb);
180 n &= 0x7fffffff;
181 uint32_t idx = n >> 22;
182
183 uint32_t idx1 = (idx) << 22;
184 // 直接取相邻两点插值,此时,插值线性函数必定在log2(x)下方
185 uint64_t r, r2, y1, y2;
186 if (idx == 0) {
187 // 索引0对应的值即log2(1+(0)/512) * 2^32 = 0
188 y1 = 0;
189 y2 = tab(1);
190 } else {
191 y1 = tab(idx);
192 y2 = tab(idx + 1);
193 y2 -= y1;
194 }
195 uint32_t x1x = n - idx1;
196 r = y2 * x1x / H;
197 r += y1;
198 /*
199 n = x = n' * 2^k,k = 31 - msb
200 我们要计算的是 (2n+1)log2(n) 即 (2x+1)log2(n') + (2x+1)*k
201 */
202 r2 = r >> 6;
203 r *= x;
204 r >>= 6;
205 uint64_t ret = 2 * r + r2;
206 uint64_t x64 = (uint64_t)x * 2 + 1;
207 x64 *= (31 - msb);
208 x64 <<= 26;
209 return ret + x64;
210}
211#undef tab
212#undef H
213#undef adj_H
214
215/**
216 * @brief 计算 n * (log2(e)-1)
217 * @return n * (log2(e)-1)的定点数格式,低位32位为小数
218 */
219static inline uint64_t mul_log2e_1_ceil(uint32_t n) {
220 uint64_t r = 1901360723; // ceil((log2(e)-1)*2^32)
221 r *= n;
222 return r;
223}
224
225/**
226 * @brief 计算 n * (log2(e)-1)
227 * @return n * (log2(e)-1)的定点数格式,低位32位为小数
228 */
229static inline uint64_t mul_log2e_1_floor(uint32_t n) {
230 uint64_t r = 1901360722; // floor((log2(e)-1)*2^32)
231 r *= n;
232 return r;
233}
234
235/**
236 * @brief 计算 log2(n!)的ceil值
237 * @param n 底数
238 * @warning n > 2
239 * @return log2(n!)的ceil值
240 */
241static inline uint64_t log2_fac_ceil(uint32_t n) {
242 uint64_t r3 = (uint64_t)n << 26;
243 uint64_t r4 = mul_log2e_1_floor(n);
244
245 r4 >>= 6;
246 uint64_t r = log2n_2n1_ceil(n);
247 r -= (r3 + r4) * 2;
248 r += 177938894; // 177938894 = ceil(log2(2pi)*2^26)
249 int adj = (r & 0x3FFFFFF) ? 1 : 0;
250 r >>= 26;
251 return (1 + r) / 2 + adj;
252}
253
254/**
255 * @brief 计算 log2(n!)的floor值
256 * @param n 底数
257 * @warning n > 2
258 * @return log2(n!)的floor值
259 */
260static inline uint64_t log2_fac_floor(uint32_t n) {
261 uint64_t r3 = (uint64_t)n << 26;
262 uint64_t r4 = mul_log2e_1_ceil(n);
263
264 r4 >>= 6;
265 uint64_t r = log2n_2n1_floor(n);
266 r -= (r3 + r4) * 2;
267 r += 177938893; // 177938893 = floor(log2(2pi)*2^26)
268 r >>= 26;
269 return r / 2;
270}
271
272#endif // __LAMMP_LGLG_H__
#define tab(i)
Definition lglg.h:28
static uint64_t log2n_2n1_ceil(uint32_t n)
计算(2n+1)*log2(n)的ceil值
Definition lglg.h:121
static uint64_t log2n_2n1_floor(uint32_t n)
计算(2n+1)*log2(n)的floor值
Definition lglg.h:176
static uint64_t mul_log2e_1_ceil(uint32_t n)
计算 n * (log2(e)-1)
Definition lglg.h:219
static uint64_t log2_fac_ceil(uint32_t n)
计算 log2(n!)的ceil值
Definition lglg.h:241
static uint64_t log2_fac_floor(uint32_t n)
计算 log2(n!)的floor值
Definition lglg.h:260
#define adj_H
Definition lglg.h:30
static uint64_t xlog2n_ceil(uint32_t x, uint32_t n)
计算x*log2(n)的ceil值
Definition lglg.h:67
const uint32_t log2_fix32_q9[512]
Copyright (C) 2026 HJimmyK(Jericho Knox)
Definition lglg.c:18
#define H
Definition lglg.h:29
static uint64_t mul_log2e_1_floor(uint32_t n)
计算 n * (log2(e)-1)
Definition lglg.h:229
#define clz_shl_u32(r, x, cnt)
Definition longlong.h:161
#define r2
#define r3
#define n
#define r4