Rounding 64 bit integers to 32 bit integers - c

I have a function that does rounding operation as shown below. It takes 64bit integer as input and gives 32bit integer as output. While converting, a factor of 0x40000000 is being added to the input. What is the reason behind it?
int rounder(long long int in)
{
INT64 out;
if ((in >> 32) == 0x7FFFFFFF)
out = in;
else
out = (INT64)0x40000000 + in;
out = out >> 31;
return (INT32)out;
}

Let's start with some smaller numbers, because they're easier!
Using conventional rounding, x.49999... or less should round down to x, x.50000... or more should round up to (x+1).
(There are lots of different rounding methods, but this is the one most learn at school.)
Whenever you do integer division (or conversion of a floating point value to an integer), you simply throw away the fractional part. Hence:
6/2 == 3.0 --> 3
5/2 == 2.5 --> 2
A neat 'trick' is to add half-the-divisor (1, in this case) before division. As if by magic, you get the right rounding! eg:
6/2 becomes (6+1)/2 == 7/2 == 3.5 --> 3
5/2 becomes (5+1)/2 == 6/2 == 3.0 --> 3
You can see why this works by looking at it this way:
5/2 becomes (5+1)/2 == 5/2 + 1/2
13/6 becomes (13+3)/6 == 13/6 + 3/6 == 13/6 + 1/2
You're adding half to the real answer. Anything less than x.5 will still be less than x+1 so will still round down, anything of x.5 or more will become x+1 or more so will round up.
Now to your actual question:
This idea works with all divisors; you're shifting down by 31, which is the same as dividing by 2^31. So 'half-the-divisor' is 2^30, or 0x40000000.
Beware: as others have noted, this 'trick' only works for positive numbers (you need to subtract if it's negative, but it's a can of worms).
There is a lot to consider in this topic; it's not simple to get your head around. As ever, try some easy examples for yourself and see what happens.

The input appears to be a 64-bit fixed point number with 31 fraction bits. The 0x40000000 value is added to round the number up if it has a fractional part >= 0.5. The if is used to avoid possible overflow when factoring in the rounding.

Related

How does a float get converted to scientific notation for storage?

http://www.cs.yale.edu/homes/aspnes/pinewiki/C(2f)FloatingPoint.html
I was looking into why there are sometimes rounding issues when storing a float. I read the above link, and see that floats are converted to scientific notation.
https://babbage.cs.qc.cuny.edu/IEEE-754/index.xhtml
Base is always 2. So, 8 is stored as 1 * 2^3. 9 is stored as 1.001 * 2^3.
What is the math algorithm to determine the mantissa/significand and exponent?
Here is C++ code to convert a decimal string to a binary floating-point value. Although the question is tagged C, I presume the question is more about the algorithm and calculations than the programming language.
The DecimalToFloat class is constructed with a string that contains solely decimal digits and a decimal point (a period, most one). In its constructor, it shows how to use elementary school multiplication and long division to convert the number from decimal to binary. This demonstrates the fundamental concepts using elementary arithmetic. Real implementations of decimal-to-floating-point conversion in commercial software using algorithms that are faster and more complicated. They involve prepared tables, analysis, and proofs and are the subjects of academic papers. A significant problem of quality implementations of decimal-to-binary-floating-point conversion is getting the rounding correct. The disparate nature of powers of ten to powers of two (both positive and negative powers) makes it tricky to correctly determine when some values are above or below a point where rounding changes. Normally, when we are parsing something like 123e300, we want to figure out the binary floating-point result without actually calculating 10300. That is a much more extensive subject.
The GetValue routine finishes the preparation fo the number, taking the information prepared by the constructor and rounding it to the final floating-point form.
Negative numbers and exponential (scientific) notation are not handled. Handling negative numbers is of course easy. Exponential notation could be accommodated by shifting the input—moving the decimal point right for positive exponents or left for negative exponents. Again, this is not the fastest way to perform the conversion, but it demonstrates fundamental ideas.
/* This code demonstrates conversion of decimal numerals to binary
floating-point values using the round-to-nearest-ties-to-even rule.
Infinities and subnormal values are supported and assumed.
The basic idea is to convert the decimal numeral to binary using methods
taught in elementary school. The integer digits are repeatedly divided by
two to extract a string of bits in low-to-high position-value order. Then
sub-integer digits are repeatedly multiplied by two to continue extracting
a string of bits in high-to-low position-value order. Once we have enough
bits to determine the rounding direction or the processing exhausts the
input, the final value is computed.
This code is not (and will not be) designed to be efficient. It
demonstrates the fundamental mathematics and rounding decisions.
*/
#include <algorithm>
#include <limits>
#include <cmath>
#include <cstring>
template<typename Float> class DecimalToFloat
{
private:
static_assert(std::numeric_limits<Float>::radix == 2,
"This code requires the floatng-point radix to be two.");
// Abbreviations for parameters describing the floating-point format.
static const int Digits = std::numeric_limits<Float>::digits;
static const int MaximumExponent = std::numeric_limits<Float>::max_exponent;
static const int MinimumExponent = std::numeric_limits<Float>::min_exponent;
/* For any rounding rule supported by IEEE 754 for binary floating-point,
the direction in which a floating-point result should be rounded is
completely determined by the bit in the position of the least
significant bit (LSB) of the significand and whether the value of the
trailing bits are zero, between zero and 1/2 the value of the LSB,
exactly 1/2 the LSB, or between 1/2 the LSB and 1.
In particular, for round-to-nearest, ties-to-even, the decision is:
LSB Trailing Bits Direction
0 0 Down
0 In (0, 1/2) Down
0 1/2 Down
0 In (1/2, 1) Up
1 0 Down
1 In (0, 1/2) Down
1 1/2 Up
1 In (1/2, 1) Up
To determine whether the value of the trailing bits is 0, in (0, 1/2),
1/2, or in (1/2, 1), it suffices to know the first of the trailing bits
and whether the remaining bits are zeros or not:
First Remaining Value of Trailing Bits
0 All zeros 0
0 Not all zeros In (0, 1/2)
1 All zeros 1/2
1 Not all zeros In (1/2, 1)
To capture that information, we maintain two bits in addition to the
bits in the significand. The first is called the Round bit. It is the
first bit after the position of the least significand bit in the
significand. The second is called the Sticky bit. It is set if any
trailing bit after the first is set.
The bits for the significand are kept in an array along with the Round
bit and the Sticky bit. The constants below provide array indices for
locating the LSB, the Round Bit, and the Sticky bit in that array.
*/
static const int LowBit = Digits-1; // Array index for LSB in significand.
static const int Round = Digits; // Array index for rounding bit.
static const int Sticky = Digits+1; // Array index for sticky bit.
char *Decimal; // Work space for the incoming decimal numeral.
int N; // Number of bits incorporated so far.
char Bits[Digits+2]; // Bits for significand plus two for rounding.
int Exponent; // Exponent adjustment needed.
/* PushBitHigh inserts a new bit into the high end of the bits we are
accumulating for the significand of a floating-point number.
First, the Round bit shifted down by incorporating it into the Sticky
bit, using an OR so that the Sticky bit is set iff any bit pushed below
the Round bit is set.
Then all bits from the significand are shifted down one position,
which moves the least significant bit into the Round position and
frees up the most significant bit.
Then the new bit is put into the most significant bit.
*/
void PushBitHigh(char Bit)
{
Bits[Sticky] |= Bits[Round];
std::memmove(Bits+1, Bits, Digits * sizeof *Bits);
Bits[0] = Bit;
++N; // Count the number of bits we have put in the significand.
++Exponent; // Track the absolute position of the leading bit.
}
/* PushBitLow inserts a new bit into the low end of the bits we are
accumulating for the significand of a floating-point number.
If we have no previous bits and the new bit is zero, we are just
processing leading zeros in a number less than 1. These zeros are not
significant. They tell us the magnitude of the number. We use them
only to track the exponent that records the position of the leading
significant bit. (However, exponent is only allowed to get as small as
MinimumExponent, after which we must put further bits into the
significand, forming a subnormal value.)
If the bit is significant, we record it. If we have not yet filled the
regular significand and the Round bit, the new bit is recorded in the
next space. Otherwise, the new bit is incorporated into the Sticky bit
using an OR so that the Sticky bit is set iff any bit below the Round
bit is set.
*/
void PushBitLow(char Bit)
{
if (N == 0 && Bit == 0 && MinimumExponent < Exponent)
--Exponent;
else
if (N < Sticky)
Bits[N++] = Bit;
else
Bits[Sticky] |= Bit;
}
/* Determined tells us whether the final value to be produced can be
determined without any more low bits. This is true if and only if:
we have all the bits to fill the significand, and
we have at least one more bit to help determine the rounding, and
either we know we will round down because the Round bit is 0 or we
know we will round up because the Round bit is 1 and at least one
further bit is 1 or the least significant bit is 1.
*/
bool Determined() const
{
if (Digits < N)
if (Bits[Round])
return Bits[LowBit] || Bits[Sticky];
else
return 1;
else
return 0;
}
// Get the floating-point value that was parsed from the source numeral.
Float GetValue() const
{
// Decide whether to round up or not.
bool RoundUp = Bits[Round] && (Bits[LowBit] || Bits[Sticky]);
/* Now we prepare a floating-point number that contains a significand
with the bits we received plus, if we are rounding up, one added to
the least significant bit.
*/
// Start with the adjustment to the LSB for rounding.
Float x = RoundUp;
// Add the significand bits we received.
for (int i = Digits-1; 0 <= i; --i)
x = (x + Bits[i]) / 2;
/* If we rounded up, the addition may have carried out of the
initial significand. In this case, adjust the scale.
*/
int e = Exponent;
if (1 <= x)
{
x /= 2;
++e;
}
// Apply the exponent and return the value.
return MaximumExponent < e ? INFINITY : std::scalbn(x, e);
}
public:
/* Constructor.
Note that this constructor allocates work space. It is bad form to
allocate in a constructor, but this code is just to demonstrate the
mathematics, not to provide a conversion for use in production
software.
*/
DecimalToFloat(const char *Source) : N(), Bits(), Exponent()
{
// Skip leading sources.
while (*Source == '0')
++Source;
size_t s = std::strlen(Source);
/* Count the number of integer digits (digits before the decimal
point if it is present or before the end of the string otherwise)
and calculate the number of digits after the decimal point, if any.
*/
size_t DigitsBefore = 0;
while (Source[DigitsBefore] != '.' && Source[DigitsBefore] != 0)
++DigitsBefore;
size_t DigitsAfter = Source[DigitsBefore] == '.' ? s-DigitsBefore-1 : 0;
/* Allocate space for the integer digits or the sub-integer digits,
whichever is more numerous.
*/
Decimal = new char[std::max(DigitsBefore, DigitsAfter)];
/* Copy the integer digits into our work space, converting them from
digit characters ('0' to '9') to numbers (0 to 9).
*/
for (size_t i = 0; i < DigitsBefore; ++i)
Decimal[i] = Source[i] - '0';
/* Convert the integer portion of the numeral to binary by repeatedly
dividing it by two. The remainders form a bit string representing
a binary numeral for the integer part of the number. They arrive
in order from low position value to high position value.
This conversion continues until the numeral is exhausted (High <
Low is false) or we see it is so large the result overflows
(Exponent <= MaximumExponent is false).
Note that Exponent may exceed MaximumExponent while we have only
produced 0 bits during the conversion. However, because we skipped
leading zeros above, we know there is a 1 bit coming. That,
combined with the excessive Exponent, guarantees the result will
overflow.
*/
for (char *High = Decimal, *Low = Decimal + DigitsBefore;
High < Low && Exponent <= MaximumExponent;)
{
// Divide by two.
char Remainder = 0;
for (char *p = High; p < Low; ++p)
{
/* This is elementary school division: We bring in the
remainder from the higher digit position and divide by the
divisor. The remainder is kept for the next position, and
the quotient becomes the new digit in this position.
*/
char n = *p + 10*Remainder;
Remainder = n % 2;
n /= 2;
/* As the number becomes smaller, we discard leading zeros:
If the new digit is zero and is in the highest position,
we discard it and shorten the number we are working with.
Otherwise, we record the new digit.
*/
if (n == 0 && p == High)
++High;
else
*p = n;
}
// Push remainder into high end of the bits we are accumulating.
PushBitHigh(Remainder);
}
/* Copy the sub-integer digits into our work space, converting them
from digit characters ('0' to '9') to numbers (0 to 9).
The convert the sub-integer portion of the numeral to binary by
repeatedly multiplying it by two. The carry-outs continue the bit
string. They arrive in order from high position value to low
position value.
*/
for (size_t i = 0; i < DigitsAfter; ++i)
Decimal[i] = Source[DigitsBefore + 1 + i] - '0';
for (char *High = Decimal, *Low = Decimal + DigitsAfter;
High < Low && !Determined();)
{
// Multiply by two.
char Carry = 0;
for (char *p = Low; High < p--;)
{
/* This is elementary school multiplication: We multiply
the digit by the multiplicand and add the carry. The
result is separated into a single digit (n % 10) and a
carry (n / 10).
*/
char n = *p * 2 + Carry;
Carry = n / 10;
n %= 10;
/* Here we discard trailing zeros: If the new digit is zero
and is in the lowest position, we discard it and shorten
the numeral we are working with. Otherwise, we record the
new digit.
*/
if (n == 0 && p == Low-1)
--Low;
else
*p = n;
}
// Push carry into low end of the bits we are accumulating.
PushBitLow(Carry);
}
delete [] Decimal;
}
// Conversion operator. Returns a Float converted from this object.
operator Float() const { return GetValue(); }
};
#include <iostream>
#include <cstdio>
#include <cstdlib>
static void Test(const char *Source)
{
std::cout << "Testing " << Source << ":\n";
DecimalToFloat<float> x(Source);
char *end;
float e = std::strtof(Source, &end);
float o = x;
/* Note: The C printf is used here for the %a conversion, which shows the
bits of floating-point values clearly. If your C++ implementation does
not support this, this may be replaced by any display of floating-point
values you desire, such as printing them with all the decimal digits
needed to distinguish the values.
*/
std::printf("\t%a, %a.\n", e, o);
if (e != o)
{
std::cout << "\tError, results do not match.\n";
std::exit(EXIT_FAILURE);
}
}
int main(void)
{
Test("0");
Test("1");
Test("2");
Test("3");
Test(".25");
Test(".0625");
Test(".1");
Test(".2");
Test(".3");
Test("3.14");
Test(".00000001");
Test("9841234012398123");
Test("340282346638528859811704183484516925440");
Test("340282356779733661637539395458142568447");
Test("340282356779733661637539395458142568448");
Test(".00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125");
// This should round to the minimum positive (subnormal), as it is just above mid-way.
Test(".000000000000000000000000000000000000000000000700649232162408535461864791644958065640130970938257885878534141944895541342930300743319094181060791015626");
// This should round to zero, as it is mid-way, and the even rule applies.
Test(".000000000000000000000000000000000000000000000700649232162408535461864791644958065640130970938257885878534141944895541342930300743319094181060791015625");
// This should round to zero, as it is just below mid-way.
Test(".000000000000000000000000000000000000000000000700649232162408535461864791644958065640130970938257885878534141944895541342930300743319094181060791015624");
}
One of the surprising things about a real, practical computer -- surprising to beginning programmers who have been tasked with writing artificial little binary-to-decimal conversion programs, anyway -- is how thoroughly ingrained the binary number system is in an actual computer, and how few and how diffuse any actual binary/decimal conversion routines actually are. In the C world, for example (and if we confine our attention to integers for the moment), there is basically one binary-to-decimal conversion routine, and it's buried inside printf, where the %d directive is processed. There are perhaps three decimal-to-binary converters: atof(), strtol(), and the %d conversion inside scanf. (There might be another one inside the C compiler, where it converts your decimal constants into binary, although the compiler might just call strtol() directly for those, too.)
I bring this all up for background. The question of "what's the actual algorithm for constructing floating-point numbers internally?" is a fair one, and I'd like to think I know the answer, but as I mentioned in the comments, I'm chagrined to discover that I don't, really: I can't describe a clear, crisp "algorithm". I can and will show you some code that gets the job done, but you'll probably find it unsatisfying, as if I'm cheating somehow -- because a number of the interesting details happen more or less automatically, as we'll see.
Basically, I'm going to write a version of the standard library function atof(). Here are my ground rules:
I'm going to assume that the input is a string of characters. (This isn't really an assumption at all; it's a restatement of the original problem, which is to write a version of atof.)
I'm going to assume that we can construct the floating-point number "0.0". (In IEEE 754 and most other formats, it's all-bits-0, so that's not too hard.)
I'm going to assume that we can convert the integers 0-9 to their corresponding floating-point equivalents.
I'm going to assume that we can add and multiply any floating-point numbers we want to. (This is the biggie, although I'll describe those algorithms later.) But on any modern computer, there's almost certainly a floating-point unit, that has built-in instructions for the basic floating-point operations like addition and multiplication, so this isn't an unreasonable assumption, either. (But it does end up hiding some of the interesting aspects of the algorithm, passing the buck to the hardware designer to have implemented the instructions correctly.)
I'm going to initially assume that we have access to the standard library functions atoi and pow. This is a pretty big assumption, but again, I'll describe later how we could write those from scratch if we wanted to. I'm also going to assume the existence of the character classification functions in <ctype.h>, especially isdigit().
But that's about it. With those prerequisites, it turns out we can write a fully-functional version of atof() all by ourselves. It might not be fast, and it almost certainly won't have all the right rounding behaviors out at the edges, but it will work pretty well. (I'm even going to handle negative numbers, and exponents.) Here's how it works:
skip leading whitespace
look for '-'
scan digit characters, converting each one to the corresponding digit by subtracting '0' (aka ASCII 48)
accumulate a floating-point number (with no fractional part yet) representing the integer implied by the digits -- the significand -- and this is the real math, multiplying the running accumulation by 10 and adding the next digit
if we see a decimal point, count the number of digits after it
when we're done scanning digits, see if there's an e/E and some more digits indicating an exponent
if necessary, multiply or divide our accumulated number by a power of 10, to take care of digits past the decimal, and/or the explicit exponent.
Here's the code:
#include <ctype.h>
#include <stdlib.h> /* just for atoi() */
#include <math.h> /* just for pow() */
#define TRUE 1
#define FALSE 0
double my_atof(const char *str)
{
const char *p;
double ret;
int negflag = FALSE;
int exp;
int expflag;
p = str;
while(isspace(*p))
p++;
if(*p == '-')
{
negflag = TRUE;
p++;
}
ret = 0.0; /* assumption 2 */
exp = 0;
expflag = FALSE;
while(TRUE)
{
if(*p == '.')
expflag = TRUE;
else if(isdigit(*p))
{
int idig = *p - '0'; /* assumption 1 */
double fdig = idig; /* assumption 3 */
ret = 10. * ret + fdig; /* assumption 4 */
if(expflag)
exp--;
}
else break;
p++;
}
if(*p == 'e' || *p == 'E')
exp += atoi(p+1); /* assumption 5a */
if(exp != 0)
ret *= pow(10., exp); /* assumption 5b */
if(negflag)
ret = -ret;
return ret;
}
Before we go further, I encourage you to copy-and-paste this code into a nearby C compiler, and compile it, to convince yourself that I haven't cheated too badly. Here's a little main() to invoke it with:
#include <stdio.h>
int main(int argc, char *argv[])
{
double d = my_atof(argv[1]);
printf("%s -> %g\n", argv[1], d);
}
(If you or your IDE aren't comfortable with command-line invocations, you can use fgets or scanf to read the string to hand to my_atof, instead.)
But, I know, your question was "How does 9 get converted to 1.001 * 2^3 ?", and I still haven't really answered that, have I? So let's see if we can find where that happens.
First of all, that bit pattern 10012 for 9 came from... nowhere, or everywhere, or it was there all along, or something. The character 9 came in, probably with a bit pattern of 1110012 (in ASCII). We subtracted 48 = 1100002, and out popped 10012. (Even before doing the subtraction, you can see it hiding there at the end of 111001.)
But then what turned 1001 into 1.001E3? That was basically my "assumption 3", as embodied in the line
double fdig = idig;
It's easy to write that line in C, so we don't really have to know how it's done, and the compiler probably turns it into a 'convert integer to float' instruction, so the compiler writer doesn't have to know how to do it, either.
But, if we did have to implement that ourselves, at the lowest level, we could. We know we have a single-digit (decimal) number, occupying at most 4 bits. We could stuff those bits into the significand field of our floating-point format, with a fixed exponent (perhaps -3). We might have to deal with the peculiarities of an "implicit 1" bit, and if we didn't want to inadvertently create a denormalized number, we might have to some more tinkering, but it would be straightforward enough, and relatively easy to get right, because there are only 10 cases to test. (Heck, if we found writing code to do the bit manipulations troublesome, we could even use a 10-entry lookup table.)
Since 9 is a single-digit number, we're done. But for a multiple-digit number, our next concern is the arithmetic we have to do: multiplying the running sum by 10, and adding in the next digit. How does that work, exactly?
Again, if we're writing a C (or even an assembly language) program, we don't really need to know, because our machine's floating-point 'add' and 'multiply' instructions will do everything for us. But, also again, if we had to do it ourselves, we could. (This answer's getting way too long, so I'm not going to discuss floating-point addition and multiplication algorithms just yet. Maybe farther down.)
Finally, the code as presented so far "cheated" by calling the library functions atoi and pow. I won't have any trouble convincing you that we could have implemented atoi ourselves if we wanted/had to: it's basically just the same digit-accumulation code we already wrote. And pow isn't too hard, either, because in our case we don't need to implement it in full generality: we're always raising to integer powers, so it's straightforward repeated multiplication, and we've already assumed we know how to do multiplication.
(With that said, computing a large power of 10 as part of our decimal-to-binary algorithm is problematic. As #Eric Postpischil noted in his answer, "Normally we want to figure out the binary floating-point result without actually calculating 10N." Me, since I don't know any better, I'll compute it anyway, but if I wrote my own pow() I'd use the binary exponentiation algorithm, since it's super easy to implement and quite nicely efficient.)
I said I'd discuss floating-point addition and multiplication routines. Suppose you want to add two floating-point numbers. If they happen to have the same exponent, it's easy: add the two significands (and keep the exponent the same), and that's your answer. (How do you add the significands? Well, I assume you have a way to add integers.) If the exponents are different, but relatively close to each other, you can pick the smaller one and add N to it to make it the same as the larger one, while simultaneously shifting the significand to the right by N bits. (You've just created a denormalized number.) Once the exponents are the same, you can add the significands, as before. After the addition, it may be important to renormalize the numbers, that is, to detect if one or more leading bits ended up as 0 and, if so, shift the significand left and decrement the exponent. Finally, if the exponents are too different, such that shifting one significand to the right by N bits would shift it all away, this means that one number is so much smaller than the other that all of it gets lost in the roundoff when adding them.
Multiplication: Floating-point multiplication is actually somewhat easier than addition. You don't have to worry about matching up the exponents: the final product is basically a new number whose significand is the product of the two significands, and whose exponent is the sum of the two exponents. The only trick is that the product of the two M-bit significands is nominally 2M bits, and you may not have a multiplier that can do that. If the only multiplier you have available maxes out at an M-bit product, you can take your two M-bit significands and literally split them in half by bits:
signif1 = a * 2M/2 + b
signif2 = c * 2M/2 + d
So by ordinary algebra we have
signif1 × signif2 = ac × 2M + ad × 2M/2 + bc × 2M/2 + bd
Each of those partial products ac, ad, etc. is an M-bit product. Multiplying by 2M/2 or 2M is easy, because it's just a left shift. And adding the terms up is something we already know how to do. We actually only care about the upper M bits of the product, so since we're going to throw away the rest, I imagine we could cheat and skip the bd term, since it contributes nothing (although it might end up slightly influencing a properly-rounded result).
But anyway, the details of the addition and multiplication algorithms, and the knowledge they contain about the floating-point representation we're using, end up forming the other half of the answer to the question of the decimal-to-binary "algorithm" you're looking for. If you convert, say, the number 5.703125 using the code I've shown, out will pop the binary floating-point number 1.011011012 × 22, but nowhere did we explicitly compute that significand 1.01101101 or that exponent 2 -- they both just fell out of all the digitwise multiplications and additions we did.
Finally, if you're still with me, here's a quick and easy integer-power-only pow function using binary exponentiation:
double my_pow(double a, unsigned int b)
{
double ret = 1;
double fac = a;
while(1) {
if(b & 1) ret *= fac;
b >>= 1;
if(b == 0) break;
fac *= fac;
}
return ret;
}
This is a nifty little algorithm. If we ask it to compute, say, 1021, it does not multiply 10 by itself 21 times. Instead, it repeatedly squares 10, leading to the exponential sequence 101, 102, 104, 108, or rather, 10, 100, 10000, 100000000... Then it looks at the binary representation of 21, namely 10101, and selects only the intermediate results 101, 104, and 1016 to multiply into its final return value, yielding 101+4+16, or 1021, as desired. It therefore runs in time O(log2(N)), not O(N).
And, tune in tomorrow for our next exciting episode when we'll go in the opposite direction, writing a binary-to-decimal converter which will require us to do... (ominous chord)
floating point long division!
Here's a completely different answer, that tries to focus on the "algorithm" part of the question. I'll start with the example you asked about, converting the decimal integer 9 to the binary scientific notation number 1.0012×23. The algorithm is in two parts: (1) convert the decimal integer 9 to the binary integer 10012, and (2) convert that binary integer into binary scientific notation.
Step 1. Convert a decimal integer to a binary integer. (You can skip over this part if you already know it. Also, although this part of the algorithm is going to look perfectly fine, it turns out it's not the sort of thing that's actually used anywhere on a practical binary computer.)
The algorithm is built around a number we're working on, n, and a binary number we're building up, b.
Set n initially to the number we're converting, 9.
Set b to 0.
Compute the remainder when dividing n by 2. In our example, the remainder of 9 ÷ 2 is 1.
The remainder is one bit of our binary number. Tack it on to b. In our example, b is now 1. Also, here we're going to be tacking bits on to b on the left.
Divide n by 2 (discarding the remainder). In our example, n is now 4.
If n is now 0, we're done.
Go back to step 3.
At the end of the first trip through the algorithm, n is 4 and b is 1.
The next trip through the loop will extract the bit 0 (because 4 divided by 2 is 2, remainder 0). So b goes to 01, and n goes to 2.
The next trip through the loop will extract the bit 0 (because 2 divided by 2 is 1, remainder 0). So b goes to 001, and n goes to 1.
The next trip through the loop will extract the bit 1 (because 1 divided by 2 is 0, remainder 1). So b goes to 1001, and n goes to 0.
And since n is now 0, we're done. Meanwhile, we've built up the binary number 1001 in b, as desired.
Here's that example again, in tabular form. At each step, we compute n divided by two (or in C, n/2), and the remainder when dividing n by 2, which in C is n%2. At the next step, n gets replaced by n/2, and the next bit (which is n%2) gets tacked on at the left of b.
step n b n/2 n%2
0 9 0 4 1
1 4 1 2 0
2 2 01 1 0
3 1 001 0 1
4 0 1001
Let's run through that again, for the number 25:
step n b n/2 n%2
0 25 0 12 1
1 12 1 6 0
2 6 01 3 0
3 3 001 1 1
4 1 1001 0 1
5 0 11001
You can clearly see that the n column is driven by the n/2 column, because in step 5 of the algorithm as stated we divided n by 2. (In C this would be n = n / 2, or n /= 2.) You can clearly see the binary result appearing (in right-to-left order) in the n%2 column.
So that's one way to convert decimal integers to binary. (As I mentioned, though, it's likely not the way your computer does it. Among other things, the act of tacking a bit on to the left end of b turns out to be rather unorthodox.)
Step 2. Convert a binary integer to a binary number in scientific notation.
Before we begin with this half of the algorithm, it's important to realize that scientific (or "exponential") representations are typically not unique. Returning to decimal for a moment, let's think about the number "one thousand". Most often we'll represent that as 1 × 103. But we could also represent it as 10 × 102, or 100 × 101, or even crazier representations like 10000 × 10-1, or 0.01 × 105.
So, in practice, when we're working in scientific notation, we'll usually set up an additional rule or guideline, stating that we'll try to keep the mantissa (also called the "significand") within a certain range. For base 10, usually the goal is either to keep it in the range 0 ≤ mantissa < 10, or 0 ≤ mantissa < 1. That is, we like numbers like 1 × 103 or 0.1 × 104, but we don't like numbers like 100 × 101 or 0.01 × 105.
How do we keep our representations in the range we like? What if we've got a number (perhaps the intermediate result of a calculation) that's in a form we don't like? The answer is simple, and it depends on a pattern you've probably already noticed: If you multiply the mantissa by 10, and if you simultaneously subtract 1 from the exponent, you haven't changed the value of the number. Similarly, you can divide the mantissa by 10 and increment the exponent, again without changing anything.
When we convert a scientific-notation number into the form we like, we say we're normalizing the number.
One more thing: since 100 is 1, we can preliminarily convert any integer to scientific notation by simply multiplying it by 100. That is, 9 is 9×100, and 25 is 25×100. If we do it that way we'll usually get a number that's in a form we "don't like" (that is "nonnormalized"), but now we have an idea of how to fix that.
So let's return to base 2, and the rest of this second half of our algorithm. Everything we've said so far about decimal scientific notation is also true about binary scientific notation, as long as we make the obvious changes of "10" to "2".
To convert the binary integer 10012 to binary scientific notation, we first multiply it by 20, resulting in: 10012×20. So actually we're almost done, except that this number is nonnormalized.
What's our definition of a normalized base-two scientific notation number? We haven't said, but the requirement is usually that the mantissa is between 0 and 102 (that is, between 0 and 210), or stated another way, that the high-order bit of the mantissa is always 1 (unless the whole number is 0). That is, these mantissas are normalized: 1.0012, 1.12, 1.02, 0.02. These mantissas are nonnormalized: 10.012, 0.0012.
So to normalize a number, we may need to multiply or divide the mantissa by 2, while incrementing or decrementing the exponent.
Putting this all together in step-by-step form: to convert a binary integer to a binary scientific number:
Multiply the integer by 20: set the mantissa to the number we're converting, and the exponent to 0.
If the number is normalized (if the mantissa is 0, or if its leading bit is 1), we're done.
If the mantissa has more than one bit to the left of the decimal point (really the "radix point" or "binary point"), divide the mantissa by 2, and increment the exponent by 1. Return to step 2.
(This step will never be necessary if the number we started with was an integer.) If the mantissa is nonzero but the bit to the left of the radix point is 0, multiply the mantissa by 2, and decrement the exponent by 1. Return to step 2.
Running this algorithm in tabular form for our number 9, we have:
step mantissa exponent
0 1001. 0
1 100.1 1
2 10.01 2
3 1.001 3
So, if you're still with me, that's how we can convert the decimal integer 9 to the binary scientific notation (or floating-point) number 1.0012×23.
And, with all of that said, the algorithm as stated so far only works for decimal integers. What if we wanted to convert, say, the decimal number 1.25 to the binary number 1.012×20, or 34.125 to 1.000100012×25? That's a discussion that will have to wait for another day (or for this other answer), I guess.

Error Propagation upon Summing Single-Precision (float) Values

I'm learning single precision and would like to understand the error propagation. According to this nice website, addition is a dangerous operation.
So I wrote a small C program to test how quickly the errors add up. I'm not entirely sure if this is a valid way of testing. If it is, I'm unsure how to interpret the result, see below.
#include <stdio.h>
#include <math.h>
#define TYPE float
#define NUM_IT 168600
void increment (TYPE base, const TYPE increment, const unsigned long num_iters) {
TYPE err;
unsigned long i;
const TYPE ref = base + increment * num_iters;
for (i=0; i < num_iters; i++ ) {
base += increment;
}
err = (base - ref)/ref;
printf("%lu\t%9f\t%9f\t%+1.9f\n", i, base, ref, err);
}
int
main()
{
int j;
printf("iters\tincVal\trefVal\trelErr\n");
for (j = 1; j < 20; j++ ) {
increment(1e-1, 1e-6, (unsigned long) (pow(2, (j-10))* NUM_IT));
}
return 0;
}
The result of executing
gcc -pedantic -Wall -Wextra -Werror -lm errorPropagation.c && ./a.out | tee float.dat | column -t
is
iters incVal refVal relErr
329 0.100328 0.100329 -0.000005347
658 0.100657 0.100658 -0.000010585
1317 0.101315 0.101317 -0.000021105
2634 0.102630 0.102634 -0.000041596
5268 0.105259 0.105268 -0.000081182
10537 0.110520 0.110537 -0.000154624
21075 0.121041 0.121075 -0.000282393
42150 0.142082 0.142150 -0.000480946
84300 0.184163 0.184300 -0.000741986
168600 0.268600 0.268600 +0.000000222 <-- *
337200 0.439439 0.437200 +0.005120996
674400 0.781117 0.774400 +0.008673230
1348800 1.437150 1.448800 -0.008041115
2697600 2.723466 2.797600 -0.026499098
5395200 5.296098 5.495200 -0.036231972
10790400 10.441361 10.890400 -0.041232508
21580800 25.463778 21.680799 +0.174485177
43161600 32.000000 43.261597 -0.260313928 <-- **
86323200 32.000000 86.423195 -0.629729033
If the test is valid
Why does the error change sign? If 0.1 is represented as e.g. 0.100000001, shouldn't this accumulate always to the same bias, irrespective of the number of summations?
What's special about 168600 summations (see *)? The error becomes very small. Might be a coincidence.
Which wall is being hit at incVal = 32.00 (see **, last two lines). I'm still well below the unsigned long limit.
Thanks in advance for your effort.
First, it's important to know that 0.1 can't be represented exactly, in binary it's has periodically repeating digits. The value would be 0.0001100110011.... Compare to how 1/3 and 1/7 are represented with decimal digits. It's worth repeating your test with increment 0.25, which can be represented exactly as 0.01.
I'll illustrate the errors in decimal, that's what we humans are used to. Let's work with decimal, and assume we can have 4 digits of precision. Those are the things happening here.
Division: let's calculate 1/11:
1/11 equals 0.090909..., which is probably rounded to 0.09091. This is, as expected, correct to 4 significant digits (in bold).
magnitude differences: suppose we calculate 10 + 1/11.
When adding 1/11 to 10, we have to do more rounding, since 10.09091 are 7 significant digits, and we have only four. We have to round 1/11 to two digits after the point, and the calculated sum is 10.09. That's a underestimation. Note how only one significant digit of 1/11 is retained. If you add a lot of small values together, this will limit the precision of your final result.
Now calculate 100 + 1/11. Now we round 1/11 to 0.1 and represent the sum as 100.1. Now we have a slight overestimation instead of a slight underestimation.
My guess is the pattern of sign changes in your test are the effect of systematic slight underestimation vs. overestimation depending on the magnitude of base.
What about 1000 + 1/11? Now we can't have any digits after the point, as we have 4 significant digits before the point already. 1/11 is now rounded to 0, and the sum is still 1000. That's the wall you're seeing.
Another important thing you're not seeing in your test is: what happens if the two values have a different sign. Calculate 1.234 – 1.243: both numbers have 4 significant digits. The result is -0.009. Now the result has only one correct significant digit instead of four.
An answer to a similar question here: How does floating point error propagate when doing mathematical operations in C++? . It has a few links to more information.
To answer your questions...
1 - IEEE float rounds to even mantissas. This was done specifically in order to prevent error accumulation from always biasing in one way or the other; if it always rounded down, or rounded up, your errors would be much larger.
2 - Nothing in particular is special about 168600 in and of itself. I haven't mathed it out but it's entirely likely that it ends up making a cleaner value in binary representation (i.e. a rational/non-repeating value). Look at the values in binary, not decimal, and see if that theory holds up.
3 - The limiting factor might be due to the float mantissa being 23 bits long. Once base gets to be a certain size, increment is so small in comparison to base that computing base + increment and then rounding the mantissa back down to 23 bits completely erases the change. That is, the difference between base and base + increment is rounding error.
The "wall" you are hitting has nothing to do with the increment value, if it is constant through the addition and you start at zero. It has to with the iters. 2^23 = 8 million, and you are doing 86 million additions. So once the accumulator is 2^23 bigger than the increment, you hit the wall.
Try running the code with 86323200 iterations, but an increment of 1 or 0.0000152587890625 (or any power of 2). It should have the same relative problem as an increment of 32.

Need Floating Point Precision Using Unsigned Int

I'm working with a microchip that doesn't have room for floating point precision, however. I need to account for fractional values during some equations. So far I've had good luck using the old *100 -> /100 method like so:
increment = (short int)(((value1 - value2)*100 / totalSteps));
// later in the code I loop through the number of totolSteps
// adding back the increment to arrive at the total I want at the precise time
// time I need it.
newValue = oldValue + (increment / 100);
This works great for values from 0-255 divided by a totalSteps of up to 300. After 300, the fractional values to the right of the decimal place, become important, because they add up over time of course.
I'm curious if anyone has a better way to save decimal accuracy within an integer paradigm? I tried using *1000 /1000, but that didn't work at all.
Thank you in advance.
Fractions with integers is called fixed point math.
Try Googling "fixed point".
Fixed point tips and tricks are out of the scope of SO answer...
Example: 5 tap FIR filter
// C is the filter coefficients using 2.8 fixed precision.
// 2 MSB (of 10) is for integer part and 8 LSB (of 10) is the fraction part.
// Actual fraction precision here is 1/256.
int FIR_5(int* in, // input samples
int inPrec, // sample fraction precision
int* c, // filter coefficients
int cPrec) // coefficients fraction precision
{
const int coefHalf = (cPrec > 0) ? 1 << (cPrec - 1) : 0; // value of 0.5 using cPrec
int sum = 0;
for ( int i = 0; i < 5; ++i )
{
sum += in[i] * c[i];
}
// sum's precision is X.N. where N = inPrec + cPrec;
// return to original precision (inPrec)
sum = (sum + coefHalf) >> cPrec; // adding coefHalf for rounding
return sum;
}
int main()
{
const int filterPrec = 8;
int C[5] = { 8, 16, 208, 16, 8 }; // 1.0 == 256 in 2.8 fixed point. Filter value are 8/256, 16/256, 208/256, etc.
int W[5] = { 10, 203, 40, 50, 72}; // A sampling window (example)
int res = FIR_5(W, 0, C, filterPrec);
return 0;
}
Notes:
In the above example:
the samples are integers (no fraction)
the coefs have fractions of 8 bit.
8 bit fractions mean that each change of 1 is treated as 1/256. 1 << 8 == 256.
Useful notation is Y.Xu or Y.Xs. where Y is how many bits are allocated for the integer part and X for he fraction. u/s denote signed/unsigned.
when multiplying 2 fixed point numbers, their precision (size of fraction bits) are added to each other.
Example A is 0.8u, B is 0.2U. C=A*B. C is 0.10u
when dividing, use a shift operation to lower the result precision. Amount of shifting is up to you. Before lowering precision it's better to add a half to lower the error.
Example: A=129 in 0.8u which is a little over 0.5 (129/256). We want the integer part so we right shift it by 8. Before that we want to add a half which is 128 (1<<7). So A = (A + 128) >> 8 --> 1.
Without adding a half you'll get a larger error in the final result.
Don't use this approach.
New paradigm: Do not accumulate using FP math or fixed point math. Do your accumulation and other equations with integer math. Anytime you need to get some scaled value, divide by your scale factor (100), but do the "add up" part with the raw, unscaled values.
Here's a quick attempt at a precise rational (Bresenham-esque) version of the interpolation if you truly cannot afford to directly interpolate at each step.
div_t frac_step = div(target - source, num_steps);
if(frac_step.rem < 0) {
// Annoying special case to deal with rounding towards zero.
// Alternatively check for the error term slipping to < -num_steps as well
frac_step.rem = -frac_step.rem;
--frac_step.quot;
}
unsigned int error = 0;
do {
// Add the integer term plus an accumulated fraction
error += frac_step.rem;
if(error >= num_steps) {
// Time to carry
error -= num_steps;
++source;
}
source += frac_step.quot;
} while(--num_steps);
A major drawback compared to the fixed-point solution is that the fractional term gets rounded off between iterations if you are using the function to continually walk towards a moving target at differing step lengths.
Oh, and for the record your original code does not seem to be properly accumulating the fractions when stepping, e.g. a 1/100 increment will always be truncated to 0 in the addition no matter how many times the step is taken. Instead you really want to add the increment to a higher-precision fixed-point accumulator and then divide it by 100 (or preferably right shift to divide by a power-of-two) each iteration in order to compute the integer "position".
Do take care with the different integer types and ranges required in your calculations. A multiplication by 1000 will overflow a 16-bit integer unless one term is a long. Go through you calculations and keep track of input ranges and the headroom at each step, then select your integer types to match.
Maybe you can simulate floating point behaviour by saving
it using the IEEE 754 specification
So you save mantisse, exponent, and sign as unsigned int values.
For calculation you use then bitwise addition of mantisse and exponent and so on.
Multiplication and Division you can replace by bitwise addition operations.
I think it is a lot of programming staff to emulate that but it should work.
Your choice of type is the problem: short int is likely to be 16 bits wide. That's why large multipliers don't work - you're limited to +/-32767. Use a 32 bit long int, assuming that your compiler supports it. What chip is it, by the way, and what compiler?

Question about round_up macro

#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
With the above macro, could someone please help me on understanding the "(s)-1" part, why's that?
and also macros like:
#define PAGE_ROUND_DOWN(x) (((ULONG_PTR)(x)) & (~(PAGE_SIZE-1)))
#define PAGE_ROUND_UP(x) ( (((ULONG_PTR)(x)) + PAGE_SIZE-1) & (~(PAGE_SIZE-1)) )
I know the "(~(PAGE_SIZE-1)))" part will zero out the last five bits, but other than that I'm clueless, especially the role '&' operator plays.
Thanks,
The ROUND_UP macro is relying on integer division to get the job done. It will only work if both parameters are integers. I'm assuming that N is the number to be rounded and S is the interval on which it should be rounded. That is, ROUND_UP(12, 5) should return 15, since 15 is the first interval of 5 larger than 12.
Imagine we were rounding down instead of up. In that case, the macro would simply be:
#define ROUND_DOWN(N,S) ((N / S) * S)
ROUND_DOWN(12,5) would return 10, because (12/5) in integer division is 2, and 2*5 is 10. But we're not doing ROUND_DOWN, we're doing ROUND_UP. So before we do the integer division, we want to add as much as we can without losing accuracy. If we added S, it would work in almost every case; ROUND_UP(11,5) would become (((11+5) / 5) * 5), and since 16/5 in integer division is 3, we'd get 15.
The problem comes when we pass a number that's already rounded to the multiple specified. ROUND_UP(10, 5) would return 15, and that's wrong. So instead of adding S, we add S-1. This guarantees that we'll never push something up to the next "bucket" unnecessarily.
The PAGE_ macros have to do with binary math. We'll pretend we're dealing with 8-bit values for simplicity's sake. Let's assume that PAGE_SIZE is 0b00100000. PAGE_SIZE-1 is thus 0b00011111. ~(PAGE_SIZE-1) is then 0b11100000.
A binary & will line up two binary numbers and leave a 1 anywhere that both numbers had a 1. Thus, if x was 0b01100111, the operation would go like this:
0b01100111 (x)
& 0b11100000 (~(PAGE_SIZE-1))
------------
0b01100000
You'll note that the operation really only zeroed-out the last 5 bits. That's all. But that was exactly that operation needed to round down to the nearest interval of PAGE_SIZE. Note that this only worked because PAGE_SIZE was exactly a power of 2. It's a bit like saying that for any arbitrary decimal number, you can round down to the nearest 100 simply by zeroing-out the last two digits. It works perfectly, and is really easy to do, but wouldn't work at all if you were trying to round to the nearest multiple of 76.
PAGE_ROUND_UP does the same thing, but it adds as much as it can to the page before cutting it off. It's kinda like how I can round up to the nearest multiple of 100 by adding 99 to any number and then zeroing-out the last two digits. (We add PAGE_SIZE-1 for the same reason we added S-1 above.)
Good luck with your virtual memory!
Using integer arithmetic, dividing always rounds down. To fix that, you add the largest possible number that won't affect the result if the original number was evenly divisible. For the number S, that largest possible number is S-1.
Rounding to a power of 2 is special, because you can do it with bit operations. A multiple of 2 will aways have a zero in the bottom bit, a multiple of 4 will always have zero in the bottom two bits, etc. The binary representation of a power of 2 is a single bit followed by a bunch of zeros; subtracting 1 will clear that bit, and set all the bits to the right. Inverting that value creates a bit mask with zeros in the places that need to be cleared. The & operator will clear those bits in your value, thus rounding the value down. The same trick of adding (PAGE_SIZE-1) to the original value causes it to round up instead of down.
The page rounding macros assume that `PAGE_SIZE is a power of two, such as:
0x0400 -- 1 KiB
0x0800 -- 2 KiB`
0x1000 -- 4 KiB
The value of PAGE_SIZE - 1, therefore, is all one bits:
0x03FF
0x07FF
0x0FFF
Therefore, if integers were 16 bits (instead of 32 or 64 - it saves me some typing), then the value of ~(PAGE_SIZE-1) is:
0xFC00
0xFE00
0xF000
When you take the value of x (assuming, implausibly for real life, but sufficient for the purposes of exposition, that ULONG_PTR is an unsigned 16-bit integer) is 0xBFAB, then
PAGE_SIZE PAGE_ROUND_DN(0xBFAB) PAGE_ROUND_UP(0xBFAB)
0x0400 --> 0xBC00 0xC000
0x0800 --> 0xB800 0xC000
0x1000 --> 0xB000 0xC000
The macros round down and up to the nearest multiple of a page size. The last five bits would only be zeroed out if PAGE_SIZE == 0x20 (or 32).
Based on the current draft standard (C99) this macro is not entirely correct however, note that for negative values of N the result will almost certainly be incorrect.
The formula:
#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S))
Makes use of the fact that integer division rounds down for non-negative integers and uses the S - 1 part to force it to round up instead.
However, integer division rounds towards zero (C99, Section 6.5.5. Multiplicative operators, item 6). For negative N, the correct way to 'round up' is: 'N / S', nothing more, nothing less.
It gets even more involved if S is also allowed to be a negative value, but let's not even go there... (see: How can I ensure that a division of integers is always rounded up? for a more detailed discussion of various wrong and one or two right solutions)
The & makes it so.. well ok, lets take some binary numbers.
(with 1000 being page size)
PAGE_ROUND_UP(01101b)=
01101b+1000b-1b & ~(1000b-1b) =
01101b+111b & ~(111b) =
01101b+111b & ...11000b = (the ... means 1's continuing for size of ULONG)
10100b & 11000b=
10000b
So, as you can see(hopefully) This rounds up by adding PAGE_SIZE to x and then ANDing so it cancels out the bottom bits of PAGE_SIZE that are not set
This is what I use:
#define SIGN(x) ((x)<0?-1:1)
#define ROUND(num, place) ((int)(((float)(num) / (float)(place)) + (SIGN(num)*0.5)) * (place))
float A=456.456789
B=ROUND(A, 50.0f) // 450.0
C=ROUND(A, 0.001) // 456.457

I need a fast 96-bit on 64-bit specific division algorithm for a fixed-point math library

I am currently writing a fast 32.32 fixed-point math library. I succeeded at making adding, subtraction and multiplication work correctly, but I am quite stuck at division.
A little reminder for those who can't remember: a 32.32 fixed-point number is a number having 32 bits of integer part and 32 bits of fractional part.
The best algorithm I came up with needs 96-bit integer division, which is something compilers usually don't have built-ins for.
Anyway, here it goes:
G = 2^32
notation: x is the 64-bit fixed-point number, x1 is its low nibble and x2 is its high
G*(a/b) = ((a1 + a2*G) / (b1 + b2*G))*G // Decompose this
G*(a/b) = (a1*G) / (b1*G + b2) + (a2*G*G) / (b1*G + b2)
As you can see, the (a2*G*G) is guaranteed to be larger than the regular 64-bit integer. If uint128_t's were actually supported by my compiler, I would simply do the following:
((uint128_t)x << 32) / y)
Well they aren't and I need a solution. Thank you for your help.
You can decompose a larger division into multiple chunks that do division with less bits. As another poster already mentioned the algorithm can be found in TAOCP from Knuth.
However, no need to buy the book!
There is a code on the hackers delight website that implements the algorithm in C. It's written to do 64-bit unsigned divisions using 32-bit arithmetic only, so you can't directly cut'n'paste the code. To get from 64 to 128-bit you have to widen all types, masks and constans by two e.g. a short becomes a int, a 0xffff becomes 0xffffffffll ect.
After this easy easy change you should be able to do 128bit divisions.
The code is mirrored on GitHub, but was originally posted on Hackersdelight.org (original link no longer accessible).
Since your largest values only need 96-bit, One of the 64-bit divisions will always return zero, so you can even simplify the code a bit.
Oh - and before I forget this: The code only works with unsigned values. To convert from signed to unsigned divide you can do something like this (pseudo-code style):
fixpoint Divide (fixpoint a, fixpoint b)
{
// check if the integers are of different sign:
fixpoint sign_difference = a ^ b;
// do unsigned division:
fixpoint x = unsigned_divide (abs(a), abs(b));
// if the signs have been different: negate the result.
if (sign_difference < 0)
{
x = -x;
}
return x;
}
The website itself is worth checking out as well: http://www.hackersdelight.org/
By the way - nice task that you're working on.. Do you mind telling us for what you need the fixed-point library?
By the way - the ordinary shift and subtract algorithm for division would work as well.
If you target x86 you can implement it using MMX or SSE intrinsics. The algorithm relies only on primitive operations, so it could perform quite fast as well.
Better self-adjusting answer:
Forgive the C#-ism of the answer, but the following should work in all cases. There is likely a solution possible that finds the right shifts to use quicker, but I'd have to think much deeper than I can right now. This should be reasonably efficient though:
int upshift = 32;
ulong mask = 0xFFFFFFFF00000000;
ulong mod = x % y;
while ((mod & mask) != 0)
{
// Current upshift of the remainder would overflow... so adjust
y >>= 1;
mask <<= 1;
upshift--;
mod = x % y;
}
ulong div = ((x / y) << upshift) + (mod << upshift) / y;
Simple but unsafe answer:
This calculation can cause an overflow in the upshift of the x % y remainder if this remainder has any bits set in the high 32 bits, causing an incorrect answer.
((x / y) << 32) + ((x % y) << 32) / y
The first part uses integer division and gives you the high bits of the answer (shift them back up).
The second part calculates the low bits from the remainder of the high-bit division (the bit that could not be divided any further), shifted up and then divided.
I like Nils' answer, which is probably the best. It's just long division, like we all learned in grade school, except the digits are base 2^32 instead of base 10.
However, you might also consider using Newton's approximation method for division:
x := x (N + N - N * D * x)
where N is the numerator and D is the demoninator.
This just uses multiplies and adds, which you already have, and it converges very quickly to about 1 ULP of precision. On the other hand, you won't be able to acheive the exact 0.5-ULP answer in all cases.
In any case, the tricky bit is detecting and handling the overflows.
Quick -n- dirty.
Do the A/B divide with double precision floating point.
This gives you C~=A/B. It's only approximate because of floating point precision and 53 bits of mantissa.
Round off C to a representable number in your fixed point system.
Now compute (again with your fixed point) D=A-C*B. This should have significantly lower magnitude than A.
Repeat , now computing D/B with floating point. Again, round the answer to an integer. Add each division result together as you go. You can stop when your remainder is so small that your floating point divide returns 0 after rounding.
You're still not done. Now you're very close to the answer, but the divisions weren't exact.
To finalize, you'll have to do a binary search. Using the (very good) starting estimate, see if increasing it improves the error.. you basically want to bracket the proper answer and keep dividing the range in half with new tests.
Yes, you could do Newton iteration here, but binary search will likely be easier since you need only simple multiplies and adds using your existing 32.32 precision toolkit.
This is not the most efficient method, but it's by far the easiest to code.

Resources