snigelen - nice detective work, and thanks for the link to netlib.org, that is new to me.
I'd like to say to Kwok C. Ng, thank you and very well done.
Edit: netlib.org looks like an excellent resource, thanks^2
snigelen - nice detective work, and thanks for the link to netlib.org, that is new to me.
I'd like to say to Kwok C. Ng, thank you and very well done.
Edit: netlib.org looks like an excellent resource, thanks^2
I learned a lot. Now, only if I can convince more people to do fixed point...
snigelen -- thanks for looking that up.
by the way, while it seems like your code worked, you should know that the default optimization flags we use mean that your code (reproduced below) might cause the writes to "t" to get optimized out:
void loop() {
double t;
unsigned char *b;
// Let b point to the bytes in t
b = (unsigned char*)&t;
if(SerialUSB.available()) {
SerialUSB.read();
for (uint16 i = 0; i < 256; i++) {
t = sin(2*M_PI/255.0 * (double)i);
// print t in hexadecimal form
for (int8 j = 7; j >= 0; j--) {
if(b[j] < 16)
SerialUSB.print('0');
SerialUSB.print(b[j],HEX);
}
SerialUSB.println();
}
}
}
in particular, we use -Os to suggest to GCC to optimize for size. this enables -fstrict-aliasing:
http://gcc.gnu.org/onlinedocs/gcc-4.1.1/gcc/Optimize-Options.html#index-fstrict_002daliasing-542
-fstrict-aliasing tells the compiler that the same address won't hold objects of two different types. so since t is a double and b is a pointer to unsigned char, it's possible that GCC might decide that since the variable t is only written to, and never read from, it could emit smaller code by removing the line where t = sin(...).
the standard "type punning" trick to get at the bits of a non-integral value is to use a union. for example, since "unsigned long" and "double" both take 8 bytes on the Maple, you could say
union ul_d_t {
unsigned long bits;
double d;
};
ul_d_t u;
u.d = sin(...);
SerialUSB.println(u.bits, HEX);
as a special case, GCC permits this "type punning" trick, even when -fstrict-aliasing is enabled.
edit:formatting
Thanks for the explanation mbolivar.
I know the standard way is to use a union, I don't know why I did as I did. It worked anyway.
But I discovered some other things. For example
union {
double t;
uint64 b;
} u;
u.t = ...;
SerialUSB.println(u.b, HEX);
prints only the lower 32 bits. But
union {
double t;
uint32 b[2];
} u;
u.t = ...;
SerialUSB.print(u.b[1], HEX);
SerialUSB.print(u.b[0], HEX);
works as expected.
Another thing: "SerialUSB.print((double) 12);"
prints 12.00 as expected, but I thought that "SerialUSB.print((double) 12, 5);" would print 12.00000, but instead it prints 22 (12 in base 5).
will look into the uint64 situation; thanks for the report.
as to SerialUSB.print((double)12, 5), there's no Print method with arguments double and int; there's only USBSerial::print(long n, int base); inherited from Print (see Print.cpp):
https://github.com/leaflabs/libmaple/blob/master/wirish/Print.cpp#L72
so that double is getting converted to a long, then printed in base 5.
by the way, perry says printf is available through newlib. don't know what stdout points to by default, will update this thread later on that.
A few C programming tips on the code:
The volatile keyword is your friend. Basically, if any value can be changed in another thread/process/task, via an interrupt, is a memory mapped peripheral, etc., use volatile.
In the "for" loops, the loop variable should be native data type (int or unsigned int), then cast it to the appropriate type, checking for overflows if appropiate. I don't know much about the compiler, so it may not make much of a difference.
Definitely true. I hit this bug all the time. Best to use volatile for any registers, or really anything thats not plain old RAM/local variables - unless youre using DMA, in which case your buffer should be declared volatile as well.
Well the doubts Silntknight had at the beginning of this thread are probably sorted out by now, so this is kind of getting of topic. Anyway.
I understand what you are talking about, but I can't see how it's (more than remotely) related to this thread.
Isn't volatile supposed to be used to tell the compiler that a variable can be changed at any time (by hardware or an interrupt routine for example)? It isn't anything like that in this thread.
In this case there was a variable (t) that was read through a pointer (b) and the optimizer could have missed that and thrown out the calculation of t. But in this case it didn't. But if it had, one could probably prevent that by putting a volatile in front of t. But that's not what volatile is for. You're not supposed to throw in a volatile here and there just in case. So the "hidden pointer" approach is not the way to go.
So the correct way to do it (in this case), is to tell the compiler that I want to use this variable in two ways, one for reading and one for writing, and that's what a union is for and no volatile is needed.
(as I understand it, or IMHO, or something like that)
Good call Snigelen, but go ahead and use this thread if you'd all like. For future readers looking for this kind of discussion (can't think of anyone actually) you might want to move it so that the title makes it a little more obvious.
sniglen - you beat me to it.
You're not supposed to throw in a volatile here and there just in case.
I agree.
You probably don't need any support, but ... :-)
The volatile keyword is your friend. Basically, if any value can be changed in another thread/process/task, via an interrupt, is a memory mapped peripheral, etc., use volatile.
In those cases I completely agree, but this is not one of those case.
The fact that volatile may help doesn't feel like enough reason to do it that way.
After all calling an external function like dubious(&t);
might also have the side effect of causing the compiler to update t, but that isn't a good fix.
union is intended to support punning of memory (though it can be messy, with big endian and little endian being mixable on some ARM architectures).
Also using volatile should cause a bunch of optimisations to be switched off for t and b which could still be applied for the union, and IMHO, that seems like a helpful test of 'what is right'. The compiler might use registers for the union, and maybe even do some loop unrolling on b[...]. The compiler's 'understanding' of union is different from the use of volatile.
IMHO replacing the 'broken code' with a union makes the programmer's intent much clearer to programmers and the compiler than using volatile to make 'broken code' work.
(sniglen - the 'broken code' was just an "off few minutes", I mean no disrespect :-)
My $0.02
snigelen: entered a ticket for the uint64 issue:
Complimentary code inspection is what we have here.
You guys are right about the volatile; it's a lazy way of fixing the issue. The problem is a side effect of taking advantage of the C "feature" of conflating arrays and pointers, plus knowledge of the bit width of the underlying data types. It's a lot cleaner, conceptually, to use union to "unpack" the data.
It also just struck me that the exact bit width is specified for the loop variables (i,j), but not for the values (t,b) that will be sent externally. The opposite is more desirable.
printf update: the word from perry is that if you modify syscalls.c's implementation of putch to do what you want, then printf should Just Work:
https://github.com/leaflabs/libmaple/blob/master/libmaple/syscalls.c#L94
mbolivar <>... if you modify syscalls.c's implementation of putch to do what you want, then printf should Just Work</>
Isn't that how operating systems get written?
They are their to make int main() { printf("Hello World!\n"); }
work :-)
You must log in to post.