Help with division and multiplication of float values [solved]


#1

Hello everyone.
I'm currently trying to make a knee object (kinda like the one i have recently put in the library, you can find it as knee k and knee s)
This time i'd like to make the knee move horizontally (see the picture)

As you can see, i already calculated the math formulas for the two ramps (nothing hard really).
For K=l/2 the two ramps are basically just one continuous ramp (x=y)
The hard part comes now: have to scale all values to axoloti's weird ranges.

For the other object i got away without doing divisions and using only ___SMMUL and bitshifts. This time however i have to divide for quantities that are not fixed and that route is no longer viable.

I tried to see how the math/reciprocal works, and it does its thing with

      float inf = inlet_in;
      outlet_out = (int)(281474976710656.f/inf);

I copied that into my object and got this code (which partially works, but only of the first part of the ramp)

/*
     * inlet_in=x
     * outlet_out=y
     * 
     * param_amp= knob value ( ParameterFrac32UMapGain type)
     * inlet_mod= additional mod input value ( inletFrac32Bipolar type )
     * the param_amp and inlet_mod are then added to provide the K parameter
    */
    
    param = param_amp + (inlet_mod<<4); // this is K
    float fparam = (param>>4);
    float temp;
    float input = (inlet_in);
    
    if (inlet_in< (param>>4)) //if x<K -> output the first part of the ramp (this one works as intended)
    {
        temp=(281474976710656.f/(fparam)); 
        outlet_out= ___SMMUL((int)(temp/2),inlet_in)<<11;
    }
    else //if x>K -> output the second part of the ramp (and here comes the trouble, it does not work as expected at all)
    
    {
        temp=(281474976710656.f/(64-fparam)); 
        outlet_out=___SMMUL((int)(temp/2),(inlet_in+(1<<27)-(param>>3)))<<11;
    }

Can someone help?
I'm getting several downward ramps in the output, that variate in number when you rotate the knob. Totally not what i was expecting, and at this point my programming skills break down
I'll post the object and an example patch

knee h k.axo (1.5 KB)
bend prova.axp (1.3 KB)


How to convert an int to a float?
#2

Okay, i no longer need help, i figured it out myself: iside the last parenthesis i have put this thing here:

{    
    temp=(281474976710656.f/((1<<27)-fparam)); 
    outlet_out=(___SMMUL((int)(temp/2),inlet_in-(param>>4))<<11) +(1<<26); 
}

Basically the operation 281474976710656.f / "stuff" performs a recyprocal. however what you put inside "stuff" is an integer, not a float (this may seem odd because i declared fparam as a float value, but that will remain a mistery for me since i'm no software engineer).

Now it works. Apparently i did not even divide by zero.


#3

interesting...

I thought Id have a play with this too, its interesting to understand Q Numbers...
... so this is not intended as a solution, as you already have one.

but thought if someone searches in the future, might be an interesting side note.

281474976710656 = 2^48
Im not going to use, it, but I was interested to know what this number was :smile:

ok, on to division in Q number format..
division is explained here:

so, we are using 32 bit numbers, and Q is 27
(so we need to use int32_t and int64_4)

so a quick conversion, gives us... (I've kept it the same as much as possible, to make it clear)

int32_t a = inlet_a;
int32_t b = inlet_b;

int32_t result;
int64_t temp;

// pre-multiply by the base (Upscale to Q16 so that the result will be in Q8 format)
temp = (int64_t) a << 27;
// Rounding: mid values are rounded up (down for negative values).
if ((temp >= 0 && b >= 0) || (temp < 0 && b < 0))
    temp += b / 2;
else
    temp -= b / 2;
result = (int32_t)(temp / b);

outlet_c = result;

and it works....

so lets see it in use... as perhaps the results are not 'obvious', and perhaps not what you want @Sputnki (hence why i say its a side note)

a = 64 b = 64 , result = 64

this may seem wrong initially, but remember 64 = 1.0 , so actually 1/1 = 1
from here its clear...
a = 16 b = 32 , result = 32 , which is 0.25 / 0.5 = 0.5

now this is why we have to be careful with division, as numbers get big quite quickly
a = 16, b = 8, result = 128 (so red on a dial, but its not really overflowed) .25/.125 = 2

so what if instead we use the math/recip object?
so implement.
res = a * ( 1/b)

well we get perhaps what might have been expected...
a = 16, b = 8, result = 2

this actually surprised me in someways... as its stepping out of our data typing... where we view 0-64 as 0 to 1,
instead its actually giving us straight division... I would suspect this because its generally more useful/whats expected.

of course, the first approach, can easily be changed to implement the second

simply change the last line to

  outlet_c = result >> 6;

(i.e. divide by 64)

anyway, as I say... I personally, just wanted to play with Q Numbers, the more I play and experiment, the more I understand... and thought Id pass the it on, as it may help others.

(apologises if you read this a found obvious, boring, or generally ... so what :smile: )

test patch (Axoloti 1.0.9) , showing 3 different implementations

divtest109.axp (6.4 KB)


#4

Great explanation, thanks! I had no idea of the existence of this format. I had already figured out that operations were made in fixed point but with some weird scaling (this and the weird scaling of input and parameter values), however my approach was more of a trial and error and long process adding bitshifts here and there until i got the desired result.

If only there was a manual!


#5

A post was split to a new topic: SMMUL and other intrinsics, debugging strategies