Inverting function that includes modulus

  • Context: Graduate 
  • Thread starter Thread starter cepheid
  • Start date Start date
  • Tags Tags
    Function Modulus
Click For Summary

Discussion Overview

The discussion revolves around the mathematical inversion of a function involving a modulus operator, specifically for a function that maps an angle \(\phi\) to a count \(x\) based on an incremental rotary encoder. Participants explore how to express the wrapping behavior of the function when \(x\) is negative and how to derive \(\phi(x)\) from \(x\). The context includes considerations of angle restrictions and integer values.

Discussion Character

  • Exploratory
  • Technical explanation
  • Debate/contested
  • Mathematical reasoning

Main Points Raised

  • Some participants express the need to invert the function \(x(\phi) = \left[(\phi - \phi_0)\frac{72~000}{360^\circ} + x_0\right]~\textrm{mod}~72~000\) to find \(\phi(x)\).
  • There is a discussion on the behavior of the modulus operator, particularly in programming contexts, with some noting that negative values require special handling.
  • One participant suggests a rearrangement to express \(\phi\) in terms of \(x\), involving both mod 72000 and mod 360 operations.
  • Another participant questions the necessity of the mod 72000 operation, suggesting it may be superfluous under certain conditions.
  • Participants discuss the implications of restricting \(\phi\) to a range of 0 to 180 degrees, noting that this complicates the inversion process.
  • There is mention of practical applications involving an incremental rotary encoder, with specific values for \(x\), \(x_0\), and \(\phi_0\) being defined.

Areas of Agreement / Disagreement

Participants express differing views on the necessity and behavior of the modulus operator in the context of programming and mathematical inversion. There is no consensus on the best approach to handle the modulus operation or the implications of restricting the angle \(\phi\).

Contextual Notes

Limitations include the dependence on programming language behavior regarding modulus operations and the unresolved nature of how to effectively restrict \(\phi\) while inverting the function.

Who May Find This Useful

Readers interested in mathematical functions involving modulus, programming implications of mathematical operations, and applications in rotary encoders may find this discussion relevant.

cepheid
Staff Emeritus
Science Advisor
Gold Member
Messages
5,197
Reaction score
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
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?
 
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?
 
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?
 
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.
 
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!
 
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:
 
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!
 
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? :-p

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? :-p

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

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? :-p
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)
 

Similar threads

  • · Replies 5 ·
Replies
5
Views
3K
  • · Replies 1 ·
Replies
1
Views
2K
  • · Replies 13 ·
Replies
13
Views
2K
  • · Replies 4 ·
Replies
4
Views
4K
  • · Replies 2 ·
Replies
2
Views
2K
  • · Replies 3 ·
Replies
3
Views
4K
  • · Replies 9 ·
Replies
9
Views
3K
  • · Replies 93 ·
4
Replies
93
Views
16K
Replies
7
Views
2K
  • · Replies 1 ·
Replies
1
Views
3K