For Float2Text:
A floating point number is more accurately described as "base 2 floating point". E.g. the number 1.25 in binary is represented as 1.01b or 101b >> 2.
Your initial goal should be to convert "base 2 floating point" into "base 10 floating point". E.g. for "base 10 floating point" the number 1.25 will be the digits (characters?) 125 with the exponent of -2, like "125 * 10^(-2)". Once you have the number in "base 10 floating point" it becomes relatively easy to print (e.g. just print the digits while inserting the decimal point in the right place).
Start by splitting the "base 2 floating point" number into signed integer "base 2 mantissa" (while taking care of the "implied leading 1" if it's not a sub-normal) and a signed integer "base 2 exponent" (while taking care of the bias).
Next; if the "base 2 exponent" is negative, multiply the original number by 10 while decreasing the "base 10 exponent" to keep track. Note that you can multiply by 10 by multiplying "base 2 mantissa" by 5 and increasing "base 2 exponent". In the same way you can multiply by 100 by multiplying "base 2 mantissa" by 25 and adding 2 to "base 2 exponent"; or multiply by 1000 by multiplying "base 2 mantissa" by 125 and adding 3 to "base 2 exponent", etc. You can speed this up a lot by using the existing "base 2 exponent" to select the right multiplier; e.g. maybe something vaguely like:
while(base2_exponent < 0) {
switch(base2_exponent) {
case -1:
base2_mantissa *= 5; base2_exponent -= 1;
base10_exponent += 1;
break;
case -2:
base2_mantissa *= 25; base2_exponent -= 2;
base10_exponent += 2;
break;
case -3:
base2_mantissa *= 125; base2_exponent -= 3;
base10_exponent += 3;
break;
default:
base2_mantissa *= 625; base2_exponent -= 4;
base10_exponent += 4;
break;
}
}
This will cause a minor problem - for worst case (very small numbers); the "base 2 mantissa" can grow to become about 160 bits. To deal with this you have to use "big integers" (and you can't just do base2_mantissa *= 625;
with a normal integer and will need something more like big_integer_multiply(&base2_mantissa, 625);
).
Next; if the "base 2 exponent" is positive; multiply "base 2 mantissa" by 2 and decrease "base 2 exponent" (or in other words, shift "base 2 mantissa" left by the "base 2 exponent"). This has a similar problem - for worst case (very large numbers) the "base 2 mantissa" can grow to become about 160 bits.
At this point, "base 2 exponent" will be zero and can be ignored.
The next step is converting "base 2 mantissa" into a "base 10 mantissa". In theory this shouldn't be that hard - maybe something vaguely like while(base2_mantissa > 0) { *dest = base2_mantissa % 10 + '0'; base2_mantissa /= 10; dest--; }
except that you'll be using "big integer" (with 160 bit integers) for modulo and division. However; it's better to work from most significant digit to least significant digit. To do this, have a lookup table of powers of 10 (e.g. 1, 10, 100, 1000, 10000, ....), find the largest divisor that's not larger than the mantissa, then do something vaguely like while(base2_mantissa > 0) { divisor = find_largest_divisor_from_table(base2_mantissa); *dest = base2_mantissa / divisor + '0'; base2_mantissa %= divisor; dest++; }
. This allows you to quit early - e.g. if you only need a maximum of the 10 significant digits then you can use a fixed size buffer of 10 char
and stop when you have 10 digits. Of course all of this (the table of divisors, etc) will need to be using "big integer" too.
The final step is printing "base 10 mantissa" while making sure you insert the decimal point character ('.') where "base 10 exponent" says it should be. If you used "most significant digit to least significant digit" in the previous step then this can be combined with the previous step (e.g. print one digit at a time without storing "base 10 mastissa" at all, while inserting the decimal point in the right place).
For other floating point operations:
Negation is trivial (an XOR of the sign bit).
For everything else, do it in 3 phases - split the source values into "signed integer mantissa" and "signed integer exponent" (mostly as described above); do a "middle phase" that depends on what the operation is; then combine the resulting "signed integer mantissa" and "signed integer exponent" back into the right format.
As a general guide (without complications - overflow, underflow, rounding, NaNs, ...) the "middle phases" are:
for multiplication: result_mantissa = src1_mantissa * src2_mantissa; result_exponent = src1_exponent + src2_exponent - mantissa_size;
for division: result_mantissa = (src1_mantissa << mantissa_size) / src2_mantissa; result_exponent = src1_exponent - src2_exponent;
for addition: find the value with the most positive exponent; shift the other value's mantissa right while increasing its exponent until the exponents match; result_mantissa = src1_mantissa + src2_mantissa; result_exponent = src1_exponent;
for subtraction: negate the second value and use addition instead
for shift left: if the mantissa is too small (sub-normal) shift it left as much as you can (while making sure it doesn't become too large); then add the remaining shift count to the exponent.
for shift right: subtract the shift count from the exponent as much as you can while keeping the exponent from becoming "too negative". If there's any shift count left then shift the mantissa right (forming a sub-normal).
Note that combining the resulting "signed integer mantissa" and "signed integer exponent" back into the right format should include some kind of while(result_mantissa's magnitude is too big) { result_mantissa >> 1; result_exponent++); }
; which ends up being a little messy (can/should take into account rounding mode).