Inverting function that includes modulus

In summary: It seems like you are using the remainder operator, not the modulus operator. The modulus operator is supposed to work like the mod operator in mathematics, not the remainder operator. In summary, the function provided is x(\phi) = \left[(\phi - \phi_0)\frac{72~000}{360^\circ} + x_0\right]~\textrm{mod}~72~000. To invert this function and get \phi(x), the following expression can be used: \phi =( \frac{(x-x_0)~\textrm{mod}~72000}{200} + \phi_0 ) ~\textrm{mod}~360. However
  • #1
cepheid
Staff Emeritus
Science Advisor
Gold Member
5,199
38
I have the function:

[tex] x(\phi) = \left[(\phi - \phi_0)\frac{72~000}{360^\circ} + x_0\right]~\textrm{mod}~72~000 [/tex]

and I'm wondering how to invert it in order to get [itex] \phi(x) [/itex]? Also, the modulus operator is not exactly what I'm looking for here, because if x goes below 0, I want it to wrap around so that [itex] -1 \longrightarrow 71~999 [/itex], and [itex] -2 \longrightarrow 71~998 [/itex] etc. How do I express that mathematically?

Finally, the angle [itex] \phi [/itex] is restricted between values that cover a range < 360 degrees, leading me to believe this function should be one-to-one. [itex] \phi_0 [/itex] also lies in this range. [itex] x_0 [/itex] is arbitrary but should be between 0 and 71999.

EDIT: I should also mention that x is in the set of integers (which I guess means that step sizes in [itex]\phi[/itex] of less than 18 arsec don't occur).
 
Last edited:
Mathematics news on Phys.org
  • #2
cepheid said:
I have the function:

[tex] x(\phi) = \left[(\phi - \phi_0)\frac{72~000}{360^\circ} + x_0\right]~\textrm{mod}~72~000 [/tex]

and I'm wondering how to invert it in order to get [itex] \phi(x) [/itex]? Also, the modulus operator is not exactly what I'm looking for here, because if x goes below 0, I want it to wrap around so that [itex] -1 \longrightarrow 71~999 [/itex], and [itex] -2 \longrightarrow 71~998 [/itex] etc. How do I express that mathematically?

Finally, the angle [itex] \phi [/itex] is restricted between values that cover a range < 360 degrees, leading me to believe this function should be one-to-one. [itex] \phi_0 [/itex] also lies in this range. [itex] x_0 [/itex] is arbitrary but should be between 0 and 71999.

EDIT: I should also mention that x is in the set of integers (which I guess means that step sizes in [itex]\phi[/itex] of less than 18 arsec don't occur).

Modulus is the operator you want .. it works as you describe for negative numbers.

As for the inverse, I think you could handle it like inverting a trig function. First you say that the range of phi is restricted to 0 to 360 (I would say 0 to 2*pi). Then you just rearrange:

[tex] \frac{(x - x_0)~\textrm{mod}~72000}{72000} = \frac{\phi - \phi_0}{360}[/tex]

Note the mod 72000 is required to deal with possible negative values of (x-x0).

So that suggests,

[tex] \phi =( \frac{(x-x_0)~\textrm{mod}~72000}{200} + \phi_0 ) ~\textrm{mod}~360[/tex]

Will that work?
 
  • #3
SpectraCat said:
Modulus is the operator you want .. it works as you describe for negative numbers.

Well, yes and no. If I type in -1 mod 60 into Google calc, then I get 59. Unfortunately, that's not how the modulus (%) operator behaves in C. I have to implement this in C. -1 % 60 yields -1, since % gives you the remainder. Right now I have a workaround, which is to say that:

Code:
x = ((phi - phi0)*(72000/360) + x0) % 72000;
x = (x < 0) ? x + 72000 : x;

The workaround in the 2nd line is only good if the x value doesn't "wrap around" in either direction by more than one multiple of 72000. Then again, given my phi range, that might be the case. This also complicates the problem of inverting the function.

SpectraCat said:
As for the inverse, I think you could handle it like inverting a trig function. First you say that the range of phi is restricted to 0 to 360 (I would say 0 to 2*pi). Then you just rearrange:

Right well, I'm using degrees because this is for a practical application in which angles will be measured in degrees. I plan to restrict phi to between 0 and 180 degrees.

SpectraCat said:
[tex] \frac{(x - x_0)~\textrm{mod}~72000}{72000} = \frac{\phi - \phi_0}{360}[/tex]

Note the mod 72000 is required to deal with possible negative values of (x-x0).

So that suggests,

[tex] \phi =( \frac{(x-x_0)~\textrm{mod}~72000}{200} + \phi_0 ) ~\textrm{mod}~360[/tex]

Will that work?

This works for the values for which I tested it, but I must confess that I do not understand how you arrived at it at all? Why the mod 72000, and then the mod 360? Can you elaborate?
 
  • #4
cepheid said:
Well, yes and no. If I type in -1 mod 60 into Google calc, then I get 59. Unfortunately, that's not how the modulus (%) operator behaves in C. I have to implement this in C. -1 % 60 yields -1, since % gives you the remainder. Right now I have a workaround, which is to say that:

Code:
x = ((phi - phi0)*(72000/360) + x0) % 72000;
x = (x < 0) ? x + 72000 : x;

The workaround in the 2nd line is only good if the x value doesn't "wrap around" in either direction by more than one multiple of 72000. Then again, given my phi range, that might be the case. This also complicates the problem of inverting the function.



Right well, I'm using degrees because this is for a practical application in which angles will be measured in degrees. I plan to restrict phi to between 0 and 180 degrees.



This works for the values for which I tested it, but I must confess that I do not understand how you arrived at it at all? Why the mod 72000, and then the mod 360? Can you elaborate?

Well, the mod 360 is because you are dealing with an angle ... I figured you wanted a value between 0 and 360. So, you need to account for the possibility that both [itex]\phi[/itex] and [itex]\phi_0[/itex] are between 180 and 360 degrees. In that case, the raw value will be greater than 360, so you need the mod.

Note that if you really want to restrict [itex]\phi[/itex] to between 0 and 180, then things get more complicated when you invert the function. I will let you take a shot a figuring out how to do that, but if you get stuck, come back and I'll try to help some more.

For the mod 72000, you know that 0<x<72000, but you didn't specific any limits on x0. Therefore I assumed you had to deal with cases where x<x0, which would result in a negative value for (x-x0). The mod 72000 should take care of that as well.

As for the proper mod operator, your code is ok, but why not just write a mod function for integers (my C is rusty, so the following might not be syntactically correct, but you get the drift I guess)?

Code:
int mod(int a, int b) { return (a%b) > -1 ? a%b : (a%b)+b;}

Won't that do the trick?
 
  • #5
Just to level with you, x is the reading (in counts) of an incremental rotary encoder with a resolution of 72,000 counts per rotation. If the count value goes past an endpoint, it wraps around. x0 is the initial encoder reading upon the startup of my system, which I don't control (but x and x0 necessarily have to be between 0 and 71999). phi0 is the initial direction in which I TELL my device it is pointing (at startup). I control this. phi and phi0 range between 0 and 359.

SpectraCat said:
Well, the mod 360 is because you are dealing with an angle ... I figured you wanted a value between 0 and 360. So, you need to account for the possibility that both [itex]\phi[/itex] and [itex]\phi_0[/itex] are between 180 and 360 degrees. In that case, the raw value will be greater than 360, so you need the mod.

I get this, thanks for your patience!

SpectraCat said:
Note that if you really want to restrict [itex]\phi[/itex] to between 0 and 180, then things get more complicated when you invert the function. I will let you take a shot a figuring out how to do that, but if you get stuck, come back and I'll try to help some more.

No, never mind. I decided not to restrict the range of motion of my device.

SpectraCat said:
For the mod 72000, you know that 0<x<72000, but you didn't specific any limits on x0. Therefore I assumed you had to deal with cases where x<x0, which would result in a negative value for (x-x0). The mod 72000 should take care of that as well.

Based on the test cases I've done, it seems like the mod 72,000 is superfluous. It works, but you get the right answer even if x < x0 provided that you have the mod 360 in there. This sort of makes sense to me.

SpectraCat said:
As for the proper mod operator, your code is ok, but why not just write a mod function for integers (my C is rusty, so the following might not be syntactically correct, but you get the drift I guess)?

Yeah, I have something analogous to the following now (I have only included relevant lines of code). Although the encoder value is an integer, I want to be able to work with angles down to a fairly good precision, so I am using doubles and hence needed to use fmod instead of %:

Code:
#include <math.h>

#define CNTS_PER_DEG 72000.0/360.0

double myMod(double val, double mod);

.
.	/* stuff here */
.

int main (void) 
{

  /* variable declarations and stuff here */

  /* read encoder value into variable called enc 
     enc_ref = initial value
     phi_ref = user-defined reference angle
   */

  phi = (enc - enc_ref)/CNTS_PER_DEG + phi_ref

  phi = myMod(phi, 360.0);

  printf("current angle: %f\n", phi);

  return 0;
}

double myMod(double val, double mod)
{
  return (fmod(val, mod) >= 0) ? fmod(val, mod) : fmod(vald, mod) + mod;
}

Right now this code doesn't work. I realize it's hard for you to troubleshoot without seeing the whole program (which is actually much more complex), but I was just wondering if there was anything arithmetically wrong with what's here. The manner in which it fails is as follows. Say for example at startup the encoder reads 3986. I tell the system it is pointing at phi = 40 deg. I ask it to move to 10 deg. It does so. That's -6000 counts, which means that the encoder value should wrap around to about 70,000. It does. Then I ask it to go to 20 deg, meaning that it should move 10 deg back in the direction from which it came. But it doesn't, because for some reason it computes its current azimuth angle to be 40 deg (not 10 deg). That's in spite of the fact that it reads in an encoder value of 70,048, and:

3986 - 70,048 = 66,062 (difference between destination and reference encoder value)

66,062 * (360/72,000) = 330.31 (convert this from counts to degrees)

330.31 + 40.00 = 370.31 (add the reference angle to get the destination angle)

370.31 mod 360 = 10.31

So, when I do the computation manually, I find that the program SHOULD have found that it was exactly where I told it to go i.e. at 10 degrees, NOT 40 degrees.
 
  • #6
Hi cepheid! :smile:

Try
Code:
#define CNTS_PER_DEG (72000.0/360.0)

instead of:
Code:
#define CNTS_PER_DEG 72000.0/360.0

Cheers!
 
  • #7
cepheid said:
Just to level with you, x is the reading (in counts) of an incremental rotary encoder with a resolution of 72,000 counts per rotation. If the count value goes past an endpoint, it wraps around. x0 is the initial encoder reading upon the startup of my system, which I don't control (but x and x0 necessarily have to be between 0 and 71999). phi0 is the initial direction in which I TELL my device it is pointing (at startup). I control this. phi and phi0 range between 0 and 359.



I get this, thanks for your patience!



No, never mind. I decided not to restrict the range of motion of my device.



Based on the test cases I've done, it seems like the mod 72,000 is superfluous. It works, but you get the right answer even if x < x0 provided that you have the mod 360 in there. This sort of makes sense to me.



Yeah, I have something analogous to the following now (I have only included relevant lines of code). Although the encoder value is an integer, I want to be able to work with angles down to a fairly good precision, so I am using doubles and hence needed to use fmod instead of %:

Code:
#include <math.h>

#define CNTS_PER_DEG 72000.0/360.0

double myMod(double val, double mod);

.
.	/* stuff here */
.

int main (void) 
{

  /* variable declarations and stuff here */

  /* read encoder value into variable called enc 
     enc_ref = initial value
     phi_ref = user-defined reference angle
   */

  phi = (enc - enc_ref)/CNTS_PER_DEG + phi_ref

  phi = myMod(phi, 360.0);

  printf("current angle: %f\n", phi);

  return 0;
}

double myMod(double val, double mod)
{
  return (fmod(val, mod) >= 0) ? fmod(val, mod) : fmod(vald, mod) + mod;
}

Right now this code doesn't work. I realize it's hard for you to troubleshoot without seeing the whole program (which is actually much more complex), but I was just wondering if there was anything arithmetically wrong with what's here. The manner in which it fails is as follows. Say for example at startup the encoder reads 3986. I tell the system it is pointing at phi = 40 deg. I ask it to move to 10 deg. It does so. That's -6000 counts, which means that the encoder value should wrap around to about 70,000. It does. Then I ask it to go to 20 deg, meaning that it should move 10 deg back in the direction from which it came. But it doesn't, because for some reason it computes its current azimuth angle to be 40 deg (not 10 deg). That's in spite of the fact that it reads in an encoder value of 70,048, and:

3986 - 70,048 = 66,062 (difference between destination and reference encoder value)

66,062 * (360/72,000) = 330.31 (convert this from counts to degrees)

330.31 + 40.00 = 370.31 (add the reference angle to get the destination angle)

370.31 mod 360 = 10.31

So, when I do the computation manually, I find that the program SHOULD have found that it was exactly where I told it to go i.e. at 10 degrees, NOT 40 degrees.

My guess is that you have forgotten to update phi_ref (and possibly x_ref) in between your two angle adjustments. Could that be it? I make that sort of silly mistake rather frequently, so I have learned to look for it. :redface:
 
  • #8
I like Serena said:
Hi cepheid! :smile:

Try
Code:
#define CNTS_PER_DEG (72000.0/360.0)

instead of:
Code:
#define CNTS_PER_DEG 72000.0/360.0

Cheers!

That could very well be it, because it is acting as though the first term on the right hand side of:

phi = (enc - enc_ref)/CNTS_PER_DEG + phi_ref

is evaluating to zero, and hence it thinks the current angle is always phi_ref. Perhaps that could be due to some order of operations problem. I will try that and report back!

Many thanks both of you!
 
  • #9
cepheid said:
Perhaps that could be due to some order of operations problem. I will try that and report back!
This is one of the common oversights people have regarding the C preprocessor; it's a text processor and acts by substituting text -- it doesn't do a "mathematical" substitution.

Since mathematical substitutions need parentheses, you have to include them in your macro. It's a good habit to put parentheses around all macros meant to be used in expressions.
 
  • #10
Hurkyl said:
This is one of the common oversights people have regarding the C preprocessor; it's a text processor and acts by substituting text -- it doesn't do a "mathematical" substitution.

Since mathematical substitutions need parentheses, you have to include them in your macro. It's a good habit to put parentheses around all macros meant to be used in expressions.

Why not just define CNTS_PER_DEG as 200.0? If you are worried that you will forget why that value was chosen, you could always add a comment stating where it comes from. It is not (usually) an issue with modern code and compilers, but by defining the MACRO as the ratio you nominally force the CPU to do multiple evaluations. A well-designed compiler will probably catch this and try to cache the value, so it doesn't need to be re-evaluated, but IMHO it's cleaner to just define the macro as a constant.
 
  • #11
SpectraCat said:
Why not just define CNTS_PER_DEG as 200.0? If you are worried that you will forget why that value was chosen, you could always add a comment stating where it comes from.
I can't think of any reasonable criterion that would suggest
Code:
// 72000.0 / 360.0
#define CNTS_PER_DEG 200.0
is better code than
Code:
#define CNTS_PER_DEG (72000.0 / 360.0)


Incidentally, one distinct advantage to the calculation is that it's relatively easy to change 72000.0 -- you can just change the number, rather than having to recompute the quotient.

Even better is if 72000.0 was another macro (or constant) -- that way you only have to change the code in one place. (And don't have to worry about whether or not a particular 72000.0 appearing in your code needs to be changed or if it's just a constant that coincidentally has that value)


I agree that
Code:
static const double CNTS_PER_DEG = 72000.0 / 360.0
should be preferred to the macro.
 
  • #12
Hurkyl said:
I can't think of any reasonable criterion that would suggest
Code:
// 72000.0 / 360.0
#define CNTS_PER_DEG 200.0
is better code than
Code:
#define CNTS_PER_DEG (72000.0 / 360.0)

Well, I am an old guy, and I remember when FLOPs were precious ... that was the reasoning I tried to express in my other post. Like I said, it probably makes little difference these days. Also, I was also thinking of a more descriptive comment that captures the logic, rather than just the calculation ... I tend to make frequent use of concise, descriptive comments so that I can understand what I was thinking when I designed and/or implemented an algorithm.


Incidentally, one distinct advantage to the calculation is that it's relatively easy to change 72000.0 -- you can just change the number, rather than having to recompute the quotient.

Even better is if 72000.0 was another macro (or constant) -- that way you only have to change the code in one place. (And don't have to worry about whether or not a particular 72000.0 appearing in your code needs to be changed or if it's just a constant that coincidentally has that value)


I agree that
Code:
static const double CNTS_PER_DEG = 72000.0 / 360.0
should be preferred to the macro.

I agree, but even better would be:

Code:
static const int MAX_CNTS = 72000; 
static const double CNTS_PER_DEG = MAX_CNTS / 360.0

since MAX_CNTS is a useful constant that will clearly be required elsewhere in the code. Thus the "one definition rule" indicates it should be a symbolic constant.
 
  • #13
Hmm, "static" is redundant for a variable that is "const".

And the C/C++ compiler evaluates constant expressions before generating code.


Cheers! :smile:
 
  • #14
I like Serena said:
Hmm, "static" is redundant for a variable that is "const".

I don't know about the modern C compiler .. I stopped using C before the use of const was commonplace. I am more of a C++ guy ... and in C++ defining something as "static const" at least can be different from just defining is as "const". Depending on the scope of the "static const" declaration (class, method or file scope), the behavior will be different. At class scope, there is a very significant difference between static const and const. A const variable or method can only be accessed through a specific instance of an object (i.e. via the *this pointer), whereas a static const variable is accessible through the namespace of the class. Thus static const declarations are a very important way for defining parameters that are relevant to the class, but are not tied to a specific object. (It's hard to state more clearly than that ... but the distinction is important once you start trying to write code).

At function scope or file scope, I think I agree that the static is redundant for const declarations, but I am not completely sure, because I don't write code using such constructs. The use of static at file scope in C++ is deprecated (at least I am pretty sure it is), you are supposed to define file scope level consts inside namespaces with external linkage these days.

And the C/C++ compiler evaluates constant expressions before generating code.

Yes .. of course you are right .. so all my concerns about conserving FLOPs were completely unfounded. Whoops! :redface:
 
Last edited:
  • #15
SpectraCat said:
Well, I am an old guy, and I remember when FLOPs were precious ... that was the reasoning I tried to express in my other post. Like I said, it probably makes little difference these days.
It's not that it makes little difference: it's that it makes no difference. As you already pointed out, compilers are somewhat more intelligent today than they were thirty years ago; any compiler worth using should emit identical machine code between the two versions.


I like Serena said:
Hmm, "static" is redundant for a variable that is "const".

And the C/C++ compiler evaluates constant expressions before generating code.
Actually, it's not redundant. A constant marked "static" has a different storage class than one marked without; normally such a constant is still stored in the resulting object code, and is made visible externally to the compilation unit. However, a static constant is truly local to the compilation unit (and may even be optimized away, so that it doesn't occupy space in memory at all).

A failure to mark your global constant variables static will lead to linker errors when the same variable name appears in different files. This is especially problematic when the variable appears in a header file (because a copy will be placed in every compilation unit that includes the header file).

A similar problem can happen when you try to define inline functions in your header files, and fail to do something appropriate with the storage class.
 
  • #16
SpectraCat said:
and in C++ defining something as "static const" at least can be different from just defining is as "const".

"static" is only needed when defining a const variable within a class, and that's only because otherwise you have a syntax error (I think).

Note: this is only for "const" variables, because a "const" variable normally does not have external linkage (unless you make it explicit with "extern").
SpectraCat said:
The use of static at file scope in C++ is deprecated (at least I am pretty sure it is), you are supposed to define file scope level consts inside namespaces with external linkage these days.

Yes, "static" to suppress external linkage is deprecated.
Nowadays it's only supposed to be used for "static duration", meaning it starts and ends its lifecycle just like the program does.

To suppress external linkage, we're supposed to use the so called "nameless namespace" in C++.

(Btw, I think this thread is running away from the OP and we are in General Math! :wink:)
 
Last edited:
  • #17
I like Serena said:
Sorry. Still redundant in all cases (I think). ;)

Note: only for "const" variables!

No, that's wrong .. I gave an example of why, and Hurkyl gave another example.


A "const" variable normally does not have external linkage.

Not sure what you mean .. my point was that the declaration "extern const" within a namespace has replaced the role that "static const" declarations at file scope used to play.


(Btw, I think this thread is running away from the OP and we are in General Math! :wink:)

Yes, that is true, but I rarely get the change to geek out about coding arcana any more, so this is feeding my inner coder nerd. I would cease and desist, but I have noticed that one of our debating partners is a moderator, so as long as he is chiming in too, I guess we are fine :wink:.
 
  • #18
SpectraCat said:
No, that's wrong .. I gave an example of why, and Hurkyl gave another example.
SpectraCat said:
Not sure what you mean .. my point was that the declaration "extern const" within a namespace has replaced the role that "static const" declarations at file scope used to play.

You won't get a linker error on a const variable.
There is no space allocated for it.
It is expanded similar to a macro (but not by the preprocessor).

Btw, I just edited my previous post, since I checked.
You do get a syntax error on a non-static const variable inside a class, but I think that's only a limitation of the syntax and not of the principle involved.
SpectraCat said:
I have noticed that one of our debating partners is a moderator, so as long as he is chiming in too, I guess we are fine :wink:.

True! So if the OP doesn't chime in too and if he feels it's necessary, he can move the discussion to a separate thread. ;)
 
Last edited:
  • #19
I like Serena said:
You won't get a linker error on a const variable.
Have you tried it? :tongue:

There is no space allocated for it.
Just to check before I spend more effort on this discussion, are you familiar with the following?
Code:
// main.c
#include <stdlib.h>
extern const int x;
int main() {
  printf("Value: %d\nAddress: %p\n", x, &x);
}
Code:
// other.c
const int x = 3;
 
  • #20
I like Serena said:
You do get a syntax error on a non-static const variable inside a class
The two options have very different semantics. Consider the two classes:

Code:
class immutable_integer
{
private:
    const int data;
public:
    immutable_integer(int arg):data(arg) {}
    operator int() const { return data; }
};

Code:
class wrapper_around_three
{
private:
    static const int data = 3;
public:
    operator int() const { return data; }
};

The meaning of the data field in the two cases is very different.
 
  • #21
Hurkyl said:
Have you tried it? :tongue:

Of course I have.
Just to check before I spend more effort on this discussion, have you? :tongue:

To make it explicit, try this:
Code:
a.cpp:
const int i = 10;

b.cpp:
const int i = 20;

$ g++ -ansi -pedantic -Wall -Wextra a.cpp b.cpp dummy_main.cpp

You'll find that there are no warnings or errors!

If you want I can bring out the heavy guns, and quote the standard.
But I have to search for it again, and it's not easy to read. ;)
Hurkyl said:
Just to check before I spend more effort on this discussion, are you familiar with the following?

extern const int x;

This is where you replace the redundant "static" with "extern", specifying that you want external linkage (I already made a note of that in an earlier post)! :wink:
This is useful when you have a "const" complex object of which you do not want its implementation to be globally visible and/or that you want to initialize only once.
Hurkyl said:
The two options have very different semantics. Consider the two classes:

Yes, here you can force the const variable to have external linkage by not initializing it within the class.
And if you do initialize it, the language forces you (in this case!) to put static in front of it. Cheers! :smile:
 
Last edited:
  • #22
I like Serena said:
Of course I have.
Just to check before I spend more effort on this discussion, have you? :tongue:
I have before -- at least I'm pretty sure this test. (I have to download a compiler if I want to test it today, otherwise I would have tested before posting)

Also, IIRC it actually makes a difference between compiling each unit separately and linking them versus throwing them all on a single gcc command line.

I can't remember if it matters (when testing with gcc) whether you actually use the variable for some purpose.


If you want I can bring out the heavy guns, and quote the standard.
But I have to search for it again, and it's not easy to read. ;)
I have a copy of the C and C++ standards at work but not at home. If you know of a link it wouldbe greatly appreciated. :smile:




This is where you replace the redundant "static" with "extern", specifying that you want external linkage! :wink:
You misunderstand immutable_integer; the storage class of data is neither static nor external. Instead, it is an instance variable -- each instance of the class gets its own version of the field data, each initialized to the value passed to the constructor.

const is a CV qualifier, not a storage class.
 
  • #23
Hurkyl said:
I have before -- at least I'm pretty sure this test. (I have to download a compiler if I want to test it today, otherwise I would have tested before posting)

Also, IIRC it actually makes a difference between compiling each unit separately and linking them versus throwing them all on a single gcc command line.

I can't remember if it matters (when testing with gcc) whether you actually use the variable for some purpose.

No, for g++ it does not matter if you put them on a single command line.

And no, it does not matter whether you actually use the variable for some purpose - the compiler does not "know" whether you'd make an external reference to it.

And I only made this example to prove my point - I use non-static const variables in header files at work.
Hurkyl said:
I have a copy of the C and C++ standards at work but not at home. If you know of a link it wouldbe greatly appreciated. :smile:

If you google it, you'll find a quick hit on a draft 1996 standard.
I tend to use it for quick reference, especially since concepts like these have not changed since the C90 standard.
Hurkyl said:
You misunderstand immutable_integer; the storage class of data is neither static nor external. Instead, it is an instance variable -- each instance of the class gets its own version of the field data, each initialized to the value passed to the constructor.

const is a CV qualifier, not a storage class.

You're right, sorry, I did not read carefully.
You did not define external linkage, but only the CV qualifier.
Storage duration in this case is the same as the class instance.
 
  • #24
I did your test -- I do get a linker error with gcc 3.4.4 as I predicted. (and you're right -- it fails whether or not I build the program all at once or whether I do it one compilation unit at a time and link)

I get a linker error with g++ 3.4.4 if and only if main.cc actually references the variable x. (using an extern const int x; prototype, of course)
 
  • #25
Hurkyl said:
I did your test -- I do get a linker error with gcc 3.4.4 as I predicted. (and you're right -- it fails whether or not I build the program all at once or whether I do it one compilation unit at a time and link)

I get a linker error with g++ 3.4.4 if and only if main.cc actually references the variable x. (using an extern const int x; prototype, of course)

All right, I repeated your test.
And yes, I get a linker error too: undefined reference to `i'.
Which I predicted as well!

I'm using g++ 4.4.3.

Do you mean to say you get a different linker error?
 
  • #26
Whoops, I hadn't noticed that g++ and gcc gave me different errors.
 
  • #27
Hurkyl said:
Whoops, I hadn't noticed that g++ and gcc gave me different errors.

Just tried it with gcc, which works since the code is also C.
But I get the same linker error: undefined reference, as expected.

What do you get? :confused:
 
  • #28
(Each line is a separate file)
Code:
$ echo *.c
f1.c f2.c main.c

$ cat *.c
const int x = 0;
const int x = 10;
int main() { }

$ gcc *.c
/tmp/ccuq04L9.o:f2.c:(.rdata+0x0): multiple definition of `_x'
/tmp/ccArQr4j.o:f1.c:(.rdata+0x0): first defined here
collect2: ld returned 1 exit status


(try renaming your *.cpp files to *.c before running the gcc test. gcc infers to compile as C++ when seeing the extension *.cc or *.cpp, along with some others. Or use gcc -x c *.cpp. The -x c specifies to treat the files as C files)
 
  • #29
Hurkyl said:
(try renaming your *.cpp files to *.c before running the gcc test. gcc infers to compile as C++ when seeing the extension *.cc or *.cpp, along with some others. Or use gcc -x c *.cpp. The -x c specifies to treat the files as C files)

*Smacks head*

Of course, I have the same linker error now.
I didn't know that this was different for C though...
 
  • #30
If it makes you feel better, I didn't know it was different for C++. :smile: I knew some things were different, but hadn't really learned what. (The few times I run into problems, it's usually quicker to guess-and-check the right way to do things than to actually look things up)
 
  • #31
cepheid said:
That could very well be it, because it is acting as though the first term on the right hand side of:

phi = (enc - enc_ref)/CNTS_PER_DEG + phi_ref

is evaluating to zero, and hence it thinks the current angle is always phi_ref. Perhaps that could be due to some order of operations problem. I will try that and report back!

I am reporting back to say that this fix worked. In hindsight it is obvious that what I entered before was resulting in first dividing by 72,000, and then dividing by 360. Not the intended effect at all.

Okkaaay, as for the rest of this discussion, although I am not interested in delving as deep into the vagaries of C as you guys are, I would like to know one thing: why are constants preferred to defines in this situation?

Finally, I have another question along similar lines. I need to do this again for my device's other axis of rotation (call this angle theta if you like, and call the encoder position y). Now, theta is physically restricted to lie between -10 deg and +89 deg (and I am required to use this coordinate system, since the zero point needs to be with reference to something external, namely the horizon). A range of 99 degrees corresponds to a range in encoder values of only 19,800 counts.

After some trial and error, I decided that there were three (mutually exclusive) possibilities, depending on the values of y0 and theta0 (neither of which are under my control, but depend on the system orientation and stored position at startup):

1. the encoder values would be monotonic throughout the range of motion

2. the encoder reference position y0 would be large enough and/or the reference angle theta0 would be far enough from the maximum angle that as you increased the angle, you'd eventually exceed 71,999, and wrap back around. This would cause the highest angles to evaluate to large and negative values, requiring addition of 360 degrees to them.

3. the encoder reference position y0 would be small enough and/or the reference angle theta0 would be far enough from the minimum angle that as you decreased the angle, you'd eventually go below 0, and wrap back around. This would cause the lowest angles to evaluate to large positive values, requiring subtraction by 360.

Based on these scenarios, I concluded that I could just use the same equation as before:

[tex] \theta(y) = \left[ (y-y_0)\left(\frac{360^\circ}{72,000}\right) + \theta_0 \right]~\textrm{mod}~360^\circ [/tex]

Using this formula:

- the positive angles (those between 0 and 89) would always turn out right

- the negative angles would have 360 added to them, and this would have to be "undone." Since the formula would produce large positive values (between 350 and 359 -- much larger than the maximum angle) for the negative angles, I added a line that said if (theta > 89.0) theta -= 360.0;

Does anyone see any errors?
 
Last edited:
  • #32
cepheid said:
I am reporting back to say that this fix worked. In hindsight it is obvious that what I entered before was resulting in first dividing by 72,000, and then dividing by 360. Not the intended effect at all.

Nice! :smile:

cepheid said:
Okkaaay, as for the rest of this discussion, although I am not interested in delving as deep into the vagaries of C as you guys are, I would like to know one thing: why are constants preferred to defines in this situation?

Yeah, sorry we hijacked your thread. I hope you don't mind! :wink:

As you have seen, macro's are tricky.
It's very easy for them to behave other than you expect.
What you found is just one of the many pitfalls.

In particular with constants you need to specify the type, which means additional checks are made by the compiler that everything is in order.
This is called "type safety".


cepheid said:
Finally, I have another question along similar lines.
...
Does anyone see any errors?

I don't quite understand what you are doing (your text is too long ;)).
If I understand correctly you want to map a range of encoder values to a range of angle values.
What are those ranges exactly?
And why do you think there might be a problem?
 
  • #33
cepheid said:
I am reporting back to say that this fix worked. In hindsight it is obvious that what I entered before was resulting in first dividing by 72,000, and then dividing by 360. Not the intended effect at all.

Those silly #define-induced logic errors always are obvious in hindsight ...

Okkaaay, as for the rest of this discussion, although I am not interested in delving as deep into the vagaries of C as you guys are, I would like to know one thing: why are constants preferred to defines in this situation?

As was already pointed out, type safety is one reason ... the other is what you already experienced ... the macro-preprocessor has one job .. to go through and do text-based find-and-replace of your macros throughout your code. Therefore, it is much less context sensitive than the compiler, and is prone to face-palm-worthy goof ups like the one you experienced.

Finally, I have another question along similar lines. I need to do this again for my device's other axis of rotation (call this angle theta if you like, and call the encoder position y). Now, theta is physically restricted to lie between -10 deg and +89 deg (and I am required to use this coordinate system, since the zero point needs to be with reference to something external, namely the horizon). A range of 99 degrees corresponds to a range in encoder values of only 19,800 counts.

... depending on the values of y0 and theta0 (neither of which are under my control, but depend on the system orientation and stored position at startup)

Before answering, I need to know whether or not y0 and theta0 are truly independent, or if they are correlated ... actually this question holds for x0 and phi0 as well.In other words, when you start up, if you run the value of x0 through your formula, do you get phi0, and vice-versa? Or are the encoder and the "true" angle values unrelated at startup?

The reason I ask is that the formula I came up with before assumes that they are uncorrelated. It should be possible to use a much simpler formula if the values are correlated.

A similar question has to do with the encoder ... is it a physical entity, or is it just a theoretical construct to help you control your device (telescope?) with a computer?
 
  • #34
SpectraCat said:
Before answering, I need to know whether or not y0 and theta0 are truly independent, or if they are correlated ... actually this question holds for x0 and phi0 as well.In other words, when you start up, if you run the value of x0 through your formula, do you get phi0, and vice-versa? Or are the encoder and the "true" angle values unrelated at startup?

Phi0 and x0 are independent. Same for y0 and theta0. At the startup of my program, I have to enter the reference angles. I can choose whatever phi0 I want, because I can choose whatever coordinate system I want for that direction (azimuth). In contrast, I have to measure the starting theta0 relative to a fixed coordinate system (for elevation) and enter it in. The encoder positions x0 and y0 at startup are outside of my control, because the last known positions are "remembered" by the readout electronics from previous runs of the program (unless if I power cycle everything). So I don't get to choose which values of x0 and y0 map to my chosen/measured phi0 and theta0.

SpectraCat said:
The reason I ask is that the formula I came up with before assumes that they are uncorrelated. It should be possible to use a much simpler formula if the values are correlated.

I'm pretty happy with the formula I stated in post #31. It seems to hold up under testing so far. In fact, I'm convinced that if the reasoning that I outlined in those three enumerated points in that post is correct, then the formula (with the correction for negative angles) is also correct. So it was mostly just that reasoning on which I wanted feedback.

SpectraCat said:
A similar question has to do with the encoder ... is it a physical entity, or is it just a theoretical construct to help you control your device (telescope?) with a computer?

The encoders are real devices. As I stated before, they are incremental encoders with a resolution of 72,000 counts per revolution. And yes, my device is a scanning mount for a cryostat that houses a microwave telescope.
 

1. What is the definition of an inverting function that includes modulus?

An inverting function that includes modulus is a mathematical function that reverses the output of another function while taking into account the absolute value of the input. This means that the output of the inverting function will be the inverse of the original function, but with the absolute value of the input taken into consideration.

2. How do you find the inverse of a function that includes modulus?

To find the inverse of a function that includes modulus, you must first isolate the modulus expression on one side of the equation. Then, you can remove the modulus by setting up two equations, one with the positive expression and one with the negative expression. From there, you can solve for the variable and find the inverse of the original function.

3. What is the importance of inverting a function that includes modulus?

Inverting a function that includes modulus allows for the original function to be easily reversed and used in different contexts. It also helps in solving equations and finding the roots of a function by providing a more accurate representation of the original function.

4. Can all functions with modulus be inverted?

No, not all functions with modulus can be inverted. Functions that include modulus can only be inverted if they are one-to-one functions, meaning that each input has a unique output. If a function is not one-to-one, then it cannot be inverted.

5. How can inverting a function that includes modulus be applied in real life?

Inverting a function that includes modulus can be applied in various fields such as engineering, economics, and physics. For example, in engineering, inverting a function that includes modulus can help in finding the maximum or minimum values of a system. In economics, it can be used to analyze demand and supply curves. In physics, it can help in calculating the trajectory of a projectile.

Similar threads

Replies
5
Views
2K
Replies
1
Views
815
  • Linear and Abstract Algebra
Replies
1
Views
904
Replies
4
Views
4K
  • Special and General Relativity
Replies
2
Views
929
  • Math Proof Training and Practice
3
Replies
93
Views
10K
  • Precalculus Mathematics Homework Help
Replies
7
Views
748
  • Topology and Analysis
Replies
9
Views
2K
  • Math Proof Training and Practice
6
Replies
175
Views
20K
  • General Math
Replies
4
Views
4K
Back
Top