Skip to content

Commit ec21dd5

Browse files
committed
Add scaling exponent argument to series and mpf2multiword
1 parent 63177d3 commit ec21dd5

File tree

3 files changed

+89
-22
lines changed

3 files changed

+89
-22
lines changed

functional_algorithms/floating_point_algorithms.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,16 @@ def get_is_power_of_two_constants(ctx, largest: float):
129129
fp64 = ctx.constant(1 << (53 - 1), largest)
130130
fp32 = ctx.constant(1 << (24 - 1), largest)
131131
fp16 = ctx.constant(1 << (11 - 1), largest)
132-
Q = ctx.select(largest > 1e308, fp64, ctx.select(largest > 1e38, fp32, fp16)).reference("Qispowof2", force=True)
132+
Q = ctx.select(largest > 1e308, fp64, ctx.select(largest > 1e38, fp32, fp16))
133133

134134
fp64 = ctx.constant(1 << (53 - 1) + 1, largest)
135135
fp32 = ctx.constant(1 << (24 - 1) + 1, largest)
136136
fp16 = ctx.constant(1 << (11 - 1) + 1, largest)
137-
P = ctx.select(largest > 1e308, fp64, ctx.select(largest > 1e38, fp32, fp16)).reference("Pispowof2", force=True)
137+
P = ctx.select(largest > 1e308, fp64, ctx.select(largest > 1e38, fp32, fp16))
138+
139+
if hasattr(Q, "reference"):
140+
Q = Q.reference("Qispowof2", force=True)
141+
P = P.reference("Pispowof2", force=True)
138142
return Q, P
139143

140144

@@ -395,11 +399,15 @@ def add_3sum(ctx, x, y, z, Q, P, three_over_two):
395399
return s, e, t
396400

397401

398-
def add_dw(ctx, xh, xl, yh, yl, Q, P, three_over_two):
402+
def add_dw(ctx, xh, xl, yh, yl, Q=None, P=None, three_over_two=None):
399403
"""Add two double-word numbers:
400404
401405
xh + xl + yh + yl = s
402406
"""
407+
if Q is None:
408+
largest = get_largest(ctx, xh)
409+
Q, P = get_is_power_of_two_constants(ctx, largest)
410+
three_over_two = ctx.constant(1.5, largest)
403411
sh, sl = add_2sum(ctx, xh, yh)
404412
th, tl = add_2sum(ctx, xl, yl)
405413
gh, gl = add_2sum(ctx, sl, th)
@@ -419,7 +427,7 @@ def add_dw(ctx, xh, xl, yh, yl, Q, P, three_over_two):
419427
)
420428

421429

422-
def add_4sum(ctx, x, y, z, w, Q, P, three_over_two):
430+
def add_4sum(ctx, x, y, z, w, Q=None, P=None, three_over_two=None):
423431
"""Add four numbers:
424432
425433
x + y + z + w = s
@@ -432,6 +440,10 @@ def add_4sum(ctx, x, y, z, w, Q, P, three_over_two):
432440
Note:
433441
The accuracy of `s` is higher than that of `x + y + z + w`.
434442
"""
443+
if Q is None:
444+
largest = get_largest(ctx, x)
445+
Q, P = get_is_power_of_two_constants(ctx, largest)
446+
three_over_two = ctx.constant(1.5, largest)
435447
xh, xl = add_2sum(ctx, x, y)
436448
yh, yl = add_2sum(ctx, z, w)
437449
return add_dw(ctx, xh, xl, yh, yl, Q, P, three_over_two)

functional_algorithms/rewrite.py

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1092,19 +1092,11 @@ class ReplaceSeries:
10921092

10931093
def __rewrite_modifier__(self, expr):
10941094
if expr.kind == "series":
1095-
s = None
1095+
from functional_algorithms import floating_point_algorithms as fpa
1096+
10961097
index, sexp = expr.operands[0]
1097-
for i, t in enumerate(reversed(expr.operands[1:])):
1098-
i = len(expr.operands[1:]) - 1 - i + index
1099-
if sexp == 0 or i == 0:
1100-
if s is None:
1101-
s = t
1102-
else:
1103-
s += t
1104-
else:
1105-
if s is None:
1106-
s = t * expr.context.constant(2 ** (-i * sexp), t)
1107-
else:
1108-
s += t * expr.context.constant(2 ** (-i * sexp), t)
1109-
return s
1098+
S = expr.context.constant(2 ** (-sexp), expr)
1099+
if sexp == 0:
1100+
return sum(reversed(expr.operands[1:-1]), expr.operands[-1])
1101+
return fpa.fast_polynomial(expr.context, S, expr.operands[1:], reverse=False, scheme=[None, fpa.horner_scheme][1])
11101102
return expr

functional_algorithms/utils.py

Lines changed: 67 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2064,10 +2064,10 @@ def multiword2mpf(ctx, mw):
20642064
return s
20652065

20662066

2067-
def mpf2multiword(dtype, x, p=None, max_length=None):
2067+
def mpf2multiword(dtype, x, p=None, max_length=None, sexp=0):
20682068
"""Return a list of fixed-width floating point numbers such that
20692069
2070-
x == sum([float2mpf(mpmath.mp, v) for v in result]) + O(smallest subnormal of dtype)
2070+
x == sum([float2mpf(mpmath.mp, v) / (2 ** (i * sexp)) for i, v in enumerate(result)]) + O(...)
20712071
20722072
where x is a mpmath mpf instance.
20732073
@@ -2096,6 +2096,69 @@ def mpf2multiword(dtype, x, p=None, max_length=None):
20962096
len(result) <= max_length
20972097
20982098
holds.
2099+
2100+
The truncation from the limited exponent size can be avoided by
2101+
specifying positive value to the scaling exponent argument sexp:
2102+
2103+
Take x = pi:
2104+
p | sexp | maximal x precision in bits
2105+
--------+-------------+----------------------------
2106+
float16
2107+
2 | 0,1,... | 11,11,...
2108+
3 | 0,1,...[4] | 26,38,49,49,[49],40,25,18,17,11,...
2109+
4 | 0,1,...[5] | 26,33,42,65,105,[105],63,34,24,20,...
2110+
5 | 0,1,...[6] | 26,33,38,47,78,105,[105],69,33,29,...
2111+
6 | 0,1,...[7] | 26,30,38,44,59,82,105,[105],73,38,
2112+
7 | 0,1,...[8] | 26,30,34,42,54,61,87,150,[800],109,44,...
2113+
8 | 0,1,...[9] | 26,29,33,38,42,53,63,76,122,[2978],187,...
2114+
9 | 0,1,...[11] | ...,99,106,135,208,[357],99,...
2115+
10 | 0,1,...[11] | ...,155,[1096],249,99,...
2116+
11 | 0,1,...[13] | ...,99,114,208,451,150,...
2117+
float32
2118+
4 | 0,1,...[10] | ...,105,105,...,[105],103,99,...
2119+
5 | 0,1,...[13] | ...,105,105,...,[105],93,...
2120+
6 | 0,1,...[16] | ...,105,105,...,[105],92,...
2121+
7 | 0,1,...[9] | ...,299,396,602,912,[912],871,...
2122+
8 | 0,1,...[9] | ...,676,1343,[4280],1061,....
2123+
9 | 0,1,...[10] | ...,1205,[3726],1600,...
2124+
10 | 0,1,...[11] | ...,1851,[16429],1182
2125+
11 | 0,1,...[12] | ...,1462,[>44000],1797
2126+
12 | 0,1,...[13] | ...,1823,[>50000],...
2127+
13 | 0,1,...[14] | ...,1609,[>50000],1987,...
2128+
...
2129+
24 | 0,1,...[25] | ...,1738,3202,[>50000],...
2130+
float64
2131+
8 | 0,1,...[11] | ...,3188,4280,4280,4280,4280,[4280],3129,...
2132+
12 | 0,1,...[13] | ...,13847,[>50000],13356,...
2133+
...
2134+
53 | 0,1,...,53,[..] | ...,55030,[...],...
2135+
2136+
From the above table follows a relation
2137+
2138+
sexp == p + 1
2139+
2140+
that maximizes the precision of x that can be represented exactly
2141+
as a multiword.
2142+
2143+
The maximal bit-size of the multiword representation depends on
2144+
the value of x. For example, take sexp = p + 1, then for
2145+
p=1,...,11 the maximal bit-size sequence is as follows [float16]:
2146+
2147+
pi : 4,4, 49,105,105,105,800,2978, 208,1096, 208
2148+
log(2): 6,6,101, 19,101,139,101, 107,1345, 830, 596
2149+
2 / pi: 5,6, 6,129,127,322,324,2299,1093,1127,1339
2150+
2151+
that is, the optimal value for p also depends on the value of x.
2152+
2153+
The dtype dependence of the maximal bit-size sequence is as
2154+
follows [2 / pi]:
2155+
2156+
float16: 5,6,6,129,127,322,324,2299,1093,1127,1339
2157+
float32: 5,6,6,129,127,322,324,2299,2835,[>5000]
2158+
float64: 5,6,6,129,127,322,324,2299,[>5000]
2159+
2160+
that is, the optimal value for p increases with increasing dtype
2161+
bitsize.
20992162
"""
21002163
sign, man, exp, bc = x._mpf_
21012164
mpf = x.context.mpf
@@ -2122,9 +2185,9 @@ def mpf2multiword(dtype, x, p=None, max_length=None):
21222185
offset -= d
21232186
man1 = (man & (mask << offset)) >> offset
21242187
bl1 = man1.bit_length()
2125-
exp1 = exp + offset
2188+
exp1 = exp + offset + len(result) * sexp
21262189
x1 = mpf2float(dtype, mpf((sign, man1, exp1, bl1)))
2127-
if x1 == dtype(0):
2190+
if x1 == dtype(0) and offset == 0:
21282191
# result represents truncated x
21292192
break
21302193
result.append(x1)

0 commit comments

Comments
 (0)