xref: /openbmc/linux/lib/math/int_log.c (revision 9ab04d7e)
1*9ab04d7eSAndy Shevchenko // SPDX-License-Identifier: LGPL-2.1-or-later
2f97fa3dcSAndy Shevchenko /*
3f97fa3dcSAndy Shevchenko  * Provides fixed-point logarithm operations.
4f97fa3dcSAndy Shevchenko  *
5f97fa3dcSAndy Shevchenko  * Copyright (C) 2006 Christoph Pfister (christophpfister@gmail.com)
6f97fa3dcSAndy Shevchenko  */
7f97fa3dcSAndy Shevchenko 
8f97fa3dcSAndy Shevchenko #include <linux/bitops.h>
9f97fa3dcSAndy Shevchenko #include <linux/export.h>
10f97fa3dcSAndy Shevchenko #include <linux/int_log.h>
11f97fa3dcSAndy Shevchenko #include <linux/kernel.h>
12f97fa3dcSAndy Shevchenko #include <linux/types.h>
13f97fa3dcSAndy Shevchenko 
14f97fa3dcSAndy Shevchenko #include <asm/bug.h>
15f97fa3dcSAndy Shevchenko 
16f97fa3dcSAndy Shevchenko static const unsigned short logtable[256] = {
17f97fa3dcSAndy Shevchenko 	0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
18f97fa3dcSAndy Shevchenko 	0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
19f97fa3dcSAndy Shevchenko 	0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
20f97fa3dcSAndy Shevchenko 	0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
21f97fa3dcSAndy Shevchenko 	0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
22f97fa3dcSAndy Shevchenko 	0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
23f97fa3dcSAndy Shevchenko 	0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
24f97fa3dcSAndy Shevchenko 	0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
25f97fa3dcSAndy Shevchenko 	0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
26f97fa3dcSAndy Shevchenko 	0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
27f97fa3dcSAndy Shevchenko 	0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
28f97fa3dcSAndy Shevchenko 	0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
29f97fa3dcSAndy Shevchenko 	0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
30f97fa3dcSAndy Shevchenko 	0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
31f97fa3dcSAndy Shevchenko 	0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
32f97fa3dcSAndy Shevchenko 	0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
33f97fa3dcSAndy Shevchenko 	0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
34f97fa3dcSAndy Shevchenko 	0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
35f97fa3dcSAndy Shevchenko 	0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
36f97fa3dcSAndy Shevchenko 	0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
37f97fa3dcSAndy Shevchenko 	0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
38f97fa3dcSAndy Shevchenko 	0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
39f97fa3dcSAndy Shevchenko 	0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
40f97fa3dcSAndy Shevchenko 	0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
41f97fa3dcSAndy Shevchenko 	0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
42f97fa3dcSAndy Shevchenko 	0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
43f97fa3dcSAndy Shevchenko 	0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
44f97fa3dcSAndy Shevchenko 	0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
45f97fa3dcSAndy Shevchenko 	0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
46f97fa3dcSAndy Shevchenko 	0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
47f97fa3dcSAndy Shevchenko 	0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
48f97fa3dcSAndy Shevchenko 	0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47,
49f97fa3dcSAndy Shevchenko };
50f97fa3dcSAndy Shevchenko 
intlog2(u32 value)51f97fa3dcSAndy Shevchenko unsigned int intlog2(u32 value)
52f97fa3dcSAndy Shevchenko {
53f97fa3dcSAndy Shevchenko 	/**
54f97fa3dcSAndy Shevchenko 	 *	returns: log2(value) * 2^24
55f97fa3dcSAndy Shevchenko 	 *	wrong result if value = 0 (log2(0) is undefined)
56f97fa3dcSAndy Shevchenko 	 */
57f97fa3dcSAndy Shevchenko 	unsigned int msb;
58f97fa3dcSAndy Shevchenko 	unsigned int logentry;
59f97fa3dcSAndy Shevchenko 	unsigned int significand;
60f97fa3dcSAndy Shevchenko 	unsigned int interpolation;
61f97fa3dcSAndy Shevchenko 
62f97fa3dcSAndy Shevchenko 	if (unlikely(value == 0)) {
63f97fa3dcSAndy Shevchenko 		WARN_ON(1);
64f97fa3dcSAndy Shevchenko 		return 0;
65f97fa3dcSAndy Shevchenko 	}
66f97fa3dcSAndy Shevchenko 
67f97fa3dcSAndy Shevchenko 	/* first detect the msb (count begins at 0) */
68f97fa3dcSAndy Shevchenko 	msb = fls(value) - 1;
69f97fa3dcSAndy Shevchenko 
70f97fa3dcSAndy Shevchenko 	/**
71f97fa3dcSAndy Shevchenko 	 *	now we use a logtable after the following method:
72f97fa3dcSAndy Shevchenko 	 *
73f97fa3dcSAndy Shevchenko 	 *	log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
74f97fa3dcSAndy Shevchenko 	 *	where x = msb and therefore 1 <= y < 2
75f97fa3dcSAndy Shevchenko 	 *	first y is determined by shifting the value left
76f97fa3dcSAndy Shevchenko 	 *	so that msb is bit 31
77f97fa3dcSAndy Shevchenko 	 *		0x00231f56 -> 0x8C7D5800
78f97fa3dcSAndy Shevchenko 	 *	the result is y * 2^31 -> "significand"
79f97fa3dcSAndy Shevchenko 	 *	then the highest 9 bits are used for a table lookup
80f97fa3dcSAndy Shevchenko 	 *	the highest bit is discarded because it's always set
81f97fa3dcSAndy Shevchenko 	 *	the highest nine bits in our example are 100011000
82f97fa3dcSAndy Shevchenko 	 *	so we would use the entry 0x18
83f97fa3dcSAndy Shevchenko 	 */
84f97fa3dcSAndy Shevchenko 	significand = value << (31 - msb);
8508f6a14bSAndy Shevchenko 	logentry = (significand >> 23) % ARRAY_SIZE(logtable);
86f97fa3dcSAndy Shevchenko 
87f97fa3dcSAndy Shevchenko 	/**
88f97fa3dcSAndy Shevchenko 	 *	last step we do is interpolation because of the
89f97fa3dcSAndy Shevchenko 	 *	limitations of the log table the error is that part of
90f97fa3dcSAndy Shevchenko 	 *	the significand which isn't used for lookup then we
91f97fa3dcSAndy Shevchenko 	 *	compute the ratio between the error and the next table entry
92f97fa3dcSAndy Shevchenko 	 *	and interpolate it between the log table entry used and the
93f97fa3dcSAndy Shevchenko 	 *	next one the biggest error possible is 0x7fffff
94f97fa3dcSAndy Shevchenko 	 *	(in our example it's 0x7D5800)
95f97fa3dcSAndy Shevchenko 	 *	needed value for next table entry is 0x800000
96f97fa3dcSAndy Shevchenko 	 *	so the interpolation is
97f97fa3dcSAndy Shevchenko 	 *	(error / 0x800000) * (logtable_next - logtable_current)
98f97fa3dcSAndy Shevchenko 	 *	in the implementation the division is moved to the end for
99f97fa3dcSAndy Shevchenko 	 *	better accuracy there is also an overflow correction if
100f97fa3dcSAndy Shevchenko 	 *	logtable_next is 256
101f97fa3dcSAndy Shevchenko 	 */
102f97fa3dcSAndy Shevchenko 	interpolation = ((significand & 0x7fffff) *
10308f6a14bSAndy Shevchenko 			((logtable[(logentry + 1) % ARRAY_SIZE(logtable)] -
104f97fa3dcSAndy Shevchenko 			  logtable[logentry]) & 0xffff)) >> 15;
105f97fa3dcSAndy Shevchenko 
106f97fa3dcSAndy Shevchenko 	/* now we return the result */
107f97fa3dcSAndy Shevchenko 	return ((msb << 24) + (logtable[logentry] << 8) + interpolation);
108f97fa3dcSAndy Shevchenko }
109f97fa3dcSAndy Shevchenko EXPORT_SYMBOL(intlog2);
110f97fa3dcSAndy Shevchenko 
intlog10(u32 value)111f97fa3dcSAndy Shevchenko unsigned int intlog10(u32 value)
112f97fa3dcSAndy Shevchenko {
113f97fa3dcSAndy Shevchenko 	/**
114f97fa3dcSAndy Shevchenko 	 *	returns: log10(value) * 2^24
115f97fa3dcSAndy Shevchenko 	 *	wrong result if value = 0 (log10(0) is undefined)
116f97fa3dcSAndy Shevchenko 	 */
117f97fa3dcSAndy Shevchenko 	u64 log;
118f97fa3dcSAndy Shevchenko 
119f97fa3dcSAndy Shevchenko 	if (unlikely(value == 0)) {
120f97fa3dcSAndy Shevchenko 		WARN_ON(1);
121f97fa3dcSAndy Shevchenko 		return 0;
122f97fa3dcSAndy Shevchenko 	}
123f97fa3dcSAndy Shevchenko 
124f97fa3dcSAndy Shevchenko 	log = intlog2(value);
125f97fa3dcSAndy Shevchenko 
126f97fa3dcSAndy Shevchenko 	/**
127f97fa3dcSAndy Shevchenko 	 *	we use the following method:
128f97fa3dcSAndy Shevchenko 	 *	log10(x) = log2(x) * log10(2)
129f97fa3dcSAndy Shevchenko 	 */
130f97fa3dcSAndy Shevchenko 
131f97fa3dcSAndy Shevchenko 	return (log * 646456993) >> 31;
132f97fa3dcSAndy Shevchenko }
133f97fa3dcSAndy Shevchenko EXPORT_SYMBOL(intlog10);
134