Skip to content

Commit 106fb4c

Browse files
add d_mul_2exp and replace slow ldexp calls
1 parent 5476e2a commit 106fb4c

File tree

20 files changed

+286
-93
lines changed

20 files changed

+286
-93
lines changed

doc/source/d_vec.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ Comparison
6565
are within ``eps`` of each other, otherwise returns `0`.
6666

6767

68-
Addition and subtraction
68+
Arithmetic
6969
--------------------------------------------------------------------------------
7070

7171

@@ -79,6 +79,11 @@ Addition and subtraction
7979
Sets ``(res, len2)`` to ``(vec1, len2)`` minus ``(vec2, len2)``.
8080

8181

82+
.. function:: void _d_vec_mul_2exp(double * res, const double * vec, slong len, int e)
83+
84+
Sets ``(res, len)`` to ``(vec, len)`` multiplied by `2^e`.
85+
86+
8287
Dot product and norm
8388
--------------------------------------------------------------------------------
8489

doc/source/double_extras.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,17 @@ Arithmetic
3333
``len`` coefficients. Requires that ``len`` is nonzero.
3434

3535

36+
.. function:: double d_mul_2exp_inrange(double x, int i)
37+
double d_mul_2exp_inrange2(double x, int i)
38+
double d_mul_2exp(double x, int i)
39+
40+
Returns `x \cdot 2^i`.
41+
42+
The *inrange* version requires that `2^i` is in the normal exponent
43+
range. The *inrange2* version additionally requires that both
44+
`x` and `x \cdot 2^i` are in the normal exponent range,
45+
and in particular also assumes that `x \ne 0`.
46+
3647

3748
Special functions
3849
--------------------------------------------------------------------------------

src/arb_mat/addmul_rad_mag_fast.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
# include <math.h>
1919
#endif
2020

21+
#include "double_extras.h"
22+
2123
/* Block size for better cache locality. */
2224
#define BLOCK_SIZE 32
2325

@@ -101,7 +103,7 @@ static inline slong _mag_get_exp(const mag_t x)
101103
static double
102104
mag_get_d_fixed_si(const mag_t x, slong e)
103105
{
104-
return ldexp(MAG_MAN(x), MAG_EXP(x) - e - MAG_BITS);
106+
return d_mul_2exp(MAG_MAN(x), MAG_EXP(x) - e - MAG_BITS);
105107
}
106108

107109
void

src/arf/get.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ arf_get_d(const arf_t x, arf_rnd_t rnd)
9797
else
9898
v = (double)(tp[1]) + (double)(tp[0]) * ldexp(1,-32);
9999

100-
v = ldexp(v, ARF_EXP(t) - FLINT_BITS);
100+
v = d_mul_2exp(v, ARF_EXP(t) - FLINT_BITS);
101101

102102
if (ARF_SGNBIT(t))
103103
v = -v;

src/d_vec.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,14 @@
1212
#ifndef D_VEC_H
1313
#define D_VEC_H
1414

15+
#ifdef D_VEC_INLINES_C
16+
#define D_VEC_INLINE
17+
#else
18+
#define D_VEC_INLINE static inline
19+
#endif
20+
1521
#include "flint.h"
22+
#include "double_extras.h"
1623

1724
#ifdef __cplusplus
1825
extern "C" {
@@ -45,12 +52,32 @@ int _d_vec_is_zero(const double * vec, slong len);
4552

4653
int _d_vec_is_approx_zero(const double * vec, slong len, double eps);
4754

48-
/* Addition ****************************************************************/
55+
/* Arithmetic ****************************************************************/
4956

5057
void _d_vec_add(double * res, const double * vec1, const double * vec2, slong len2);
5158

5259
void _d_vec_sub(double * res, const double * vec1, const double * vec2, slong len2);
5360

61+
D_VEC_INLINE
62+
void _d_vec_mul_2exp(double * res, const double * x, slong len, int e)
63+
{
64+
slong i;
65+
66+
if (e >= D_MIN_NORMAL_EXPONENT && e <= D_MAX_NORMAL_EXPONENT)
67+
{
68+
double_uint64_u u;
69+
u.i = ((int64_t) (e + D_EXPONENT_BIAS)) << D_EXPONENT_SHIFT;
70+
71+
for (i = 0; i < len; i++)
72+
res[i] = x[i] * u.f;
73+
}
74+
else
75+
{
76+
for (i = 0; i < len; i++)
77+
res[i] = ldexp(x[i], e);
78+
}
79+
}
80+
5481
/* Dot product and norm **************************************/
5582

5683
double _d_vec_dot(const double * vec1, const double * vec2, slong len2);

src/d_vec/dot_heuristic.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ _d_vec_dot_heuristic(const double *vec1, const double *vec2, slong len2,
3636
{
3737
p = frexp(psum, &pexp);
3838
n = frexp(nsum, &nexp);
39-
p = ldexp(1.0, pexp - D_BITS);
40-
n = ldexp(1.0, nexp - D_BITS);
39+
p = d_mul_2exp(1.0, pexp - D_BITS);
40+
n = d_mul_2exp(1.0, nexp - D_BITS);
4141
d = FLINT_MAX(p, n);
4242
*err = 2 * len2 * d;
4343
}
Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/*
2-
Copyright (C) 2012 Fredrik Johansson
3-
Copyright (C) 2014 Abhinav Baid
2+
Copyright (C) 2015 William Hart
43
54
This file is part of FLINT.
65
@@ -10,13 +9,7 @@
109
(at your option) any later version. See <https://www.gnu.org/licenses/>.
1110
*/
1211

13-
#include "double_extras.h"
12+
#define D_VEC_INLINES_C
1413

15-
int
16-
d_is_nan(double x)
17-
{
18-
if (x != x)
19-
return 1;
20-
else
21-
return 0;
22-
}
14+
#include "flint.h"
15+
#include "d_vec.h"

src/double_extras.h

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#define DOUBLE_EXTRAS_INLINE static inline
1919
#endif
2020

21+
#include <stdint.h>
2122
#include <math.h>
2223
#include "flint.h"
2324

@@ -50,10 +51,56 @@ double d_polyval(const double * poly, int len, double x)
5051

5152
double d_lambertw(double x);
5253

53-
int d_is_nan(double x);
54+
DOUBLE_EXTRAS_INLINE
55+
int d_is_nan(double x)
56+
{
57+
return x != x;
58+
}
5459

5560
double d_log2(double x);
5661

62+
typedef union
63+
{
64+
double f;
65+
uint64_t i;
66+
}
67+
double_uint64_u;
68+
69+
#define D_MIN_NORMAL_EXPONENT -1022
70+
#define D_MAX_NORMAL_EXPONENT 1023
71+
#define D_EXPONENT_BIAS 1023
72+
#define D_EXPONENT_SHIFT 52
73+
74+
/* Assumes that 2^i is in the normal exponent range. */
75+
FLINT_FORCE_INLINE double d_mul_2exp_inrange(double x, int i)
76+
{
77+
FLINT_ASSERT(i >= D_MIN_NORMAL_EXPONENT && i <= D_MAX_NORMAL_EXPONENT);
78+
double_uint64_u u;
79+
u.i = ((int64_t) (i + D_EXPONENT_BIAS)) << D_EXPONENT_SHIFT;
80+
return x * u.f;
81+
}
82+
83+
/* Assumes that 2^i, x and x*2^i are all in the normal exponent range. */
84+
/* In particular, also assumes x != 0. */
85+
FLINT_FORCE_INLINE double d_mul_2exp_inrange2(double x, int i)
86+
{
87+
FLINT_ASSERT(i >= D_MIN_NORMAL_EXPONENT && i <= D_MAX_NORMAL_EXPONENT);
88+
FLINT_ASSERT(x != 0);
89+
90+
double_uint64_u u;
91+
u.f = x;
92+
u.i += ((int64_t) i) << D_EXPONENT_SHIFT;
93+
return u.f;
94+
}
95+
96+
FLINT_FORCE_INLINE double d_mul_2exp(double x, int i)
97+
{
98+
if (i >= D_MIN_NORMAL_EXPONENT && i <= D_MAX_NORMAL_EXPONENT)
99+
return d_mul_2exp_inrange(x, i);
100+
else
101+
return ldexp(x, i);
102+
}
103+
57104
#ifdef __cplusplus
58105
}
59106
#endif

src/double_extras/test/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "t-is_nan.c"
1818
#include "t-lambertw.c"
1919
#include "t-log2.c"
20+
#include "t-mul_2exp.c"
2021
#include "t-randtest.c"
2122
#include "t-randtest_signed.c"
2223

@@ -27,6 +28,7 @@ test_struct tests[] =
2728
TEST_FUNCTION(d_is_nan),
2829
TEST_FUNCTION(d_lambertw),
2930
TEST_FUNCTION(d_log2),
31+
TEST_FUNCTION(d_mul_2exp),
3032
TEST_FUNCTION(d_randtest),
3133
TEST_FUNCTION(d_randtest_signed)
3234
};
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Copyright (C) 2024 Fredrik Johansson
3+
4+
This file is part of FLINT.
5+
6+
FLINT 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 2.1 of the License, or
9+
(at your option) any later version. See <https://www.gnu.org/licenses/>.
10+
*/
11+
12+
#include "test_helpers.h"
13+
#include <float.h>
14+
#include "ulong_extras.h"
15+
#include "double_extras.h"
16+
#include "d_vec.h"
17+
18+
TEST_FUNCTION_START(d_mul_2exp, state)
19+
{
20+
double x, res1, res2;
21+
int e, ok;
22+
slong iter;
23+
24+
for (iter = 0; iter < 10000 * flint_test_multiplier(); iter++)
25+
{
26+
x = d_randtest_special(state, -1024, 1024);
27+
e = n_randint(state, 3000) - 1500;
28+
29+
res1 = d_mul_2exp(x, e);
30+
res2 = ldexp(x, e);
31+
32+
if (d_is_nan(res2))
33+
ok = d_is_nan(res1);
34+
else
35+
ok = res1 == res2;
36+
37+
if (!ok)
38+
TEST_FUNCTION_FAIL("x = %.20g\n res1 = %.20g\n res2 = %.20g\n", x, res1, res2);
39+
}
40+
41+
for (iter = 0; iter < 10000 * flint_test_multiplier(); iter++)
42+
{
43+
do {
44+
x = d_randtest_signed(state, -256, 256);
45+
} while (x == 0.0);
46+
e = n_randint(state, 512) - 256;
47+
48+
res1 = d_mul_2exp_inrange2(x, e);
49+
res2 = ldexp(x, e);
50+
51+
ok = res1 == res2;
52+
53+
if (!ok)
54+
TEST_FUNCTION_FAIL("x = %.20g\n res1 = %.20g\n res2 = %.20g\n", x, res1, res2);
55+
}
56+
57+
for (iter = 0; iter < 10000 * flint_test_multiplier(); iter++)
58+
{
59+
x = d_randtest_special(state, -1024, 1024);
60+
e = n_randint(state, D_MAX_NORMAL_EXPONENT - D_MIN_NORMAL_EXPONENT + 1) + D_MIN_NORMAL_EXPONENT;
61+
62+
res1 = d_mul_2exp_inrange(x, e);
63+
res2 = ldexp(x, e);
64+
65+
if (d_is_nan(res2))
66+
ok = d_is_nan(res1);
67+
else
68+
ok = res1 == res2;
69+
70+
if (!ok)
71+
TEST_FUNCTION_FAIL("x = %.20g\n res1 = %.20g\n res2 = %.20g\n", x, res1, res2);
72+
}
73+
74+
for (iter = 0; iter < 1000 * flint_test_multiplier(); iter++)
75+
{
76+
double v[5] = { 0, 0, 0, 0, 0 };
77+
double r[10] = { 0, 0, 0, 0, 0 };
78+
slong i, n = n_randint(state, 5);
79+
80+
for (i = 0; i < n; i++)
81+
x = d_randtest_special(state, -1024, 1024);
82+
83+
e = n_randint(state, 3000) - 1500;
84+
85+
_d_vec_mul_2exp(r, v, n, e);
86+
87+
for (i = 0; i < n; i++)
88+
{
89+
res1 = r[i];
90+
res2 = ldexp(v[i], e);
91+
92+
if (d_is_nan(res2))
93+
ok = d_is_nan(res1);
94+
else
95+
ok = res1 == res2;
96+
97+
if (!ok)
98+
TEST_FUNCTION_FAIL("x = %.20g\n res1 = %.20g\n res2 = %.20g\n", x, res1, res2);
99+
}
100+
}
101+
102+
TEST_FUNCTION_END(state);
103+
}

0 commit comments

Comments
 (0)