I was trying to write a program to calculate the value of x^n using a while
loop:
#include <stdio.h>
#include <math.h>
int main()
{
float x = 3, power = 1, copyx;
int n = 22, copyn;
copyx = x;
copyn = n;
while (n)
{
if ((n % 2) == 1)
{
power = power * x;
}
n = n / 2;
x *= x;
}
printf("%g^%d = %f\n", copyx, copyn, power);
printf("%g^%d = %f\n", copyx, copyn, pow(copyx, copyn));
return 0;
}
Up until the value of 15 for n
, the answer from my created function and the pow
function (from math.h
) gives the same value; but, when the value of n
exceeds 15, then it starts giving different answers.
I cannot understand why there is a difference in the answer. Is it that I have written the function in the wrong way or it is something else?
CodePudding user response:
When I run your code I get this:
3^22 = 31381059584.000000
3^22 = 31381059609.000000
This would be because pow
returns a double
but your code uses float
. When I changed to powf
I got identical results:
3^22 = 31381059584.000000
3^22 = 31381059584.000000
So simply use double
everywhere if you need high resolution results.
CodePudding user response:
You are mixing up two different types of floating-point data. The pow
function uses the double
type but your loop uses the float
type (which has less precision).
You can make the results coincide by either using the double
type for your x
, power
and copyx
variables, or by calling the powf
function (which uses the float
type) instead of pow
.
The latter adjustment (using powf
) gives the following output (clang-cl compiler, Windows 10, 64-bit):
3^22 = 31381059584.000000
3^22 = 31381059584.000000
And, changing the first line of your main
to double x = 3, power = 1, copyx;
gives the following:
3^22 = 31381059609.000000
3^22 = 31381059609.000000
Note that, with larger and larger values of n
, you are increasingly likely to get divergence between the results of your loop and the value calculated using the pow
or powf
library functions. On my platform, the double
version gives the same results, right up to the point where the value overflows the range and becomes Infinity
. However, the float
version starts to diverge around n = 55
:
3^55 = 174449198498104595772866560.000000
3^55 = 174449216944848669482418176.000000
CodePudding user response:
Floating point math is imprecise (and float
is worse than double
, having even fewer bits to store the data in; using double
might delay the imprecision longer). The pow
function (usually) uses an exponentiation algorithm that minimizes precision loss, and/or delegates to a chip-level instruction that may do stuff more efficiently, more precisely, or both. There could be more than one implementation of pow
too, depending on whether you tell the compiler to use strictly conformant floating point math, the fastest possible, the hardware instruction, etc.
Your code is fine (though using double
would get more precise results), but matching the improved precision of math.h
's pow
is non-trivial; by the time you've done so, you'll have reinvented it. That's why you use the library function.
That said, for logically integer math as you're using here, precision loss from your algorithm likely doesn't matter, it's purely the float
vs. double
issue where you lose precision from the type itself. As a rule, default to using double
, and only switch to float
if you're 100% sure you don't need the precision and can't afford the extra memory/computation cost of double
.
CodePudding user response:
Precision
float x = 3, power = 1; ... power = power * x
forms a float
product.
pow(x, y)
forms a double
result and good implementations internally use even wider math.
OP's loop method incurs rounded results after the 15th iteration. These roundings slowly compound the inaccuracy of the final result.
316 is a 26 bit odd number.
float
encodes all odd numbers exactly until typically 224. Larger values are all even and of only 24 significant binary digits.
double
encodes all odd numbers exactly until typically 253.
To do a fair comparison, use:
double
objects andpow()
orfloat
objects andpowf()
.
For large powers, the pow(f)()
function is certain to provide better answers than a loop at such functions often use internally extended precision and well managed rounding vs. the loop approach.