A Collection of Useful C++ Classes for Signal Processing

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

The elliptic filter design function takes "roll off" as a parameter. I can not figure out exactly how this is related to stop band attenuation. Most other elliptic design tools take stop band attenuation as an input.
I would like to do it this way as well. Can anyone tell me how to convert from stop band attenuation to in Db, pass band ripple in Db, and filter cutoff frequencies to "roll off" rate

Post

olalp wrote:Hi all,

I just wanted to report a bug I found when using the following filter to do block processing of the data:

Code: Select all

filter = new Dsp::FilterDesign
              <Dsp::Butterworth::Design::HighPass <MAXORDER>, 1 >;
I was getting a discontinuity at the boundary between buffers that had nothing to do with the data I was passing to it. By looking at it in more detail, I realized the filter was processing the second sample to whatever was in the adjacent memory location at the end of the array (it was off by one sample).
The problem was caused by Cascade::process(int numSamples, Sample* dest, StateType& state) const.
Instead of the line:

Code: Select all

*dest++ = state.process (*dest, *this);
I modified it to

Code: Select all

*dest = state.process (*dest, *this);
      ++dest;
And that fixed it.
Does this apply to other filters as well? It seems odd that should have fixed a problem, the

Code: Select all

      ++dest;
should not have been needed since the original "*dest++" should only have incremented the pointer after modifying the contents. What did you use to compile this?

It's unfortunate that the author has been dark since 2013; not only here but the repository on github and his own website.

Post

I believe that C++ doesn't specify the order of evaluation of the left-hand and right-hand terms in an assignment.

In the original code you modified, one compiler might first evaluate the rhs using the current value of dest, then store the result in dest, then increment dest (presumably what the author intended) - but another compiler might store the current value of dest in a temporary, then increment dest, then evaluate the rhs using the (now incremented) value, then store the result using the original value of dest.

Your modification avoids the ambiguity.

Post

kryptonaut wrote:I believe that C++ doesn't specify the order of evaluation of the left-hand and right-hand terms in an assignment.
I believe a lot of code would break if the pointer increment of "*foo++" was applied before modifying the content. This is what I see in a disassembly window before the loop continuation of the function in question. dest is fetched, the result stuffed in it and the ptr incremented. So, again I ask what compiler was used to get the bad results.

Code: Select all

mov         rax,qword ptr [dest]  
movsd       mmword ptr [rax],xmm0  
mov         rax,qword ptr [dest]  
add         rax,8  
mov         qword ptr [dest],rax  
	

Post

http://en.cppreference.com/w/c/language ... precedence

If you read the actual spec you will see that the precedence is very strictly specified. There are no variations like this in critical areas like this, do you think the spec is designed by a group of fools?

*x++ = value;

This breaks down like this:

((*(x++)) = (value));

So, first (value) is computed.

Secondly, x++ is applied, as this is post-fix it first returns the existing value (x) then increments the value in memory. The result is that the dereferencing *() applies to the original value of (x), not (x + 1).

Next, the pointer is dereferenced.

Lastly, the assignment operator is applied, moving (value) into (*x).

So, this is equivalent to:

data = (value);
reference = (*x);
reference = data;
x = (x + 1);

In all compilers, all the time, everywhere. (Unless they're broken.)
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

@aciddose Yes, order and precedence are very strict in C/C++ in most cases.

However, this reminded me of an old problem I had moving some code between platforms. And I now see the problem. There are cases where some things can be compiler specific. In this case its the order in which arguments to functions or operators are evaluated. The code works fine compiled by the Microsoft compiler but it won't evaluate the same in all compilers. If someone compiled this with say GCC for ARM/Android, they would get the exact error the original poster described. This is because "dest" is used on both sides of the = and as a parameter to a function, and each compiler can evaluate left to right or right to left as it pleases.

Modifying a variable of a built-in type and using that variable in the same expression is undefined behavior.

So, the OP was correct. I was wrong and this is a bug. You just might get lucky as I did depending on the compiler used. The source should be fixed. The code is in at least 2 places.

Post

Ah right, you're correct about reusing the same value on both sides of the assignment or both sides of any operator.

Normally operators should work left-to-right, except for a few including assignment.

So the pointer should be dereferenced first in most compilers, the function called, then the pointer dereferenced on the left of the assignment and the value assigned.

I'm surprised though that C doesn't specify this.

*dest++ = state.process(*dest, *this);

In this case both dereferences of dest should provide the same value, but you're saying the one inside the function brackets returns the incremented value for some compilers?

So in that case the assignment operator is using left-to-right rather than right-to-left order.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

A good way to look at these issues is to replace all operators with function calls, for example:

assign(dereference(postfix_increment(dest)), state.process(dereference(dest), dereference(this)));

Of course I'm ignoring the comma operator here... I'm not sure if the comma operator is applied to parameters in a function call though, I do not believe it is?

So if everything is processing from left-to-right in this line the second dereference of dest will already be incremented.

I believe the same issue occurs in this example without division by the assignment operator:

x = x + sum_squares(x, x++, x--);

So there is absolutely no way to know what the result of this line will be.

With:

x = 1

Possible results:

x = 1 + (1 + 1 + 1) = 4
x = 1 + (1 + 0 + 1) = 3
x = 1 + (1 + 1 + 4) = 7
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

The linked article from a few posts above starts off with this:
There is no concept of left-to-right or right-to-left evaluation in C++, which is not to be confused with left-to-right and right-to-left associativity of operators: the expression f1() + f2() + f3() is parsed as (f1() + f2()) + f3() due to left-to-right associativity of operator+, but the function call to f3 may be evaluated first, last, or between f1() or f2() at run time.
So in other words never do that.

Never call functions with consequences (modify external data or object members) as parameters to a function if there is any possibility of interaction between calls.

Since as I said you can replace operators with functions this rule applies absolutely everywhere.

So the only place you can call a function knowing the order of execution is when it is completely isolated from all other function calls with side-effects.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

I believe that a compiler can also evaluate the arguments to a function in any order which is a slightly different issue but can also easily get one into trouble if any order dependency is assumed.

olalp's fix of just moving the increment to the next line is good. either dest++ or ++dest (with brackets around the 2 lines).

Post

ChocoBilly wrote:Modifying a variable of a built-in type and using that variable in the same expression is undefined behavior.

So, the OP was correct. I was wrong and this is a bug. You just might get lucky as I did depending on the compiler used. The source should be fixed. The code is in at least 2 places.
I think you're still referring to this?

Code: Select all

*dest++ = state.process (*dest, *this);
"Expression" is what's on the right side of the "=". This is the type of statement that your bolded test is warning against:

Code: Select all

x = *source + *source++;  // this is undefined
Apologies if I'm misreading you, but it seems like you are saying that the former ("*dest++ =...") behavior is not defined. But it is. Assignment doesn't occur until after the expression is resolved.
My audio DSP blog: earlevel.com

Post

It's not "*dest++" that's in question. You can Google the issue and see that what we're referring to is up to the compiler when "*dest" is evaluated. You cannot assume left to right. Using "*dest" on both sides and relying on the order is undefined.

Post

ChocoBilly wrote:It's not "*dest++" that's in question. You can Google the issue and see that what we're referring to is up to the compiler when "*dest" is evaluated. You cannot assume left to right. Using "*dest" on both sides and relying on the order is undefined.
I wish you could be more specific than "google the issue" (google what? clearly, I don't understand what the issue is if you have to tell me—sorry, but I don't want this to be my "Saturday project"). "Evaluation" is of the expression—sure you can't rely on on order of evaluation, but the expression is "state.process (*dest, *this)", so I don't see any ambiguity with evaluation order.

Again, I may be misunderstanding you, but you say 'when "dest" is evaluated'. But dest is a variable, and you said that 'It's not "*dest++: that's in question', and if not, I don't see any potential for a problem. I'm sure that you must mean at least "when "*dest" is evaluated', but without the "++" nothing changes and there is no danger of their being ambiguity.

Sorry, I just see the compiler being asked to "pass values that 'dest' and 'this' point to, to the function state.process; set the location pointed to by 'dest', to the result returned by the function, then increment 'dest'". What happens to dest on the left side of the "=" can't affect "dest" on the right dies. Please explain what I'm missing.
My audio DSP blog: earlevel.com

Post

Yes, the assignment operator does not divide the "expression", the entire expression is the expression including the assignment.

For example:

y++ = (x++ = (y++ = x++) * x++ * y++);

See my post about replacing operators with functions.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

aciddose wrote:Yes, the assignment operator does not divide the "expression", the entire expression is the expression including the assignment.

For example:

y++ = (x++ = (y++ = x++) * x++ * y++);

See my post about replacing operators with functions.
Sure, you've made a nonsense statement here, nothing like the one we're talking about. It's easy to say why the above is ambiguous for a compiler (both the right and left side are ambiguous). Now tell me exactly why the statement in question (*dest++ = ...) is ambiguous for the compiler. Don't point me to google, just explain how a compiler would fall down interpreting that. Really, the C language is not so fragile as to not understand how to evaluate this.

I believe that you are missing the difference between a statement and an expression, in interpreting the issue of expression evaluation. The expression on the right side of the equals sign is always evaluated before assigning to the left side. Always. In fact, nothing should be allowed by a compiler on the left side of the "=" that could be a problem (look up "lval" and "rval").
My audio DSP blog: earlevel.com

Post Reply

Return to “DSP and Plugin Development”