Convert dB to linear without floating point operations - c

How to convert dB to linear using integers only?
Approximate solutions are acceptable.
Clarification: dB to linear conversion is obtained with the formula
P = 10*log10(P_dB)
The inverse is obtained with the formula:
P_dB = 10^(P/10)
When programming in C, these require floating-point operations.
The range of P_dB is limited and goes from -50 to 50, the type is integer.
Is there an accepted method to obtain these conversions using integer operations only?

Plus becomes multiplication, minus division.
example:
10dB ~ 10
3db ~ 2
57dB = 10 + 10 + 10 + 10 + 10 + 10 - 3
10*10*10*10*10*10/2 = 500000;
You do not need any floats.

Related

How to implement 3rd order Polynomial Formula calculations in C on a 16bit MCU

it is my first time posting but I'll start by apologizing in advance if this question has been asked before.
I have been struggling on how to implement a 3rd order polynomial formula in C because of either extremely small values or larger than 32bit results (on a 16bit MCU).
I use diffrent values but as an example I would like to compute for "Y" in formula:
Y = ax^3 + bx^2 + cx + d = 0.00000012*(1024^3) + 0.000034*(1024^2) + 0.056*(1024) + 789.10
I need to use a base32 to get a meaningful value for "a" = 515
If I multiply 1024^3 (10bit ADC) then I get a very large amount of 1,073,741,824
I tried splitting them up into "terms A, B, C, and D" but I am not sure how to merge them together because of different resolution of each term and limitation of my 16bit MCU:
u16_TermA = fnBase32(0.00000012) * AdcMax * AdcMax * AdcMax;
u16_TermB = fnBase24(0.000034) * AdcMax * AdcMax;
u16_TermC = fnBase16(0.056) * AdcMax;
u16_TermD = fnBase04(789.10);
u16_Y = u16_TermA + u16_TermB + u16_TermC + u16_TermD;
/* AdcMax is a variable 0-1024; u16_Y needs to be 16bit */
I'd appreciate any help on the matter and on how best to implement this style of computations in C.
Cheers and thanks in advance!
One step toward improvement:
ax^3 + bx^2 + cx + d --> ((a*x + b)*x + c)*x + d
It is numerically more stable and tends to provide more accurate answers near the zeros of the function and less likely to overflow intermediate calculations.
2nd idea; consider scaling the co-efficents if they maintain their approximate relative values as given on the question.
N = 1024; // Some power of 2
aa = a*N*N*N
bb = b*N*N
cc = c*N
y = ((aa*x/N + bb)*x/N + cc)*x/N + d
where /N is done quickly with a shift.
With a judicious selection of N (maybe 2**14 for high precision avoid 32-bit overflow), then entire code might be satisfactorily done using only integer math.
As aa*x/N is just a*x*N*N, I think a scale of 2**16 works well.
Third idea:
In addition to scaling, often such cubic equations can be re-written as
// alpha is a power of 2
y = (x-root1)*(x-root2)*(x-root3)*scale/alpha
Rather than a,b,c, use the roots of the equation. This is very satisfactory if the genesis of the equation was some sort of curve fitting.
Unfortunately, OP's equation roots has a complex root pair.
x1 = -1885.50539
x2 = 801.08603 + i * 1686.95936
x3 = 801.08603 - i * 1686.95936
... in which case code could use
B = -(x1 + x2);
C = x1 * x2;
y = (x-x1)*(x*x + B*x + C)*scale/alpha

How to convert a number to Q format [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 4 years ago.
Improve this question
I am working with XMC1300 MCU series from Infineon. For my application I need to find out the square root of some data.
Following are the math lib api provided by Infineon
int16_t XMC_MATH_CORDIC_Q15_Sqrt(int16_t x)
int32_t XMC_MATH_CORDIC_Q31_Sqrt(int32_t x)
These two API accept and returns data format Q15 and Q31 i.e. It could only represent [-1,1] range.
Suppose I wanted to find out the square root of
144
200
1000
34567
50000
How can I change these numbers in range [-1, 1].
What needs to be done for the normalization of Input and Output.
Regards,
Tinchu
You can't represent 144 or 50000 directly in Q15 or Q31 format. As you mention, those formats are fixed point representations of numbers between -1 and 1.
So the problem you are left with is a basic math problem.
We can use the fact that
sqrt(A/B) = sqrt(A) / sqrt(B)
Let's do the example where your value A = 144:
Set B to the Q31 divisor B = 0x7FFFF = 32768
A/B = 0.00439`
Sqrt(A/B) = 0.0663
Sqrt(B) = Sqrt(32768) = 181.02
Sqrt(A) = Sqrt(A/B)*Sqrt(B) = 0.0663 * 181.02 = 12.
How to do this with Q15 numbers? All numbers are scaled by B.
So Sqrt(A/B) is simply XMC_MATH_CORDIC_Q15_Sqrt(A) //=> 2172
because 0.0663 = 2172/32768.
Sqrt(B) is 181. The final result in integer math is 181 * 2172 / 32768 = 12 *
For larger numbers you need to use the Q31 divisor:
0x7fffFFFF = 2147483647
*note: Integer math will round that answer down to 11. If you want better rounding, you'll need to examine bit 15 before you do the divide.
I usually just develop formulas to convert from machine to engineering units. In this case the input and output would be expected to be Q15. Below I use _eu to designate engineering units, _mu to designate machine units and sqrt() to designate the actual square root. The formula for square root:
output_eu = sqrt(input_eu)
Conversions for input and output:
output_mu = (2^15)*output_eu
input_mu = (2^15)*input_eu
To get the equivalent calculation in machine units, substitute:
output_mu/(2^15) = sqrt(input_mu/(2^15))
output_mu = sqrt((2^15)*input_mu)
It's best to right shift the input in order to optimize precision, so in the case of 144, the number could be left shifted by 7 bits: 144*(2^7) = 18432
So the input is basically Q7.
In other words:
output_mu = sqrt((2^15)*((2^7)*input_eu))
= (2^11)*sqrt(input_eu)
So basically the output here is Q11 and could be shifted right 11 bits to get to the result in engineering units.
So if you were to execute this in code, lets say value 144 is loaded into variable x and we want to put result in variable y:
y = x << 7;
y = XMC_MATH_CORDIC_Q15_Sqrt(y);
y >>= 11;
Walking through the math:
1) y = (144*(2^7)) = 18432
2) y = sqrt((2^15)*18432) = 24576
3) y = 24576/(2^11) = 12

Compute oscillator frequency without using float or uint64 types in C

I have a 32.768 kHz oscillator which produces a 1-Hz pulse. I'm measuring this pulse with a 40MHz clock. I am trying to measure the actual frequency of the oscillator by comparing the expected results with the obtained one.
I cannot change the period that I'm measuring, it has to be 1-Hz (1s).
I cannot use float, double or types larger than uint32.
I also need the first digit after the integer part (like 32768.1 Hz).
Without the constraints the solution would be like:
computedFreq = (uint32) ( (float32)32768u / ( ( (float32)measuredPeriod / (float32)40,000,000 ) / (float32)10u ) );
or
computedFreq = ( 32768*10*40,000,000 ) / measuredPeriod;
So for a measured period of 40,008,312 the result will be 327611.
How can this be achieved while satisfying the constraints?
Your problem is that measured range goes up to 40010000 Hz, while you are only interested in 20000 Hz range. So the solution is to limit the range.
You can do this with linear interpolation using 2 points.
Select 2 input values. Min/max points might do:
inMin = 40,000,000 - 10,000
inMax = 40,000,000 + 10,000
Precalculate respective output values using your formula (values here are rough values, calculate your own): You can store these as constants in your code.
outMin = ( 32768*10*40,000,000 ) / inMin = 327761
outMax = ( 32768*10*40,000,000 ) / inMax = 327598
Notice how max is smaller than min, due the nature of your formula. This is important when selecting types below.
Use linear interpolation to calculate result using both previous points:
out = (in - inMin) * (outMax - outMin) / (inMax - inMin) + outMin
You have to use signed integers for this, because (outMax - outMin) is negative.
Upper limit of the multiplication is (inMax - inMin) * (outMax - outMin), which should fit into int32.
You can pick any 2 points, but you should pick ones that will not produce too big values to overflow on multiplication. Also, if points are too near each other, answer may be less accurate.
If you you have extra precision to spare, you could use bigger multiplier than 10 on outMin/outMax and round afterwards to keep more precision.
Note that 32768 * 10 * 40000000 = 3200000000 * 2^12.
You can use this fact in order to approximate the frequency as follows:
computedFreq = (3200000000/measuredPeriod) << 12;
UPDATE:
I understand that 39990000 <= measuredPeriod <= 40010000.
A better way to approximate the frequency under this restriction would be:
computedFreq = 327761-(measuredPeriod-39990000)/122;

C calculation not operating as expected

Hi there I am currently trying to create a simple game in c based around a map with different ai players.
I've came along to program the calculations to figure out an amount of health lost and although I am sure my math behind it is correct the implemtnation in c doesn't seem to be working! I might have missed a simple point but I'll put the extract of code which isn't working as expected.
random1 = Numbergen(50);
cal1 = (100 - random1)/100;
random2 = Numbergen(50);
cal2 = (100 - random2) / 100;
PtrPlayer[attacker].health = (double)(PtrPlayer[attacker].health * cal1);
PtrPlayer[ncount].health = (double)(PtrPlayer[ncount].health * cal2);
printf("There was a battle but both players drawed and retreated.\n");
return 1;
Numbergen is a function that calculates a random number based on a time seed, 50 is the maximum number I want it to return with.
cal1 and 2 should store a decimal number for example 0.75 to take off 25% health and that's what the cal calculations should be doing however when debugging they are showing a value of zero no matter what the random number is.
This should work by taking the random number lets say 25 way from 100 to leave 75, it then divides by 100 to get a decimal multiplier which can then be used to reduce the health by 25%. Health starts at 100 so for example it will result in 100*0.75 which should leave me with 75 health but instead cal one stores 0 and as a result the health goes down to zero.
To be clear cal1 and 2 are both floats to allow for decimal places.
If anyone can point out where I might have gone wrong I will be so grateful!
If I've missed out something important then please let me know and I'll try and explain.
Also please note I am only a beginner in programming so please don't hit me with super complex code!
As requested random1 and 2 are both ints
PtrPlayer[].health is set as ints
cal1 and 2 are set as floats
cal1 = (100 - random1)/100;
Since random1 is an int, and 100 is an int, (100 - random1) is also an int. When you use the / operator on two integer operands, a truncating integer division is performed.
If you want floating-point division, convert at least one side of / to a floating-point type :
cal1 = (100 - random1) / 100.0f;
// float literal ^^^^^^
... or...
cal1 = (float)(100 - random1) / 100;
Simply converting the result of the division would have no effect, as C expressions' types are determined strictly from the inside out.
Just try changing either of the two 100 as 100.0
100.0 - random1
100.0 - random2
Or
(100 - random1)/100.0
(100 - random2)/100.0
This is the easiest option.

Split Entire Hash Range Into n Equal Ranges

I am looking to take a hash range (md5 or sha1) and split it into n equal ranges.
For example, if m (num nodes) = 5, the entire hash range would be split by 5 so that there would be a uniform distribution of key ranges. I would like n=1 (node 1) to be from the beginning of the hash range to 1/5, 2 from 1/5 to 2/5, etc all the way to the end.
Basically, I need to have key ranges mapped to each n such that when I hash a value, it knows which n is going to take care of that range.
I am new to hashing and a little bit unsure of where I could start on solving this for a project. Any help you could give would be great.
If you are looking to place a hash value into a number of "buckets" evenly, then some simple math will do the trick. Watch out for rounding edge cases... You would be better to use a power of 2 for the BUCKETS value.
This is python code, by the way, which supports large integers...
BUCKETS = 5
BITS = 160
BUCKETSIZE = 2**BITS / BUCKETS
int('ad01c5b3de58a02a42367e33f5bdb182d5e7e164', 16) / BUCKETSIZE == 3
int('553ae7da92f5505a92bbb8c9d47be76ab9f65bc2', 16) / BUCKETSIZE == 1
int('001c7c8c5ff152f1cc8ed30421e02a898cfcfb23', 16) / BUCKETSIZE == 0
If you can stand a little very hard to remove bias (any power of two is impossible to divide evenly in 5, so there has to be some bias), then modulo (% in C and many other languages with C-like syntax) is the way to divide the full range into 5 almost identically-sized partitions.
Any message m with md5(m)%5==0 is in the first partition, etc.

Resources