Big numbers in C - 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

Related

Error on casting unsigned int to float

For the following program.
#include <stdio.h>
int main()
{
unsigned int a = 10;
unsigned int b = 20;
unsigned int c = 30;
float d = -((a*b)*(c/3));
printf("d = %f\n", d);
return 0;
}
It is very strange that output is
d = 4294965248.000000
When I change the magic number 3 in the expression to calculate d to 3.0, I got correct result:
d = 2000.000000
If I change the type of a, b, c to int, I also got correct result.
I guess this error occurred by the conversion from unsigned int to float, but I do not know details about how the strange result was created.
I think you realize that you casting minus to unsigned int before assignment to float. If you run the below code, you will get highly likely 4294965296
#include <stdio.h>
int main()
{
unsigned int a = 10;
unsigned int b = 20;
unsigned int c = 30;
printf("%u", -((a*b)*(c/3)));
return 0;
}
The -2000 to the right of your equals sign is set up as a signed
integer (probably 32 bits in size) and will have the hexadecimal value
0xFFFFF830. The compiler generates code to move this signed integer
into your unsigned integer x which is also a 32 bit entity. The
compiler assumes you only have a positive value to the right of the
equals sign so it simply moves all 32 bits into x. x now has the
value 0xFFFFF830 which is 4294965296 if interpreted as a positive
number. But the printf format of %d says the 32 bits are to be
interpreted as a signed integer so you get -2000. If you had used
%u it would have printed as 4294965296.
#include <stdio.h>
#include <limits.h>
int main()
{
float d = 4294965296;
printf("d = %f\n\n", d);
return 0;
}
When you convert 4294965296 to float, the number you are using is long to fit into the fraction part. Now that some precision was lost. Because of the loss, you got 4294965248.000000 as I got.
The IEEE-754 floating-point standard is a standard for representing
and manipulating floating-point quantities that is followed by all
modern computer systems.
bit 31 30 23 22 0
S EEEEEEEE MMMMMMMMMMMMMMMMMMMMMMM
The bit numbers are counting from the least-significant bit. The first
bit is the sign (0 for positive, 1 for negative). The following
8 bits are the exponent in excess-127 binary notation; this
means that the binary pattern 01111111 = 127 represents an exponent
of 0, 1000000 = 128, represents 1, 01111110 = 126 represents
-1, and so forth. The mantissa fits in the remaining 24 bits, with
its leading 1 stripped off as described above. Source
As you can see, when doing conversion 4294965296 to float, precision which is 00011000 loss occurs.
11111111111111111111100 00011000 0 <-- 4294965296
11111111111111111111100 00000000 0 <-- 4294965248
This is because you use - on an unsigned int. The - inverts the bits of the number. Lets print some unsigned integers:
printf("Positive: %u\n", 2000);
printf("Negative: %u\n", -2000);
// Output:
// Positive: 2000
// Negative: 4294965296
Lets print the hex values:
printf("Positive: %x\n", 2000);
printf("Negative: %x\n", -2000);
// Output
// Positive: 7d0
// Negative: fffff830
As you can see, the bits are inverted. So the problem comes from using - on unsigned int, not from casting unsigned intto float.
As others have said, the issue is that you are trying to negate an unsigned number. Most of the solutions already given have you do some form of casting to float such that the arithmetic is done on floating point types. An alternate solution would be to cast the results of your arithmetic to int and then negate, that way the arithmetic operations will be done on integral types, which may or may not be preferable, depending on your actual use-case:
#include <stdio.h>
int main(void)
{
unsigned int a = 10;
unsigned int b = 20;
unsigned int c = 30;
float d = -(int)((a*b)*(c/3));
printf("d = %f\n", d);
return 0;
}
Your whole calculation will be done unsigned so it is the same as
float d = -(2000u);
-2000 in unsigned int (assuming 32bits int) is 4294965295
this gets written in your float d. But as float can not save this exact number it gets saved as 4294965248.
As a rule of thumb you can say that float has a precision of 7 significant base 10 digits.
What is calculated is 2^32 - 2000 and then floating point precision does the rest.
If you instead use 3.0 this changes the types in your calculation as follows
float d = -((a*b)*(c/3.0));
float d = -((unsigned*unsigned)*(unsigned/double));
float d = -((unsigned)*(double));
float d = -(double);
leaving you with the correct negative value.
you need to cast the ints to floats
float d = -((a*b)*(c/3));
to
float d = -(((float)a*(float)b)*((float)c/3.0));
-((a*b)*(c/3)); is all performed in unsigned integer arithmetic, including the unary negation. Unary negation is well-defined for an unsigned type: mathematically the result is modulo 2N where N is the number of bits in unsigned int. When you assign that large number to the float, you encounter some loss of precision; the result, due to its binary magnitude, is the nearest number to the unsigned int that divides 2048.
If you change 3 to 3.0, then c / 3.0 is a double type, and the result of a * b is therefore converted to a double before being multiplied. This double is then assigned to a float, with the precision loss already observed.

When R.H.S have negative int and unsigned int outside the range of int in arithmetic operation

I apologize for the title since I had to somehow find a unique one.
Consider the code below:
#include<stdio.h>
int main(void)
{
int b = 2147483648; // To show the maximum value of int type here is 2147483647
printf("%d\n",b);
unsigned int a = 2147483650;
unsigned int c = a+(-1);
printf("%u\n",c);
}
The output of the above program when run on a 64 bit OS with gcc compiler is:
-2147483648
2147483649
Please see my understanding of the case:
Unsigned int a is outside the range of signed int type. In the R.H.S (-1) will converted to unsigned int since the operands are of different types. The result of converting -1 to unsigned int is:
-1 + (unsigned int MAX_UINT +1) = unsigned int MAX_UINT = 4294967295.
Now R.H.S will be:
unsigned int MAX_UINT + 2147483650
Now this looks like it is outside the range of unsigned int. I do not know how to proceed from here and it looks like even if I proceed with this explanation I will not reach the empirical output.
Please give a proper explanation.
PS: To know how int b = 2147483648 became -2147483648 is not my intention. I just added that line in the code so it is pretty clear that 2147483650
is outside the range of int.
2147483648 is not a 32-bit int, it is just above INT_MAX whose value is 2147483647 on such platforms.
int b = 2147483648; is implementation defined. On your platform, it seems to perform 32-bit wrap around, which is typical of two's complement architectures but not guaranteed by the C Standard.
As a consequence printf("%d\n", b); outputs -2147483648.
The rest of the code is perfectly defined on 32-bit systems, and the output 2147483649 is correct and expected. The fact that the OS by 64 bit plays a very subtle role in the evaluation steps but is mostly irrelevant to the actual result, which is fully defined by the C Standard.
Here are steps:
unsigned int a = 2147483650; no surprise here, a is an unsigned int and its initializer is either an int, a long int or a long long int depending on which of these types has at least 32 value bits. On Windows and 32-bit linux, it would be long long int whereas on 64-bit linux it would be long int. The value is truncated to 32-bit upon storing to the unsigned int variable.
You can verify these steps by adding this code:
printf("sizeof(2147483650) -> %d\n", (int)sizeof(2147483650));
printf(" sizeof(a) -> %d\n", (int)sizeof(a));
The second definition unsigned int c = a+(-1); undergoes the same steps:
c is defined as an unsigned int and its initializer is truncated to 32 bits when stored into c. The initializer is an addition:
the first term is an unsigned int with value 2147483650U.
the second term is a parenthesized expression with the unary negation of an int with value 1. Hence it is an int with value -1 as you correctly analyzed.
the second term is converted to unsigned int: conversion is performed modulo 232, hence the value is 4294967295U.
the addition is then performed using unsigned arithmetics, which is specified as taking place modulo the width of the unsigned int type, hence the result is an unsigned int with value 2147483649U, (6442450945 modulo 232)
This unsigned int value is stored into c and prints correctly with printf("%u\n", c); as 2147483649.
If the expression had been instead 2147483650 + (-1), the computation would have taken place in 64 bits signed arithmetics, with type long int or long long int depending on the architecture, with a result of 2147483649. This value would then be truncated to 32-bits when stored into c, hence the same value for c as 2147483649.
Note that the above steps do not depend on the actual representation of negative values. They are fully defined for all architectures, only the width of type int matters.
You can verify these steps with extra code. Here is a complete instrumented program to illustrate these steps:
#include <limits.h>
#include <stdio.h>
int main(void) {
printf("\n");
printf(" sizeof(int) -> %d\n", (int)sizeof(int));
printf(" sizeof(unsigned int) -> %d\n", (int)sizeof(unsigned int));
printf(" sizeof(long int) -> %d\n", (int)sizeof(long int));
printf(" sizeof(long long int) -> %d\n", (int)sizeof(long long int));
printf("\n");
int b = 2147483647; // To show the maximum value of int type here is 2147483647
printf(" int b = 2147483647;\n");
printf(" b -> %d\n",b);
printf(" sizeof(b) -> %d\n", (int)sizeof(b));
printf(" sizeof(2147483647) -> %d\n", (int)sizeof(2147483647));
printf(" sizeof(2147483648) -> %d\n", (int)sizeof(2147483648));
printf(" sizeof(2147483648U) -> %d\n", (int)sizeof(2147483648U));
printf("\n");
unsigned int a = 2147483650;
printf(" unsigned int a = 2147483650;\n");
printf(" a -> %u\n", a);
printf(" sizeof(2147483650U) -> %d\n", (int)sizeof(2147483650U));
printf(" sizeof(2147483650) -> %d\n", (int)sizeof(2147483650));
printf("\n");
unsigned int c = a+(-1);
printf(" unsigned int c = a+(-1);\n");
printf(" c -> %u\n", c);
printf(" sizeof(c) -> %d\n", (int)sizeof(c));
printf(" a+(-1) -> %u\n", a+(-1));
printf(" sizeof(a+(-1)) -> %d\n", (int)sizeof(a+(-1)));
#if LONG_MAX == 2147483647
printf(" 2147483650+(-1) -> %lld\n", 2147483650+(-1));
#else
printf(" 2147483650+(-1) -> %ld\n", 2147483650+(-1));
#endif
printf(" sizeof(2147483650+(-1)) -> %d\n", (int)sizeof(2147483650+(-1)));
printf(" 2147483650U+(-1) -> %u\n", 2147483650U+(-1));
printf("sizeof(2147483650U+(-1)) -> %d\n", (int)sizeof(2147483650U+(-1)));
printf("\n");
return 0;
}
Output:
sizeof(int) -> 4
sizeof(unsigned int) -> 4
sizeof(long int) -> 8
sizeof(long long int) -> 8
int b = 2147483647;
b -> 2147483647
sizeof(b) -> 4
sizeof(2147483647) -> 4
sizeof(2147483648) -> 8
sizeof(2147483648U) -> 4
unsigned int a = 2147483650;
a -> 2147483650
sizeof(2147483650U) -> 4
sizeof(2147483650) -> 8
unsigned int c = a+(-1);
c -> 2147483649
sizeof(c) -> 4
a+(-1) -> 2147483649
sizeof(a+(-1)) -> 4
2147483650+(-1) -> 2147483649
sizeof(2147483650+(-1)) -> 8
2147483650U+(-1) -> 2147483649
sizeof(2147483650U+(-1)) -> 4
int b = 2147483648;
printf("%d\n",b);
// -2147483648
Conversion of an integer (any signed or unsigned) that is outside the range of the target signed type:
... either the result is implementation-defined or an implementation-defined signal is raised. C11 ยง6.3.1.3 3
In your case with the signed integer 2147483648, the implementation-defined behavior appears to map the lowest 32-bits of the source 2147483648 to your int's 32-bits. This may not be the result with another compiler.
a+(-1) is the same as a + (-(1u)) same as a + (-1u + UINT_MAX + 1u) same as a + UINT_MAX. The addition overflows the unsigned range, yet unsigned overflow wraps around. So the sum is 2147483649 before the assignment. With the below code, there is no out of range conversion. The only conversion is signed 1 to unsigned 1 and long 2147483650 (or long long 2147483650) to unsigned 2147483650. Both in range conversions.
unsigned int a = 2147483650;
unsigned int c = a+(-1);
printf("%u\n",c);
// 2147483649
Look at it like this
2147483650 0x80000002
+ -1 +0xFFFFFFFF
---------- ----------
2147483649 0x80000001
Where does the 0xFFFFFFFF come from? Well, 0 is 0x00000000, and if you subtract 1 from that you get 0xFFFFFFFF because unsigned arithmetic is well-defined to "wrap".
Or taking your decimal version further, 0 - 1 is UINT_MAX because unsigned int wraps, and so does the sum.
your value 2147483650
UINT_MAX + 4294967295
----------
6442450945
modulo 2^32 % 4294967296
----------
2147483649

long long multiplication in C gives wrong result

My C code seems to be malfunctioning and incapable of multiplying long long numbers and outputting the result, and I can't for the life of me figure out why.
Here's the culprit code:
#include "stdio.h"
int main()
{
unsigned long long primes[] = {199453LL, 200723LL, 203317LL, 205103LL, 206603LL, 208057LL, 210323LL, 210961LL, 212827LL, 214237LL, 215693LL, 216319LL};
unsigned long long numbers[6];
int i;
printf("Calculating the numbers to factor!\n");
printf("Size of long: %i\n", sizeof(long));
printf("Size of long long: %i\n", sizeof(long long));
for (i = 0; i < 6; i++)
{
numbers[i] = primes[i]*primes[11-i];
printf("%ld*%ld = %ld\n", primes[i], primes[11-i], numbers[i]);
printf("Result is %ld\n",numbers[i]);
}
return 0;
}
And here's the output when I compile this and run it (I'm using gcc version 4.8.2 on Linux)
Calculating the numbers to factor!
Size of long: 4
Size of long long: 8
199453*0 = 216319
Result is 195800547
200723*0 = 215693
Result is 344873079
203317*0 = 214237
Result is 608351169
205103*0 = 212827
Result is 701783221
206603*0 = 210961
Result is 635502523
208057*0 = 210323
Result is 809499451
Your printf format string is wrong. %ld requires a long int, %lld is for a long long int, and since you are using unsigned long long, you should use %llu, otherwise large positive values will be displayed as negative.
And, as noticed by Grzegorz Szpetkowski, the size_t (sizeof) requires %zu.
From printf(3):
l (ell) A following integer conversion corresponds to a long int or unsigned long int argument, or a following n conversion
corresponds to a pointer to a long int argument, or a following c conversion corresponds to a wint_t argument, or a following s conversion corresponds to a pointer to wchar_t argument.
ll (ell-ell). A following integer conversion corresponds to a long long int or unsigned long long int argument, or a following
n conversion corresponds to a pointer to a
long long int argument.
...
z A following integer conversion corresponds to a size_t or ssize_t argument. (Linux libc5 has Z with this meaning. Don't use it.)
Now, for example this output
199453*0 = 216319
Occurs because the little-endian 64-bit numbers 199453, 216319 and 43145473507 are correctly pushed onto the stack; but printf expects to find only 32-bit numbers on the stack, so it prints 199453, 0 (which are the top 4 bytes of 64-bit number 199453), and 216319.

Precision of double numbers with 17 or more digits

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.

Overflow Error, RMS value shown wrong

I am measuring AC Voltage and I am calculating rms value for ADC.
I have an array which is having 128 samples of my signal.
while squaring number I get an error.
unsigned long int calval=0;
unsigned int loop;
float adcbufval;
for(loop=0;loop<128;loop++)
{
printf("adcval %d = %d\t ", loop, adc_temp[loop]);
calval = (adc_temp[loop])*(adc_temp[loop]);
printf("\t %ld \n", calval);
}
output:
adcval 1 = 168 28224
adcval 2 = 32 1024
adcval 3 = -88 7744
adcval 4 = -211 44521 // sqr(211) 44521 , it fine here
adcval 5 = -314 33060 // sqr(314) 98596-65536 = 33060 instead of 98596.
adcval 6 = -416 41984
adcval 7 = -522 10340
adcval 8 = -655 35809
adcval 9 = -773 7705
adcval 10 = -889 3889
Though I defined 'calval' as unsigned long int (range 0-4,294,967,295), it get over flowed at 65536 value.
In normal C compiler its working fine.
Any suggestions?
Hardware is dsPIC30f5011 / MPLAB 8.8x
You haven't shown it (or I've overlooked it), but if adc_temp[] is an array of int, then to safely square the values you must cast at least one side of the multiplication to long before doing so. Otherwise the result of the multiplication will still be int, and it will only be converted to unsigned long for the assignment, after overflow has already occurred.
Like so:
calval = (long)(adc_temp[loop])*(adc_temp[loop]);
That cast may be unsigned long if adc_temp[] is also unsigned.
According to the datasheet the dsPIC30f5011 is a 16bit microcontroller, according to C99 you are correct unsigned long should be 2^32. However it appears that the compiler you are using treats long as an alias for int which is still compliant with C90 which simply required sizeof(short) <= sizeof(int) <= sizeof(long). You might have better luck if you really need 32bit math using unsigned long long.

Resources