I made a C++ program which takes a list of triplets with S.No. from a text file, where the triplet, i.e. a,b,c correspond to a+b=c (addition done using float data type). Now I convert a and b from hex to 32-bit binary numbers and extract sign, mantissa and fractional part and then add them. Finally I convert the sum back to hexadecimal representation and compare to c.
Explanation:
[Note: '0b': binary '0x': hex]
Take the test case 4 be954bb1 c2a2c2e1 c2a3582d
.
- Now
a = 0xbe954bb1 = 0b10111110100101010100101110110001
. - And
b = 0xc2a2c2e1 = 0b11000010101000101100001011100001
. - In IEEE754 first bit is sign which is 1 for both hence both are negative. next eight bits are for exponent i.e.
0b01111101
for a and0b10000101
which correspond to125
and133
in decimal. These exponents have a offset of 127 so actual exponents are125-127=-2
and133-127=6
- Rest bits are mantissa and the actual floating point number is
1.mantissa x 2^exponent
where1.mantissa
is in binary. So our numbers are1.00101010100101110110001 x 2^-2
and1.01000101100001011100001 x 2^6
- For adding we make the exponent same (the larger one, i.e. 6), hence we have
1.00101010100101110110001 x 2^-2 + 1.01000101100001011100001 x 2^6 = 0.0000000100101010100101110110001 x 2^6 + 1.01000101100001011100001 x 2^6 = ...
Code:
- Now I extracted the sign, magnitude and mantissa. Note that the above mantissa will correspond to
0b00101010100101110110001
and0b01000101100001011100001
and that is1395633
and2278113
in decimal. - We are working with integers only so we will not multiply both by same factor, i.e.
2^-23
to convert to.mantissa
rather we add2^23
to both to get9784241
and10666721
(which is0b1mantissa
). - Just forget the actual exponents and divide the mantissa of smaller exponent by
2^difference of exponents
. Hence we get9784241/(2^8)=38220
(round to nearest even) and10666721
- We can now add them to get
10704941 = 0b101000110101100000101101
. Now we have exactly 24 digits in binary, we just remove the2^23
to get2316333 = 0b01000110101100000101101
(extra '0' at start to make 23 digits). - Now actual expoenents didn't matter so we take exponent of sum as
133
(the higher one, since we shifted the lower one's mantissa) - The sign will be negative hence sign bit is
1
- The sum can now be represented as
0b11000010101000110101100000101101=0xc2a3582d
which is the sum. I hope you can understand the rest of it, which contains minor other facts about IEEE754-single precision 32 bit floating point decimal.
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <algorithm>
using namespace std;
#define SIGN 0
#define EXP 1
#define FRAC 2
#define MANTISSA_MAX ((2<<22)-1)
#define HIDDEN_MANTISSA (2 << 22)
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a>b)?b:a)
#define zero(x) (x[EXP] == 0 && x[FRAC] == 0)
#define denorm(x) (x[EXP] == 0 && x[FRAC] != 0)
#define sign(x) ((x[SIGN]==1)?-1:1)
#define hideMantissaBit(x) (x=x&MANTISSA_MAX);
#define convertToSignificand(x) x[FRAC]=((!denorm(x))?(x[FRAC]+HIDDEN_MANTISSA):(x[FRAC]));x[EXP]+=(denorm(x))?1:0
#define shift(x,y) x[FRAC] = shiftAndRound(x[FRAC],max(y[EXP] - x[EXP], 0));
#define renormalize(x,d) x[FRAC]=(d>0)?(shiftAndRound(x[FRAC],d)):(x[FRAC]<<(-d));
int msb_length(long l) {
int cnt = 0;
while (l >= 1) {
l >>= 1;
cnt++;
}
return cnt;
}
long shiftAndRound(long x, int d) {
d = min(d, msb_length(x));
int result = x >> d;
if (d == 0)
return result;
int r = (x & ((d > 1) ? (2 << (d - 2)) : 1)) >> (d - 1);
if (r == 1) {
int lb = result & 1;
int s = 0;
for (int i = 0; i < d - 1 && s == 0; i++) {
s |= x & 1;
x >>= 1;
}
if (s == 1 || lb == 1)
result += 1;
}
return result;
}
long* get(long x) {
long* arr;
arr = new long[3];
arr[SIGN] = (x & 0x80000000) >> 31;
arr[EXP] = (x & 0x7F800000) >> 23;
arr[FRAC] = (x & 0x7FFFFF);
return arr;
}
long add(long x, long y) {
long* a = get(x);
long* b = get(y);
if (zero(a))
return y;
if (zero(b))
return x;
convertToSignificand(a);
convertToSignificand(b);
if (a[EXP] != b[EXP]) {
shift(a, b)
shift(b, a)
}
long* sum;
sum = new long[3];
sum[FRAC] = sign(a) * a[FRAC] + sign(b) * b[FRAC];
sum[SIGN] = ((sum[FRAC] >= 0) ? 0 : 1);
sum[FRAC] = abs(sum[FRAC]);
if (sum[FRAC] != 0) {
sum[EXP] = max(a[EXP], b[EXP]);
int deltaExp = msb_length(sum[FRAC]) - 24;
sum[EXP] += deltaExp;
renormalize(sum, deltaExp)
if (!denorm(sum))
hideMantissaBit(sum[FRAC])
} else {
sum[EXP] = 0;
}
return (sum[SIGN] << 31) + (sum[EXP] << 23) + sum[FRAC];
}
int main() {
ifstream inputStream;
inputStream.open("src/input.txt");
int r = 0, w = 0;
long i = 0;
while (!inputStream.eof()) {
long a;
long b;
long c;
inputStream >> dec >> i;
inputStream >> hex >> a;
inputStream >> hex >> b;
inputStream >> hex >> c;
if (c == add(a, b)) {
r++;
cout << "Test " << i << " PASSED" << endl;
} else {
w++;
cout << "Test " << i << " FAILED" << endl;
}
}
inputStream.close();
cout << "Total " << r << " " << "PASSED " << w << " FAILED." << endl;
}
Please provide your views on my implementation. If you need any clarification, please ask me. For more details see IEEE754 Converter and Adding Floating Point Numbers.
Sample input:
0 c005de11 42f4b8e4 42f089f3
1 c2bd4228 3fb12f62 c2ba7d6a
2 3d8c556d 3fede65f 3ff6abb6
3 c0e9454c 3ee3960f c0db0beb
4 be954bb1 c2a2c2e1 c2a3582d
5 c3d0f1de c288a91c c3f31c25
6 bf6318c3 bef260b1 bfae248e
7 3f425736 bff456f0 bf932b55
8 bf367049 c0d76ab9 c0ee38c2
9 c29910c4 c0978ddf c2a289a2
10 c09f1c01 c2678d1a c27b709a
11 407d8462 c1874c4d c14f3782
12 c1b32297 c1910602 c222144c
13 409d6975 407a73f4 410d51b8
14 43365262 bf5fa00e 433572c2
15 c1481a5d 4331b215 4325306f
16 c39bd96b c36cec1e c40927bd
17 3efe0c78 c1e306f0 c1df0ebe
18 c2996785 3df31cb1 c2992abe
19 be6877d9 bdf46d42 beb1573d
20 40bb7d87 4054ab2d 4112e98f
21 be439c4e c29a36fb c29a98c9
22 41a4b54f 40921821 41c93b57
23 bfeb3480 bf6c8ae3 c030bcf9
24 c2e65c67 bff8fc3c c2ea4058
25 41e6caec 410dd1eb 4216d9f1
26 3e1132e1 c163979c c16152d0
27 bfc2c685 c00db62a c06f196c
28 bfb1b31f c20c043a c21191d3
29 c14aa2c2 4054b2a8 c1157618
30 c3c2ca3a bead7855 c3c2f598
31 bea8ba44 c2545034 c255a1a9
32 c27a466e 403312fc c26f153e
33 c2b5fa29 c027de73 c2bb391d
34 3e3b1571 3f8f0192 3fa66440
35 41d7e271 43ba9c1a 43c81a41
36 c1fee0c1 c109e53b c221e9af
37 c17067b9 3fff215b c150838e
38 42ac2fc8 c246d968 42118628
39 bf2b141e 40eccb24 40d768a0
...
*(double*)&c = *(double*)&a + *(double*)&b;
instead of all this stuff. Your test harness would also be more compelling if it compared the output of your bit-twiddling code to the naive floating-point implementation, instead of requiring the input file to contain the answer in hex. \$\endgroup\$static inline
functions instead. \$\endgroup\$float
and IEEE754 64-bit double precision fordouble
- implementations that do not use these representations are very rare, even among implementations that do not fully conform to the standard for operations. And*(float*)&x
isn't a cast to a float, it is a "type-pun" via a pointer cast (generally in C++ you would usereinterpret_cast
). You could also convert it to float or double in a portable way withldexp[f]
. \$\endgroup\$