Uncommon bug in M_APM?

Discussion in 'Mac Programming' started by jerrywickey, Jul 30, 2010.

  1. jerrywickey macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #1
    Wonderful help last night getting M_APM math library up for me. Thank you. I finished with the math later last night, but have a problem, that I couldn't get to go away all day: Anyone know of undocumented bugs in M_APM like this one?

    If I remark out the m_apm_multiply() statement (marked in code with //====>) the loop runs with no problems, of course, providing an incorrect answer. With that multiply statement in, it sometimes freezes at run time, sometimes gives me a runtime error of "can't find "cpu_capabilities.h" file and sometimes another runtime error I can't remember or reproduce right now.

    I tried both sprintf and m_apm_set_long to get int nnn into m_apm n to make sure that wasn't screwing things up.

    By having each step report, I found that the multiply processes ok, and things progress to the next loop iteration. But it doesn't proceed. If I remark out the multiply, the loop proceeds just fine. Does a C++ while loop use a stack that m_apm could be messing up? I am just throwing out ideas.

    Any ideas?

    Jerry

    Code:
    	M_APM p,L,n,iL,ct,cn,tr,a,b,c,d,p2;
    	int ll,nn,nnn;
    
    	p = m_apm_init();  
    ... for each M_APM handles ...
    
    	m_apm_set_string(p,"2e21");
    	m_apm_set_string(L,"2e21");
    ...
    			// this formula is called the holy smokes!
    			// it is the chance (0-1) that 
    			//   p, L and n can have the given values
    			//
    			//  p! /(p-n)! n! /L^n  * (1-1/L)^(p-n)  
    			//
    			//   lets break it into two parts since we 
    			//   do not want to take the factorial of 2e21  
    			//   not even 32000 digits can do that
    			//           a          *      b
    			
    			// find a  
    			// by sneaking around (p-n)! with a loop
    			a= MM_One;
    			p2= p;
    			nnn= nn;
    			while (nnn>=1){
    				
             // tried		m_apm_set_long(n, nnn);
    
    	 // both of 		sprintf(obuf, "%d", nnn);
    	 // these		m_apm_set_string(n, obuf);   
    				
    				m_apm_divide(tr, 400, a, n);
    
    	 //=====>        	m_apm_multiply(a, tr, p2);
    
    				m_apm_divide(tr, 400, a, L);
    				a=tr;
    				m_apm_subtract(tr, p2, MM_One);
    				p2=tr;
    				nnn--;
    			}
    			
    			// find b
    			m_apm_subtract(c, MM_One, iL);
    			m_apm_set_long(n, nn);        // <==== set_long(int ) works fine here
    			m_apm_subtract(d, p, n);
    			m_apm_pow(b, 400, c, d);
    			
    			// put it all together
    			m_apm_multiply(cn, a, b);
    			m_apm_add(tr, ct, cn);
    			ct=ct+tr;
    			  
    			m_apm_to_fixpt_string(obuf, 15, cn);   
    			m_apm_to_fixpt_string(obuf2, 3, ct);   
    			printf("\rn = %5i   %s       %s\n",nn, obuf, obuf2); 
     
  2. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #2
    OK??!! This is off the chart.

    Wondering if something was fiddling with the data in ways it should not be, I left the m_apm variable 'a' alone, leaving it declared but unused. I declared another m_apm variable. I replaced the reference to 'a' with the new variable in the m_apm multiply that was causing trouble. Now it works with out problems.

    No, there wasn't any mistakes in the 'a' declaration or initialization.

    Any ideas?

    This doesn't make any sense.

    Jerry
     
  3. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #3
    An M_APM is typedef'd to a pointer to a struct. This is supposed to be "opaque", and manipulated by m_apm functions. If you have an M_APM and you want its value in another, use m_apm_copy. Assigning MM_One to a assigns the pointer. Messing with a "constant" like this will probably bad news.

    -Lee
     
  4. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #4
    Good advice, but I didn't touch m_apm types except with m_apm functions:

    Such as

    Code:
    m_apm_set_long(n, nnn);
    m_apm_set_string(n, obuf);   
    
    This

    Code:
    ct=ct+tr;
    
    was me messing around, trying to make it repeat the error I couldn't reproduce. I forgot to take it out before pasting it into the post.

    Code:
    m_apm_add(cn, ct, tr);
    ct=cn;
    
    is the way it appears in my code.

    Did I miss something?

    Jerry
     
  5. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #5
    Code:
    a= MM_One;
    p2= p;
    a=tr;
    p2=tr;
    ct=ct+tr;
    
    eventually most of your variables used in your loop are pointing to the struct init'd and assigned to tr.

    -Lee
     
  6. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #6
    With the exception of ct which was my cut and paste mistake, everyone of those are all m_apm type.

    Jerry
     
  7. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #7
    M_APM p,L,n,iL,ct,cn,tr,a,b,c,d,p2;

    M_APM is typedef'd to a pointer to a struct. The struct has a pointer to a dynamic block of memory. You can't just do an assignment. Use m_apm_copy.

    -Lee
     
  8. lloyddean macrumors 6502a

    Joined:
    May 10, 2009
    Location:
    Des Moines, WA
    #8
    Jerry,

    It looks like the use of the C++ wrapper would make things much easier for you. Or do you have something against using C++.
     
  9. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #9
    Nothing against C++; And wrappers addressing these things would be great. Some are included in the download. I have not yet looked at them.

    And probably won't if I can get my math to work in the next few hours.

    Thanks

    Jerry
     
  10. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #10
    What issues are you still having? Did you switch any M_APM assignment statements to m_apm_copy calls?

    -Lee
     
  11. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #11
    Yes.

    From your originally posted code:
    Code:
    			[COLOR="Red"]a= MM_One;[/COLOR]
    			p2= p;
    			nnn= nn;
    			while (nnn>=1){
    
    The red-hilited code assigns a pointer to MM_One to the variable 'a'. That is, 'a' now points to MM_One.

    Consider what happens on the first iteration of the loop, when this statement is reached (and is not commented out):

    Code:
    	 //=====>        	m_apm_multiply(a, tr, p2);
    
    Subsequent statements in the loop then assign a different pointer to 'a', but by then any damage to MM_One is already done, and its "oneness" is gone.

    So given that you no longer use 'a', and have made other changes to the code, and only posted fragments originally, we can't really tell what the revised code is except by guessing. Guessing at code rarely works. If you have a problem or a question with your revised code, please post the revised code.
     
  12. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #12
    You are sooo right crown. I was not thinking like the C compiler.

    And thanks lee.

    damn!!!

    Code:
    extern	void	m_apm_copy(M_APM, M_APM);
    There it is!

    The only issues that remain are correctly implementing the complex formula, but I did not realize that m_apm_copy was available and presumed that assignment was the only option.

    After your recent post, I looked through the .h file and found it. That's great. And I suspect you are correct about the original problem. Using this will likely clear up my original problem more cleanly.

    Jerry
     
  13. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #13
    Tell ya what would help me a lot:

    A disambiguation of the C assignment statement.

    I come from a much broader interpretation of indirection. In the land I come from we reserve memory and label it. That memory could contain a constant value or it could contain a value which is altered programatically by reference to its label or it could contain a label to another memory location which in turn could contain a constant, a value or again a label to still another location.

    This is often imprecisely referred to as an index because it is often used in this way.... altering the contents of a memory location which contains the label of a memory location which contains the value. In addition the label itself is always a constant (hard constant) and could be used as the base value for an index.

    C must implement the same indirection but with different terminology. I gather, such use would be called a pointer in C. Differentiation between a pointer and other label is ambiguous because in the most general sense all labels are pointers to something unless they are a hard constant, which would never be used in a higher level programming language.

    So...

    After a type def, such as m_apm a; or int b; memory is reserved the amount of which is specified by the type. 'a' and 'b' refer to the location of that memory.

    Does, the C statement a=b;

    a) Copy the contents of memory stored at 'b' into the memory at 'a' regardless if the size reserved for 'a' is sufficient or not or

    b) copy into the memory at 'a' the location where memory pointed to by 'b' can be found

    Jerry
     
  14. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #14
    Once upon a time, a man told a story. He hoped that this story would explain what language the storyteller was using. Unbeknownst to the man, many of the listeners were proficient polyglots, and they wondered why he would tell a story instead of just saying what language he was using.

    What language does your story actually refer to?

    The answer will help make sense of your story, which uses terms like "label" in ways that are specific to the language.

    Some languages, like C, have explicit pointers. Other languages have implicit pointers, sometimes also called references.


    Short answer: all C assignment works by copying bits. The bits of an int variable's storage are copied into the bits of a pointer variable's storage. If the destination storage is smaller, bits are lost.

    Not all types are "assignment compatible", and copying an int into a pointer or vice versa usually elicits a warning from the compiler. You are advised not to ignore these warnings.

    Also, a C array-variable name is actually a pointer to the first element of the array, so "assigning an array" does not actually copy the bits of the array. Passing an array as a parameter also doesn't copy, but passing a struct or union does. Passing an explicit pointer, typedef'ed or otherwise, does not copy what the pointer points to.

    Your questions sound like you don't know C, or don't know it well, and are interpolating a language you do know onto C. This might work, if the language is sufficiently similar to C. However, this doesn't seem to be the case.
     
  15. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #15
    The C assignment statement doesn't have much mystery behind it, unlike a language like C++ which allows you to overload the assignment statement, meaning what it does with a particular type can be user-defined. In C the assignment operator will take the value in the right-hand operand and store it in the left-hand operand. There are primitives and structs in C, that's it. Even with a struct, the value of each element is assigned, so there's still no magic.

    What seems to be vexing you is the notion of a pointer. A pointer variable still just holds a value. When you dereference a pointer with the unary * operator, this evaluates to whatever value is stored at that memory location, with the type of the pointer. I.e. if you dereference an int *, you get an int.

    When you assign a pointer to another pointer, you just copy the value stored, that is, the memory address. Now both variables point to the same memory location. In your case, you were taking various pointers to m_apm_struct's and assigning them to one another. This meant they all pointed to the same place in memory. The MM_one assignment was the worst because this is supposed to be a constant and you were trying to change it. This was made a bit more confusing because M_APM was typedef'd to a pointer to a m_apm_struct. That means you don't see that it is a pointer. Really, though, you're not supposed to care. You're only supposed to manipulate this type through m_apm_ functions. These handle all the messy memory allocation, etc. for you.

    Sorry to assume you knew a lot of this. My posts weren't intentionally cryptic, I thought telling you to use m_apm_copy instead of assignment would be clear.

    -Lee

    Edit: bah. beaten to the punch. Typing on an iPhone plus caring for the baby slowed me down.
     
  16. Sander macrumors 6502

    Joined:
    Apr 24, 2008
    #16
    This could be construed as a critique of operator overloading: the "you can't immediately tell what's going on" argument. I'll bite (gently).

    Had this been a C++ library, the library designers would probably have given these types "value semantics". The

    Code:
    a = MM_One;
    example would then have made a its own copy of the value of MM_One, just like when they had been ordinary ints. Also, "multiply", "add" etc. would have used their "normal" operators instead of function calls.

    This way, a "newbe" to the language would have to know less about the internals of this library to be able to use it.

    (Though to be honest, I wouldn't quickly use the "it's easier for newbes" argument to defend C++ :) )
     
  17. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #17
    http://forums.macrumors.com/showpost.php?p=6098288&postcount=17

    took me a while, but that's the meanest thing I've said about operator overloading. It can do wonders to simplify code, but if used without understanding what's going on under the covers, terrible efficiency can result. I felt that I handled this pretty neutrally, and while I did imply that there can be some mystery about what an operator does in C++, I think that's fair. Especially if you have a binary library with headers only. = could send the author the contents of your home directory.

    -Lee
     
  18. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #18
    chown33

    I don't know C well at all. Enough to write my own iPhone app. DNA Song. Well objective C anyway.



    lee,

    Your posts were the most helpful. Actually I first assumed that I would need to do all manipulations of m_apm values through m_apm functions, but could not find an assignment function. I even considered the following as a substitute for assignment: store m_apm a into m_apm x

    m_apm_add(x, a, MM_Zero);

    But decided surely C has this covered with a well behaved '=' assignment. Well... it doesn't.

    I never thought to search for 'copy' until you mentioned it and I found it immediately.

    Thank you. By the way I got all the math implemented and since using m_apm_copy all the strange activity has gone away. You had it figured out.




    In general,

    I understand you deal with newbies a lot. And the biggest difficulty for a newbie is incorporating the concept that a task must be resolvable before it can be translated into a sequence of definitive imperatives. And resolvability is sometimes not resolvable. One must state the task in manner which lends itself to resolvability before one can actually disambiguate the task.

    What I am saying is that newbies are often stuck trying to do something in a way which can not be done, but supposing that it can be done if only they knew the correct "code" or magical incantation to make it work. You guys easily realize that they are approaching the problem from the wrong direction. The newbie then finds it difficult to reorganize his thinking because he is inexperienced.

    The terminology I use comes from the fundamental principles of programming. Its not a book. They are the fundamental principals of task disambiguation. I cut my teeth on 6502, Z80, and 8080 assembler. I built a working turing machine from Lego toy bricks when I was 14. No kit, not diagrams from a book. Just considering the structures required of any machine such that it can successfully resolve any resolvable task and building what that implies piece by piece. Disambiguating a task is trivial work for me.

    We name things when we find it inconvenient or difficult to describe them instead. We use names like "pointer" or "metacarpal" or "quasar" because they sound sexy. They make us look professional whatever our field, programmer, doctor or cosmologist.

    A problem arises when we use names claiming it is for connivence but it is actually because we have difficulty describing it. Some concepts are difficult to keep in ones head. Sometimes while we are very experienced at using something, and use it very well, expertly in fact, we don't actually know the details of what it is.

    Perhaps answering these questions will be easy. Or perhaps, the effort will reveal that you know the names and are expertly practiced at using them, but do not know exactly what is really going on. I am not insulting you. I have a great deal of respect for you. You are clearly very experienced. I am just trying to learn from you the things about C which text books don't teach because they are too busy trying to help people understand the art of programming.


    C statements

    1 ---> int a, b;
    2 ---> Some_Type c;

    3 ---> a = b;
    4 ---> a = 'nnnn';
    5 ---> a = c;
    6 ---> c = a;


    C++ statements

    7 ---> int d, e;
    8 ---> Some_Type f;

    9 ---> d = e;
    10 ---> d = 'nnnn';
    11 ---> d = c;
    11 ---> f = c;


    Questions:

    1) While in line 2, the number of bytes is defined somewhere in Some_Type and the meaning of each byte is interpreted by Some_Type, in line 1, it is 4 bytes. Do both merely set aside x number of bytes of memory and simply replace all references to 'a' and 'b' in code with a reference to that memory at 'a' and at 'b' respectively?

    2) Does line 3 fetch the contents of memory at 'b' and store it in memory at 'a'?

    3) Line 4 can easily be explained if the compiler embeds in code the constant 'nnnn', but this means that the '=' assign statement must be consistent, storing the literal string in memory at 'a'. Is this correct?

    4) (type casting) Does the '=' assignment statement in line 5 magically change into a different type of assignment statement? Does it consistently move the contents of memory at 'c' into memory at 'a' and expect the code which handles 'a' to correctly interpret the weirdness created by this? Or does this '=' assignment statement magically change into another sort of assignment statement which does something different?

    5) If this is so than '=' assignment will work with any 'a' 'b' or 'c' which are the same type. I gather it changes. Does the compiler get its hint to alter its compilation from the difference in type? And what exactly does it do?

    6) Which lines mean different things in C and C++?

    Jerry
     
  19. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #19
    1) Unknown. Some_Type could be a primitive type or a pointer or even a pointer to a function.

    If it is a pointer you need allocate the memory yourself.

    As for function pointers they simply point to the memory location of a function.

    2) Yes.

    3) As long as a is a constant pointer to char that statement would work OK. Seeing as the definition of a is not in the line mentioned I have to assume that the result is undefined behaviour.

    4) In C the '=' operator always means store the value in the right hand operand into the left hand operand. C will let you store a long in an int (although it will warn you) but it will just truncate the value to fit so you will lose data.

    You should probably read about the integer and floating point promotion rules in C. They catch a lot of people out (including me).

    5) No. You can't cast any type to any other type. Unless you cast a pointer to a void pointer and then you have lost all type safety anyway. I think I might have missed the point you were getting at though.

    6) I forget if you can overload the '=' operator in C++. One would hope that the C++ designers kept at least that level of sanity. If you can then anywhere you see '=' it could potentially mean something different in C++. If not then they should both behave as expected.
     
  20. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #20
    Cromulent covered most of it. I'll add to 6), though. It looks like things got a bit off in the C++ examples, but anyway... You can define the behavior of = in C++ such that assigning an int to a Some_Type will do whatever you want. This is where C++ will diverge from C in terms of your questions. In C there will be promotions performed as Cromulent mentioned, but those rules are fixed. In C++ the primitives have their operators fixed, but once you throw a Class in as any operand, the programmer can define this behavior.

    -Lee
     
  21. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #21
    In line 4, your notation 'nnnn' does not represent a string. That would require double quotes: "aaaa". Single quotes signify a character constant.

    A character constant expression has an integer value which is the value of the code for a character from a character set (also called a "text encoding"). The character set is typically based on ASCII (i.e. has the same initial 128 codes as ASCII), but it need not be. For example, EBCDIC is valid. Once the source is compiled, though, the character constants are irrevocably EBCDIC, and can't be changed to any other character set except by recompiling the source.

    The typical use is a SINGLE character constant, such as 'n', but there is an Apple language extension that accepts a multi-character notation and packs one char per byte of an integer value. Example: 'abcd' would resolve to the integer value 0x61626364, when the characters are drawn from an ASCII-based character set.

    Also note: "char" is a type, "character" is a descriptive word. The distinction is important when char is promoted to other types, or when a character won't fit in a char type (e.g. Unicode).

    If you had used this notation:
    Code:
     a = "nnnn";
    
    Then "nnnn" is an initialized array (a sequence of like-type elements) of 5 chars in memory (4 'n', 1 terminating nul byte). The memory is presumed constant (read-only), but that may or may not be enforced at runtime.

    The expression "nnnn" resolves to a pointer to (i.e. the address of) the first array element. That address is stored in the memory reserved for the variable a. If 'a' were a pointer type, then we could say "a points to a C string". Since 'a' isn't a pointer type, all we can say is "a contains the int-sized part of the address of a C string".

    In general, pointers are not directly assignable to non-pointer types without eliciting a compiler warning. So the compiler would warn about this assignment. And if the gcc option to treat warnings as errors were enabled, then the compiler would fail with this error.

    Incidentally, your variables are uninitialized, which the compiler also has a warning for.


    When typedefs appear, the compiler essentially uses the underlying type, whatever it was defined to be. So if the r-value and l-value are both scalars, then simple size coercion is performed. If a representation conversion is needed (e.g. integer to floating point), then the necessary machine instructions for that conversion are emitted.

    A typedef is fundamentally a source-code shorthand. The same effect can be obtained by the properly parenthesized use of the builtin types.

    If the r-value and l-value aren't scalars, then they must be composites. There are two kinds of composites: arrays and struct/unions. Struct/union types elicit code that performs a copy of the entire struct/union. Array types do not, and therein lie many difficulties. Fundamentally, C does not have an actual "array type". It has types which are pointers to (addresses of) other types, and these pointers are assignable, comparable, and can undergo arithmetic (pointer arithmetic, which is scaled by the size of the type).


    I first learned all this from the original K&R C book, which is written in a concise style that many people find too terse. You may find it useful. I also have some experience working on compilers, and nothing shows you what the language is better than seeing its actual mechanics.

    Another important reference is the C language specification, which uses excruciatingly correct language, reducing amiguity. It also has some pretty clear descriptions of the memory model for variables. It's not light reading, but it is definitive.

    Other books I've found useful as references:
    "C A Reference Manual", by Harbison and Steele
    Anything by P. J. Plauger

    The other thing I strongly recommend is "Try it, see what happens". Given your familarity with assembly language, you should be able to figure out the compiled code with relative ease. Also, I think you can still target PowerPC on Snow Leopard, and that has a more regular instruction set and addressing modes than x86 does. So even if you don't run the code compiled for PPC, you can still inspect it and see what any particular source code sequence is doing.
     
  22. jared_kipe macrumors 68030

    jared_kipe

    Joined:
    Dec 8, 2003
    Location:
    Seattle
    #22
    After seeing this, I'm seriously considering making an Objective-C wrapper for this.
     
  23. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #23
    Not a bad idea. Looks like there's about 60 functions to wrap, though some are initialization and destruction that would be implicit.

    Maybe add methods to allow setting with an NSNumber, etc. Sounds useful to me.

    -Lee
     
  24. Sander macrumors 6502

    Joined:
    Apr 24, 2008
    #24
    So could m_apm_multiply(). If you don't trust library writers, I think the particular language it was written in is the least of your problems :)

    As to the "terrible efficiency": there is the problem of "hidden copies" which sometimes have to be made, resulting in lots of temporary objects being constructed and destructed in seemingly innocuous expressions. Some of this is actively being worked on by the standardization committee.
     
  25. jerrywickey thread starter macrumors member

    Joined:
    Apr 16, 2009
    Location:
    Key West
    #25
    Thank you for your efforts. Cromulent has it right. What is intended and what is read is nearly never the same. This would require a lot of disambiguation.

    Sorry for my delay. I was distracted by a paying programming gig.

    My comments.

    I don't mind at all overloading operators. I take that to mean altering their functionality within a defined scope. What seems inconsistent to me is to call it overloading. It is merely defining or redefining a function like any other. We use a lot of different words in place of describing what is going on. This leads to the need for more disambiguation, instead of what the definition of the word was intended to do, ameliorate that very problem.

    The is only one single language which definitively disambiguates all resolvable tasks. The various machines languages for each processor approximates this very well. The more we try to use big words in higher languages, the more we confound the approximation we are attempting.

    It is a good thing programmers are very skilled at disambiguation, otherwise we would be hopelessly lost. Case in point: I have never seen a unambiguous description of a "pointer" where no reference in the description refers to something not in the description. Not in a manual, not in a tutorial, not in person. Try it you'll see.

    What ends up happening is someone finally says. "well.... you just have to be more experienced."


    By the way. It may have gotten lost in this thread, but I worked it all out with M_APM. No wrapper is needed. In fact a wrapper would just add to the ambiguity. It would make it simpler for someone to do specific things. But once they spend the time finding the wrapper they could have just done it directly with M_APM with out adding a dozen more

    "Well you could do it that way but thats called and this is called and I don't really know why I call them this or that when it is really just this."


    Jerry
     

Share This Page