Precision of double numbers with 17 or more digits - c

I am getting precision loss when converting a big double (17+ digits) number to integer.
#include <stdio.h>
int main() {
int n = 20;
double acum = 1;
while (n--) acum *= 9;
printf("%.0f\n", acum);
printf("%llu\n", (unsigned long long)acum);
return 0;
}
The output of this code is:
12157665459056929000
12157665459056928768
I can't use unsigned long long for the calculations because this is just a pseudo code and I need the precision on the real code, where divisions are included.
If I increase the decimals the first output becomes, for e.g 12157665459056929000.0000000000.
I've tried round(acum) and trunc(acum) and in both cases the result were the same as the second output. Shouldn't they be equal to the first??
I know float has only 6 decimals precision and double has about 17. But what's wrong with the digits?!?

Actually, when I change the acum's type to unsigned long long like:
unsigned long long acum = 1;
the result is:
12157665459056928801
When I use Python to calculate the accurate answer:
>>9**20
12157665459056928801L
You see?
12157665459056929000 is not an accurate answer at all and is actually an approximation of the accurate.
Then I change the code like this:
printf("%llu\n", (unsigned long long)1.2157665459056929e+019);
printf("%llu\n", (unsigned long long)1.2157665459056928e+019);
printf("%llu\n", (unsigned long long)1.2157665459056927e+019);
printf("%llu\n", (unsigned long long)1.2157665459056926e+019);
And result is:
12157665459056928768
12157665459056928768
12157665459056926720
12157665459056926720
In fact 19 digits is exceeding the numeric digit limit of cpp and the result of converting such a big number is unexpectable and unsafe.

Related

Unable to calculate factorial , getting output as 0 in C

Tried calculating factorial of 65, got correct output. Anything greater than 65 results in output of 0. Shocking since I'm using unsigned long int. What is amiss ?
Code:
#include <stdio.h>
void factorial(int unsigned long);
int main()
{
int unsigned long num, result;
printf("\nEnter number to obtain factorial : ");
scanf("%ld", &num);
factorial(num);
}
void factorial (int unsigned long x)
{
register int unsigned long f = 1;
register int unsigned long i;
for (i=x;i>=1;i--)
f= f*i;
printf("\nFactorial of %lu = %lu\n",x,f);
}
You certainly did not get the correct result for 65! log2(65!) is just over 302 bits (Google it), so you'd need a long int of at least 303 bits to calculate that correctly. There is no computer in the world where long int is over 300 bits (let's see how this answer ages!).
The largest factorial you can compute in 64 bits is 20! (which is about 2.4e18).
Adding on to #JohnZwinck, the maximum value for a variable of type unsigned long long (ULLONG_MAX) is 18446744073709551615. So all the values greater than 20! are going to have a garbage value
You can refer to this for more information.

Pow is not working for two double digit numbers

I have made a code to read two long integers (A and B) from standard input and output (to standard output) A to the power of B.
It works for 1^some huge number, 3^3 etc.
But not for 13^16.
I've tried to put long int ans to solve it, it gave me a different value but not the right one.
#include <stdio.h>
#include <math.h>
int main()
{
int x, n;
long int ans;
scanf("%d \n %d",&x, &n);
ans = pow(x,n);
printf("%d", ans);
return 0;
}
pow(1,anything) is always 1. pow(3, 3) is 27. These are both quite small numbers and easily fit into a 32 bit integer. pow(13,16) is (approximately) 6.65 x 1017. This is too big for a 32 bit integer to contain. It will go into a 64 bit integer (although pow(14, 17) will not). It's likely that your compiler treats a long as a 32 bit value, which is not uncommon. You could try long long which is likely to be 64 bits or int64_t which is explicitly 64 bits long.
Note though that the prototype for pow() is
double pow(double x, double y);
which means that it is returning a double precision floating point number and then coercing it into the type of your variable. double (a 64 bit floating point number) only has 53 bits of precision in its mantissa, which means you are not going to get the exact number when you cast it back to even a 64 bit integer. You could use powl() whose prototype is
long double powl(long double x, long double y);
But long double might be defined as 80 bits or 128 bits or even only 64 bits (Microsoft). It might give you the precision you need, but such is the nature of power operations, your input numbers won't have to get much bigger to overflow the precision of even the longest long double.
If you really need to raise large numbers to large powers, you are going to need a big integer library.
Rather than use floating point pow() and friends with their potential limited precision for an integer problem within 64 bits (1316 needs 60 bits), use an integer power function.
unsigned long long upow(unsigned x, unsigned y) {
unsigned long long z = 1;
unsigned long long xx = x;
while (y) {
if (y % 2) {
z *= xx;
}
y /= 2;
xx *= xx;
}
return z;
}
int main() {
printf("%llu\n", upow(3, 3));
printf("%llu\n", upow(13, 16));
}
Output
27
665416609183179841
If code needs to handle answers more the 64 bits, consider long double (with potential loss of precision) or big_integer libraries.
You have defined "ans" as long int then you are trying to print it as int (%d - Take the next argument and print it as an int ) So change printf("%d", ans) to printf("%ld",ans) . your code would look something like this :
#include <stdio.h>
#include <math.h>
int main()
{
int x, n;
long int ans;
scanf("%d \n %d",&x, &n);
ans = pow(x,n);
printf("%ld", ans);
return 0;
}

Calculating max long value using exponent overflows

I'm trying to figure out maximum value for type long by calculating an exponential of base 2 to the power of the bit number.
Unfortunately the calculation overflows at step 61 and I don't understand why.
long exponential(int base, int exponent)
{
long result = (long)base;
for (int i = 0; i < exponent; i++) {
result *= base;
}
return result;
}
unsigned int sLong = sizeof(long);
long lResult = exponential(2, (sLong * 8) - 1);
lResult is 0 after running the function.
What's odd is that when I do this for char, short and int it works fine.
The code here has an off-by-one error.
Consider the following: what is the result of exponential(10, 2)? Empirical debugging (use a printf statement) shows that it's 1000. So exponential calculates the mathematical expression be+1.
The long type usually has 64 bits. This seems to be your case (seeing that the overflow happens around step 64). Seeing that it's a signed type, its range is (typically) from -263 to 263-1. That is, the maximal power of 2 that the data type can represent is 262; if the code tries to calculate 263, then overflow happens (you probably want to avoid it).
So, because of the off-by-one error, the code will cause an overflow for exponent greater or equal to 62.
To fix the off-by-one error, start multiplying from 1:
long power_of(int base, int exponent)
{
long result = (long)1; // 0th power of base is 1
for (int i=0; i<exponent;i++) {
result*=base;
}
return result;
}
However, this will not get rid of the overflow, because the long data type cannot represent the number 263. Fortunately, you can use unsigned long long, which is guaranteed to be big enough for the type of calculations you are doing:
unsigned long long power_of(int base, int exponent)
{
unsigned long long result = 1ULL; // 0th power of base is 1
for (int i=0; i<exponent;i++) {
result*=base;
}
return result;
}
Print it with the llu format:
printf("%llu", power_of(2, 63));

Big numbers in C

I'm implementing RSA in C.
I'm using "unsigned long long int" (Top limit: 18446744073709551615).
The problems come when I have to calculate thing like 4294967296 ^ 2.
It should be 18446744073709551616, but I get 0 (overflow).
I mean, I need to calculate things that result is over the top limit.
I've tried using float, double, long double, but the results are incorrect.
Example:
4294967000.0 * 4294967000.0 the result is 18446741874686296064.0
but it should be 18446741531089000000
Openssl example:
#include <stdio.h>
#include <openssl/bn.h>
/* compile with -lcrypto */
int main ()
{
char p_sa[] = "4294967296";
char p_sb[] = "4294967296";
BN_CTX *c = BN_CTX_new();
BIGNUM *pa = BN_new();
BIGNUM *pb = BN_new();
BN_dec2bn(&pa, p_sa);
BN_dec2bn(&pb, p_sb);
BN_mul (pa,pb, pa,c);
char * number_str = BN_bn2hex(pa);
printf("%s\n", number_str);
OPENSSL_free(number_str);
BN_free(pa);
BN_free(pb);
BN_CTX_free(c);
return 0;
}
"implementing RSA" + "I have to calculate thing like 4294967296 ^ 2" is contradictory. To implement RSA, that calculation is not needed. The algorithm does not need wider than 64-bit integers.
I've tried using float, double, long double, but the results are incorrect.
4294967000.0 * 4294967000.0 the result is 18446741874686296064.0
Use unsigned long long math. Typical double has only 53 bits of precision, yet this calculation needs 60+ bits for a precise product.
int main(void) {
unsigned long x = 4294967000;
printf("%lu * %lu the result is %lu\n", x,x,x*x); // overflow
printf("%lu * %lu the result is %llu\n", x,x,(unsigned long long) (x*x)); // overflow
printf("%lu * %lu the result is %llu\n", x,x,(unsigned long long)x*x);// good 64-bit math
return 0;
}
Output
4294967000 * 4294967000 the result is 87616
4294967000 * 4294967000 the result is 87616
4294967000 * 4294967000 the result is 18446741531089000000
4294967296 is this:
> witch 4294967296
witch (c) 1981-2017 Alf Lacis Build: 20200823.144838
Param ________Hex_______ _______Uns.Dec______
1 0x0000000100000000 4294967296
As you can see, it is already using the bottom bit of byte 5 (01).
If you square this number, it will try to set the bottom bit of byte 9, but there are only 8 bytes in a 64-bit unsigned integer, so the answer, 0, is what the processor believes to be correct. And that's why the value of UINT64_MAX (stdint.h) is 18446744073709551615, not 18446744073709551616.
18446744073709551615 is this in hex:
>witch 18446744073709551615
witch (c) 1981-2017 Alf Lacis Build: 20200823.144838
Param ________Hex_______ _______Uns.Dec______
1 0xffffffffffffffff 18446744073709551615

pow() function weird behavior when used in brackets and with long long integer

I found something weird.
This function puts a digit in a number at the given spot and returns the modified number.
Now we want to do put_digit(123456789123456784, 0, 9);
That will put 9 at the end of the number, replacing the last number (4).
This is the code that WORKS:
long long int put_digit(long long int number, char place, char digit)
{
long long int result = number;
long long int power = number;
power = pow(10, (place));
result -= get_digit(number, place)*power;
result += digit*pow(10, (place));
return result;
}
The code returns 123456789123456789
This is the code that DOES NOT WORK:
long long int put_digit(long long int number, char place, char digit)
{
long long int result = number;
result -= get_digit(number, place)*pow(10, (place));
result += digit*pow(10, (place));
return result;
}
This code returns 123456789123456800 as the result.
The functions get_digit() returns the digit from the number in the given place.
This is it's code:
char get_digit(long long int number, char place)
{
long long int target = number;
char digit = 0;
target /= pow(10, place);
digit = target % 10;
return digit;
}
• This does not happen with lower numbers.
• get_digit() always returns the correct value (4 in this case).
• get_digit() is a char because this is not a counter function, and thus it is better to focus on using less memory rather than using a faster variable like int.
• I've tried using brackets to avoid troublesome operator precedence, but to no avail.
• A weird behavior is also observed when doing put_digit(123456789123456000, 2, 7), which for some reason returns 123456789123456704. This is solved by replacing the pow function in the second result calculation with the variable "power".
I just don't understand why this is happening.
Am I getting some kind of an overflow? Is it my system's fault or my own? Am I using pow() in a bad way?
The declaration of pow() is: double pow( double base, double exponent );
In the first case:
long long int power = number;
power = pow(10, (place));
the value returned by pow() is converted to long long int when it is assigned power. The rest of the computation is processed using integer numbers and the result is the one you expect.
On the second case:
result -= get_digit(number, place)*pow(10, (place));
the value returned by get_digit(number, place) is converted to double because it needs to be multiplied with a floating point number (returned by pow()). Also, the value of result is converted to double before subtracting the result of the multiplication. In the end, the computed value is converted from double to long long int to be stored in result.
But starting on some magnitude, the floating point numbers lose the precision of their least significant digit(s).
Try this simple piece of code to see for yourself:
long long int i = 123456789123456785;
for (; i <= 123456789123456795; i ++) {
printf("long long int: %lld; double: %f\n", i, (double)i);
}
It outputs:
long long int: 123456789123456785; double: 123456789123456784.000000
long long int: 123456789123456786; double: 123456789123456784.000000
long long int: 123456789123456787; double: 123456789123456784.000000
long long int: 123456789123456788; double: 123456789123456784.000000
long long int: 123456789123456789; double: 123456789123456784.000000
long long int: 123456789123456790; double: 123456789123456784.000000
long long int: 123456789123456791; double: 123456789123456784.000000
long long int: 123456789123456792; double: 123456789123456800.000000
long long int: 123456789123456793; double: 123456789123456800.000000
long long int: 123456789123456794; double: 123456789123456800.000000
long long int: 123456789123456795; double: 123456789123456800.000000
This behaviour is not a bug but a limitation of the floating point numbers.
The solution for your code is to convert the value returned by pow(10, place) to long long int as soon as it returns:
result -= get_digit(number, place)*(long long int)pow(10, place);

Resources