Conversation started Oct 3, 2020 at 8:02.
Oct 3, 2020 08:02
@JohnRennie: Hi sir :-)
Are you free now?
@GuruVishnu hi, yes I'm free :-)
Ok sir. Could you please tell whether the following expression is a good way to find the units place digit?:
r = (c/10-floor(c/10))*10;
For some reason, this doesn't work well for number 34:
In C/C++ ?
In C++ sir. Here's the full code:
#include<iostream>
#include<math.h>

using namespace std;

int main()
{
	double a = 1, b = 1, c = 0, sum=0;
	int r = 0;
	do
	{
		c = a + b;
		a = b;
		b = c;
		r = (c/10-floor(c/10))*10;
		cout << r<<"\t"<<c<<endl;
		if (r % 2 == 0)
			sum += c;
	} while (c < 55);
	cout << sum;
}
One of the things you have to be careful of in any language is that decimals cannit be exactly represented in binary so you can get rounding errors.
I'd guess that is what happens with 34. You'd have to go through the individual operations in your expression to find out where the rounding error is creeping in.
Fortunately there is a far simpler solution. I would use:
r = ((int) c) % 10;
Oct 3, 2020 08:09
Thank you sir. I agree using modulo is simpler, the problem is the number c can be as large as 4 million and can lead to data loss when type casted to int :-(
Is there any workaround to find the unit place digit for type double?
r = ((long long unsigned) c) % 10;
long long unsigned can be up to 18,446,744,073,709,551,615
Thank you very much sir. Now it works:
Or you could use floor(c) % 10
But how can we use a non-integral operand (of type long long) in the modulo operator?
I thought only (integer)%(integer) is valid.
The (long long unsigned) cast converts it to an integer type, so you do have integers both sides of the modulo operator.
@GuruVishnu long long unsigned is an integer type
Oct 3, 2020 08:17
Ah. Fine sir. Thank you :-)
I wonder what was causing the original rounding error ...
@JohnRennie A small doubt, how can this give us the unit place? c is already a whole number.
So, floor(c)=c and c%10 is invalid as c is of type double. Maybe if I delare it as long long unsigned, it would solve all the problem.
Any integer mod 10 gives the rightmost digit ...
Do you mean how does the cast to long long unsigned work?
No sir. I can understand that we're explicitly typecasting the double output to long long unsigned. I was wondering how the double value of c could be used in the modulo operator to give us the unit digit. Especially, I was referring to the expression floor(c) % 10.
Is it something like the range of the function floor() is of the type integer?
Ah, yes, you can't use floor(c) % 10 as that will complain the the left side is not an integer type.
You'd have to use ((long long unsigned) floor(c)) % 10 and in that case the call to floor() is unnecessary.
Oct 3, 2020 08:30
Ok sir. Thank you. Although, I must be thinking of an efficient way to solve this problem. This is just the straight forward approach.
Aha! Re your original approach, try this:
int main()
{
  long long unsigned r;
  double c, d;

  c = 34;

  d = (c/10-floor(c/10))*10;
  printf("d - 4 = %e\n", d - 4.0);

  r = (c/10-floor(c/10))*10;
  cout << r<<"\t"<<c<<endl;

  return 0;
}
I get:
D:\rhs\c>rat
d - 4 = -8.881784e-16
3       34
So the rounding errors are making d very slightly less than 4.
Oh! You were analysing why the first method went wrong? I simply changed the data type of all the variables to long long unsigned :-)
I didn't expect that rounding errors would creep in.
Yes, I was curious where the error was coming from.
As a general rule, whenever you are using floating point variables you need to bear in mind that there will be rounding errors.
Ok sir. Good to know. Thank you. I'll be careful from now on.
But here, I'm unable to understand why we would get such errors as we're just dividing by 10. Moreover, before assigning this to the integer variable, we're multiplying with 10 again. Could you explain why this is so sir?
OK try:
  d = c/10-floor(c/10);
  printf("d - 4 = %e\n", d - 0.4);
I get:
d - 4 = -1.110223e-16
floor(c/10) will be exactly equal to 3 because floor() is guaranteed to return an integer, so it must be that c/10 isn't exactly equal to 3.4.
Though:
  d = c/10;
  printf("d - 4 = %e\n", d - 3.4);
prints:
0.000000e+00
Oct 3, 2020 08:46
So, is it a problem with the floor function?
Try:
  d = c/10-3;
  printf("d - 0.4 = %e\n", d - 0.4);
I get:
d - 0.4 = -1.110223e-16
Yes, so it isn't the floor function because replacing it with the integer 3 still gives the problem.
I think the rounding error comes when we do arithmetic between a float and a pure integer.
d = c/10-1.0 and d = c/10-2.0 both give an exact result, but d = c/10-3.0 and d = c/10-4.0 do not.
Try:
  d = c/10-1.0;
  printf("c/10-1.0 gives %e\n", d - 2.4);
  d = c/10-2.0;
  printf("c/10-2.0 gives %e\n", d - 1.4);
  d = c/10-3.0;
  printf("c/10-3.0 gives %e\n", d - 0.4);
  d = c/10-4.0;
  printf("c/10-4.0 gives %e\n", d + 0.6);
Now it gets weird. I tried:
  c = 34;
  d = c/10-1.0;
  printf("c/10-1.0 gives %e\n", d - 2.4);
  d = c/10-2.0;
  printf("c/10-2.0 gives %e\n", d - 1.4);
  d = c/10-3.0;
  printf("c/10-3.0 gives %e\n", d - 0.4);
  d = c/10-4.0;
  printf("c/10-4.0 gives %e\n", d + 0.6);
  d = c/10-5.0;
  printf("c/10-5.0 gives %e\n", d + 1.6);
  d = c/10-6.0;
  printf("c/10-6.0 gives %e\n", d + 2.6);
  d = c/10-7.0;
  printf("c/10-7.0 gives %e\n", d + 3.6);
  d = c/10-8.0;
  printf("c/10-8.0 gives %e\n", d + 4.6);
And that gives:
c/10-1.0 gives 0.000000e+00
c/10-2.0 gives 0.000000e+00
c/10-3.0 gives -1.110223e-16
c/10-4.0 gives -1.110223e-16
c/10-5.0 gives 0.000000e+00
c/10-6.0 gives 0.000000e+00
c/10-7.0 gives 0.000000e+00
c/10-8.0 gives 0.000000e+00
I think to get any further would require a detailed look at how the values are being represented in binary, and my enthusiasm for doing that is limited :-)
Oct 3, 2020 09:00
I agree sir. Thank you for spending a lot of time for this :-)
 
Conversation ended Oct 3, 2020 at 9:00.