LeoJr2015
Posts: 11
Joined: Sun Mar 01, 2015 4:25 pm

math problems in C

Sat Mar 21, 2015 8:16 pm

I am in the process of trying to convert a python script to C, and I am having issues with the math. For some reason, I cannot get a good result.

Code: Select all

#include <wiringPiSPI.h>
#include <mcp3004.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define THERMISTORNOMINAL 10000
#define TEMPERATURENOMINAL 25
#define BCOEFFICIENT 3435
#define SERIESRESISTOR 10000

int main(void) {
   int a;

   mcp3004Setup (100, 0); //MCP3004/MCP3008

   double b;

   a = analogRead (100);
   printf("\na = %d\n", a);

   b = (1023 / a) - 1;
   b = SERIESRESISTOR / b;
   printf("b = %d\n", b);

   double temp;

   temp = b / THERMISTORNOMINAL;
   temp = log(temp);
   temp /= BCOEFFICIENT;
   temp += 1.0 / (TEMPERATURENOMINAL + 273.15);
   temp = 1.0 / temp;
   temp -= 273.15;

   printf("\nThe temperature is: %d\n\n", temp);

}
result.jpg
result.jpg (52.67 KiB) Viewed 3397 times

For some reason, 'b' comes out to be the same value as 'a', and I can't see why. Am I missing something??

Thanks for any assistance!

User avatar
PeterO
Posts: 5147
Joined: Sun Jul 22, 2012 4:14 pm

Re: math problems in C

Sat Mar 21, 2015 8:23 pm

You have the wrong conversion specifier for doubles in printf.

"man 3 printf" will tell you all you need to know :-)

Code: Select all

[email protected] ~ $ ./temp

a = 413
b = 10000.000000

The temperature is: 25.000000

[email protected] ~ $
PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson

LeoJr2015
Posts: 11
Joined: Sun Mar 01, 2015 4:25 pm

Re: math problems in C

Sat Mar 21, 2015 8:28 pm

PeterO wrote:You have the wrong conversion specifier for doubles in printf.

"man 3 printf" will tell you all you need to know :-)

PeterO
Thanks, Peter. I will check that out! :)

BMardle
Posts: 112
Joined: Wed Feb 13, 2013 4:00 pm
Location: Isle of Wight

Re: math problems in C

Fri Apr 10, 2015 11:20 pm

Also, you might want to tweak "b = (1023 / a) - 1", since that'll do an integer division (i.e. rounded towards 0). Simplest solution: chance 1023 to 1023.0.
Bruce Mardle. "You know I yearn for a simpler time of barn dances and buggy rides before life was cheapened by heartless machines."

User avatar
experix
Posts: 204
Joined: Mon Nov 10, 2014 7:39 pm
Location: Coquille OR
Contact: Website

Re: math problems in C

Sat Apr 11, 2015 4:44 pm

I admit I'm stumped. I'm thinking I have to go back and check every bit of arithmetic in all of my programs. Try this and compare the results I have put in a comment beneath the code. Why is it
correct only in that infinitely pedantic last try?

Code: Select all

/*
gcc -g -Wall -o temp temp.c
*/
#include <stdio.h>
#define SERIESRESISTOR 10000
#define DOUBLERESISTOR 10000.0

int main( void )
{
  int a;  double aa;  double b;

  a = 413;
  printf( "\na = %d\n", a );
  b = (1023/a) - 1;
  printf( "b = %d %g\n", b, b );
  b = SERIESRESISTOR/b;
  printf( "b = %d %g\n", b, b );

  a = 413;
  printf( "\na = %d\n", a );
  b = 1023/a;
  printf( "b = %d %g\n", b, b );
  b -= 1;
  printf( "b = %d %g\n", b, b );
  b = SERIESRESISTOR/b;
  printf( "b = %d %g\n", b, b );

  a = 413;
  printf( "\na = %d\n", a );
  b = 1023.0/((double)a);
  printf( "b = %d %g\n", b, b );
  b -= 1.0;
  printf( "b = %d %g\n", b, b );
  b = ((double)SERIESRESISTOR)/b;

  a = 413;  aa = (double)a;
  printf( "\naa = %g\n", aa );
  b = 1023.0/aa;
  printf( "b = %g\n", b );
  b -= 1.0;
  printf( "b = %g\n", b );
  b = DOUBLERESISTOR/b;
  printf( "b = %g\n", b );

  return 0;
}
/*
My results:

a = 413
b = 0 5.29981e-315
b = 0 5.3683e-315

a = 413
b = 0 5.30499e-315
b = 0 5.29981e-315
b = 0 5.3683e-315

a = 413
b = 457575208 2.71991e-177
b = 915150416 6.14977e-46
b = -422455799 -1.94016e+187

aa = 413
b = 2.477
b = 1.477
b = 6770.49
*/

BMardle
Posts: 112
Joined: Wed Feb 13, 2013 4:00 pm
Location: Isle of Wight

Re: math problems in C

Sat Apr 11, 2015 7:49 pm

OK... [deep breath]:
Like any C function that accepts variable numbers of arguments ('varargs'), printf treats its optional args the way C treated all arguments in the bad old days before prototypes (or, these days, if the compiler doesn't see the prototype). If an argument has an integer type no bigger than 'int', an int is pushed on a stack. doubles are pushed as doubles. (float is converted to double before being pushed. long is pushed as long, but on the Pi sizeof(long)==sizeof(int) anyway, IIRC.)
Getting to specifics, in
double d=1.0; printf("%d %g", d, d);
d will be pushed twice, 8 bytes each, then printf will pull 4 bytes off the stack for the %d... which is half the bytes in one of the doubles, essentially gibberish! Then it'll pull 8 bytes of the stack for %g... which is the other half of the first double and half of the other, also gibberish!
If you want to print a double as an int, you need to convert it to an int:
printf("%d %g", (int)d, d);
(and if you wanted to print an int as a float/double you'd need to convert that by casting to either float or double).
(And if you think that's confusing, printf("%f"...) prints a double but scanf("%f"...) reads a float and scanf("%lf"...) reads a double!)
I hope that helped!
Bruce Mardle. "You know I yearn for a simpler time of barn dances and buggy rides before life was cheapened by heartless machines."

User avatar
jojopi
Posts: 3086
Joined: Tue Oct 11, 2011 8:38 pm

Re: math problems in C

Sat Apr 11, 2015 8:34 pm

experix wrote:/*
gcc -g -Wall -o temp temp.c
*/
That comment is a good start. If you compile like that with -Wall (enable almost all warnings), and check the compiler output, you should see seven warnings, all similar:

Code: Select all

temp.c: In function 'main':
temp.c:15:3: warning: format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat]
GCC's warnings are generally excellent. They are well worth enabling and paying attention to.

Neil
Posts: 98
Joined: Thu Sep 29, 2011 7:10 am
Contact: Website

Re: math problems in C

Mon Apr 13, 2015 2:18 pm

experix wrote:Why is it correct only in that infinitely pedantic last try?
Compilers are pedantic. If you're not, then you can expect the compiler to misunderstand what you have not told it.

Code: Select all

#define SERIESRESISTOR 10000
Do you know what the type of SERIESRESISTOR is?

Code: Select all

#define DOUBLERESISTOR 10000.0

int main( void )
{
  int a;  double aa;  double b;

  a = 413;
The signed integer 413 is assigned to a signed integer a, so no type conversion necessary.

Code: Select all

  printf( "\na = %d\n", a );
The first, and only, conversion specifier in your formatting string consumes a signed integer from the stack and formats it as a signed decimal number. Since a is already a signed integer, and the default promotion rules would not change that, then you're ok.

Code: Select all

  b = (1023/a) - 1;
The type of 1023 is signed integer, the type of a is also signed integer, so the arguments to the division (which is done as signed integer) do not need promoting. The division takes place as a signed integer operation. The result has signed integer type. The 1 also has the type of signed integer, and again the subtraction is performed as a signed integer operation, and the result has type of signed integer. The assignment, however, is to b, which has type double, so there is a type conversion from signed integer to double, and then the result is stored in b.

Code: Select all

  printf( "b = %d %g\n", b, b );
The first conversion specifier consumes a signed integer. However the first variadic argument is a double. Default promotion rules leave it as double, but doubles are 64-bit compared to 32-bit signed integers, so printf consumes exactly half of the double type, effectively corrupting the stack from this point. No point discussing the second conversion specifier, you've hosed it from here until the end of the call to printf.

Code: Select all

  b = SERIESRESISTOR/b;
SERIESRESISTOR has type signed integer. But b has type double which is greater than signed integer so the signed integer value in the numerator is promoted to a double (in a compiler-supplied temporary variable), then both arguments to the division are the same (double and double), the division is a double operation, and the double result is stored into a double-typed b. That's alright then, innit.

Code: Select all

  printf( "b = %d %g\n", b, b );
Same disaster as the previous call to printf.

Code: Select all

  a = 413;
Same as above.

Code: Select all

  printf( "\na = %d\n", a );
Same as above.

Code: Select all

  b = 1023/a;
Same as above.

Code: Select all

  printf( "b = %d %g\n", b, b );
Same as above.

Code: Select all

  b -= 1;
1 is a signed integer, so that gets promoted to a double value before being subtracted from b.

Code: Select all

  printf( "b = %d %g\n", b, b );
Same as above.

Code: Select all

  b = SERIESRESISTOR/b;
Same as above.

Code: Select all

  printf( "b = %d %g\n", b, b );
Same as above.

Code: Select all

  a = 413;
Same as above.

Code: Select all

  printf( "\na = %d\n", a );
Same as above.

Code: Select all

  b = 1023.0/((double)a);
Ah, now you're starting to tell the compiler something more interesting. Not only does the constant have type double, you are also now telling the compiler to convert a into a double before doing the division. So now the division is done in double precision, the result is double, and the assignment to b does not require any type conversions.

Code: Select all

  printf( "b = %d %g\n", b, b );
Doh, same problem with printf as before though (consuming half a double).

Code: Select all

  b -= 1.0;
Ok, now you have specified the type of the constant as double, so no promotion is required, and the subtract just happens.

Code: Select all

  printf( "b = %d %g\n", b, b );
Same printf disaster as above.

Code: Select all

  b = ((double)SERIESRESISTOR)/b;
Now here you are taking SERIESRESISTOR (which has type signed int) and casting it to double. The compiler would have done this for you since b is already double, and the composite type of (signed int + double) is double. But it helps to document the implicit conversion.

Code: Select all

  a = 413;  aa = (double)a;
Again, the conversion of a to double would be silently done by the compiler during assignment, but the cast helps document it.

Code: Select all

  printf( "\naa = %g\n", aa );
NOW you're starting to get it, although I've rarely seen the use of the g conversion specifier (the output changes between what you'd get for %e and %f depending on what the value is, wind direction, price of fish, etc).

Code: Select all

  b = 1023.0/aa;
double / double produces double, assigned to double. Check.

Code: Select all

  printf( "b = %g\n", b );
Printf format correct.

Code: Select all

  b -= 1.0;
Ok.

Code: Select all

  printf( "b = %g\n", b );
Ok.

Code: Select all

  b = DOUBLERESISTOR/b;
double / double produces double, assigned to double. Check.

Code: Select all

  printf( "b = %g\n", b );
Ok.

Code: Select all


  return 0;
}
Compilers follow rules. In this case the rules of the C programming language. It seems you don't know the rules. Understand (or at least have some understand of) the rules and then you'll know how to write programs in C.
I strongly suggest you read "The C Programming Language" by Kernighan and Ritchie. You can find the rules in the standard itself. Lots more useful links here: http://www.iso-9899.info/wiki/Books#References
You can also download the last draft of the C99 standard here: http://www.open-std.org/jtc1/sc22/WG14/ ... /n1256.pdf

Neil
(Note: to the die-hard C pedants I'm sure there are wide-open holes in the above hand-wavey description, so feel free to open another thread and pull it apart.)

User avatar
experix
Posts: 204
Joined: Mon Nov 10, 2014 7:39 pm
Location: Coquille OR
Contact: Website

Re: math problems in C

Mon Apr 13, 2015 3:05 pm

Thank you, BMardle and jojopi, for setting me straight. My only excuse is that I do pay attention to those compiler warnings about wrong-type arguments and fix them in my code before I get to the testing phase, so I have not run into that kind of trouble with printf. But I have done considerable work with assembly language so should have recognized what was going on.
Does anybody have ideas about dealing with these warnings:

initialisation discards 'const' qualifier from pointer target
With 'const' it seems you are damned if you do, damned if you don't.

'variable-name' may be used uninitialised in this function
Usually it arises when I confuse the compiler with goto's and such-like. I don't like to initialize just to get rid of that, because a spurious initialization may be no better than none and anyway it is junk code.

format '%lu' expects argument of type 'long unsigned int', but argument 3 has type '__u32'
Kerrisk has some discussion of this, mainly where special types #defined in header files are being passed to printf and such. He recommends casting them to a type that is sure to be big enough.

pointer targets in assignment differ in signedness
What if you need to pass the same object to functions that declare their arguments differently (and you think you know what you are doing)?

passing argument 1 of 'freE' discards 'const' qualifier from pointer target type
So I have a pointer in a struct and I want everybody to regard this as a const pointer, except when I finally get to the function that destroys this struct.

passing argument N of 'function' discards 'volatile' qualifier from pointer target type
Where can I find a good discussion of when and how to use 'volatile'?

User avatar
Paeryn
Posts: 2747
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England

Re: math problems in C

Mon Apr 13, 2015 4:41 pm

experix wrote:Does anybody have ideas about dealing with these warnings:

initialisation discards 'const' qualifier from pointer target
With 'const' it seems you are damned if you do, damned if you don't.
I assume you're trying to initialize a non-const pointer from a const pointer. That's a no-no unless you're really sure that you don't care that you could break something. The const is there to tell the compiler "what this points to isn't going to change" and "please make sure I don't pass it to a function that may try to change it". By copying that pointer to a non-const then your function could easily change it. The compiler might also have the contents previously saved in a register and have optimised out the re-reading of it after your potentially altering function.
experix wrote:'variable-name' may be used uninitialised in this function
Usually it arises when I confuse the compiler with goto's and such-like. I don't like to initialize just to get rid of that, because a spurious initialization may be no better than none and anyway it is junk code.
Gotos... <shudders>. Either accept the warning and risk using the uninitialised variable if you sometime later alter your code or just add the initialisation. What do you mean by "junk code", to me that means code that has no place being there.
experix wrote:format '%lu' expects argument of type 'long unsigned int', but argument 3 has type '__u32'
Kerrisk has some discussion of this, mainly where special types #defined in header files are being passed to printf and such. He recommends casting them to a type that is sure to be big enough.
If you're using %lu then the corresponding parameter needs casting to (long unsigned int). It might be that on the RPi a long is the same size as an int, but on another system a long might be bigger and then you'd have problems. The compiler is just doing what it can to warn you and telling you that a long is not a __u32.
experix wrote:pointer targets in assignment differ in signedness
What if you need to pass the same object to functions that declare their arguments differently (and you think you know what you are doing)?
Are you sure that the function can cope? What if you pass a pointer to int when the function expects a pointer to unsigned int and the value pointed to has the value -1? If you are sure then cast the pointer to the appropriate type when you call the function.
experix wrote:passing argument 1 of 'freE' discards 'const' qualifier from pointer target type
So I have a pointer in a struct and I want everybody to regard this as a const pointer, except when I finally get to the function that destroys this struct.
If you really need to then cast it to a non-const.
experix wrote:passing argument N of 'function' discards 'volatile' qualifier from pointer target type
Where can I find a good discussion of when and how to use 'volatile'?
volatile is used primarily for pointers to shared memory, where the values pointed at may be modified at any time outside of the current process. IE the compiler will always read the value from memory rather than using a previous copy it had read.
She who travels light — forgot something.

Killertechno
Posts: 181
Joined: Wed Jan 02, 2013 8:28 am

Re: math problems in C

Thu Dec 20, 2018 11:25 am

Hi to all, not same trouble but I'm porting program from python too....

Code: Select all

#define		Gauss_sigma		0.025
#define		Gauss_wiLength	2.0
#define		SPS				100


double dT;
unsigned int Gauss_smplLen;


int main (void)
{
	dT = 1.0/(double)SPS;	// quanto di campionamento
	printf("Lunghezza finestra filtro %f campioni (double)\r\n", (Gauss_wiLength/dT));
	Gauss_smplLen = (int)(Gauss_wiLength/dT);
	printf("Lunghezza finestra filtro %d campioni (int)\r\n", Gauss_smplLen);
	Gauss_smplLen = (Gauss_wiLength/dT);
	printf("Lunghezza finestra filtro %d campioni (int)\r\n", Gauss_smplLen);
	return (0);
}
Gauss_smlLen should be 200: I got correct value (200.000000) in float point, while I always get 199 on both other cases.
In similar discussions it was said troubled was due to cast
Gauss_smplLen = (int)(Gauss_wiLength/dT);
but without cast is the same....

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 24162
Joined: Sat Jul 30, 2011 7:41 pm

Re: math problems in C

Thu Dec 20, 2018 11:30 am

Killertechno wrote:
Thu Dec 20, 2018 11:25 am
Hi to all, not same trouble but I'm porting program from python too....

Code: Select all

#define		Gauss_sigma		0.025
#define		Gauss_wiLength	2.0
#define		SPS				100


double dT;
unsigned int Gauss_smplLen;


int main (void)
{
	dT = 1.0/(double)SPS;	// quanto di campionamento
	printf("Lunghezza finestra filtro %f campioni (double)\r\n", (Gauss_wiLength/dT));
	Gauss_smplLen = (int)(Gauss_wiLength/dT);
	printf("Lunghezza finestra filtro %d campioni (int)\r\n", Gauss_smplLen);
	Gauss_smplLen = (Gauss_wiLength/dT);
	printf("Lunghezza finestra filtro %d campioni (int)\r\n", Gauss_smplLen);
	return (0);
}
Gauss_smlLen should be 200: I got correct value (200.000000) in float point, while I always get 199 on both other cases.
In similar discussions it was said troubled was due to cast
Gauss_smplLen = (int)(Gauss_wiLength/dT);
but without cast is the same....
Best to start anew post rather than necro this old one.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Contrary to popular belief, humorous signatures are allowed. Here's an example...
“I think it’s wrong that only one company makes the game Monopoly.” – Steven Wright

BMardle
Posts: 112
Joined: Wed Feb 13, 2013 4:00 pm
Location: Isle of Wight

Re: math problems in C

Thu Dec 20, 2018 11:58 am

Hi, Killertechno.
I expect your '200's are actually a teensy bit smaller. Casting a floating-point number to an int truncates it. Try adding 0.5 then casting.
Bruce Mardle. "You know I yearn for a simpler time of barn dances and buggy rides before life was cheapened by heartless machines."

Return to “C/C++”