Showing posts with label Programming. Show all posts
Showing posts with label Programming. Show all posts

Tuesday, January 20, 2015

C Function with Variable Number of Arguments

One usually need to implement a function where the number of arguments (parameters) is not known, or is not constant, when the function is written. Just like the printf function. The following example shows that in terms of code:

int f(int, ... ) {
        .
        .
        .
}

int g() {
        f(1,2,3);
        f(1,2);
}

Let's consider a simple example, we need to implement a function GetSum that calculates the summation of any number of integers.
So, such function takes variable number of arguments.
The first one is Size: the number of integers to calculate the summation for.
Then it takes each integer of the numbers to be added as a separate parameter.

You can call it to calculate the summation of two numbers (4+30), like :
GetSum(2, 4, 30); /* returns 34 */

Or to calculate the summation of 5 numbers (7+10+1+15+3), like :
GetSum(5, 7, 10, 1, 15, 3); /* returns 36 */

The implementation of GetSum is as follows. The comments describe everything:

#include <stdarg.h>
int GetSum(int Size...)
{
    int Sum = 0.0;
    
    // 1- Create Variable List
    va_list variable_list;

    // 2- Initialize variable_list to retrieve data
    //      starting from the address of the parameter Size
    va_start(variable_list, Size);

    // 3- Access all the arguments assigned to variable_list
    for (int Index = 0; Index < Size; Index++{
       Sum += va_arg(variable_list, int);
    }

    // 4- Clean memory reserved for variable_list
    va_end(variable_list);

    return Sum;
}

Wednesday, December 3, 2014

Branch Prediction Penalty

Consider the following piece of code that calculates the checksum of a selection from a large array of data:

UCHAR u8Data[40000];
for (USHORT i = 0; i < 40000; i++)
{  
  u8Data[i] = std::rand() % 256;
} 
UCHAR  u8Checksum = (UCHAR)0;
for (USHORT j = 0; j < 40000; j++){  
  if (u8Data[j] >= 128)
  {    
    u8Checksum += u8Data[j];
  }
}

You can consider the if condition “if (u8Data[j] >= 128)” as a railroad junction just as in the following picture:


Suppose that the microprocessor is the operator of that junction and it hears a train coming and have no idea which way it will go. It will stop the train to ask the captain which direction he wants, and then sets the switch appropriately. Then it will starts up the heavy train that have a lot of momentum once again, which will consume a lot of time. This is why microprocessors don’t work in such way.

A more intelligent operator of that junction would ‘guess’ which direction the train will go. And if he suggested right, the train continues on. If he suggested wrong, he will stop the train and reallocate the junction. On the average, this technique will save him half of the time he was wasting without ‘guessing’. This is why modern microprocessors use what is called Branch Prediction techniques.

Modern microprocessors are complicated and have long pipelines. So they take a long time to "warm up" and "slow down". That if condition, at the processor level, is a branch instruction like the following:

Photo by [1]

When the microprocessor sees that branch instruction, it has no idea which way it will go until it evaluates the branch condition. Evaluating the condition of the branch is not decided in the moment of executing the branch instruction itself because of using pipelining. The microprocessor can act like the non-intelligent railroad junction operator and halts execution and waits until the previous instructions are completed. Then he continues down the correct path.
But is there a better way? Yes, there is, the microprocessor guesses which direction the branch will go before evaluating the branch condition.
  • If it guessed right, it continues executing, and no time is wasted.
  • If it guessed wrong, it needs to flush the pipeline and roll back to the branch. Then it can restart down the other path.
Going back to our original piece of code, you may be surprised to know that you can speed its execution by around 6 times just by adding the following line of code before the second for loop:

    std::sort(u8Data, u8Data + 40000);

With a sorted array, the condition u8Data[j] >= 128 is first false for a streak of values, then becomes true for all later values. That's easy to predict for the microprocessor. With an unsorted array, you pay all the branching costs.


References: 
1- http://stackoverflow.com/questions/11227809
2- http://en.wikipedia.org/wiki/Branch_predication

Thursday, November 13, 2014

0.1 Float > 0.1 Double ?

  • Introduction:


You should not worry about using all comparison operators with floating-point numbers (float, double, and decimal). The ==, <, >, <=, >=, and != operators work just fine with these numbers. But, it is important to remember that they are floating-point numbers, rather than real numbers or rational numbers or any other such thing.

In pure (real) math, every decimal has an equivalent binary. In floating-point math, this is just not true! Consider the following example, let:
double d = 0.1;
float f = 0.1;
, should the expression f > d return true or false? Let’s analyze the answer to this question during the remaining part of this article.
  • 0.1 in Binary:

Many new programmers become aware of binary floating-point after seeing their programs give odd results:
“Why does my program print 0.10000000000000001 when I enter 0.1?”
“Why does 0.3 + 0.6 = 0.89999999999999991?”
“Why does 6 * 0.1 not equal 0.6?”
The answer is that most decimals have infinite representations in binary. Take 0.1 for example. It’s one of the simplest decimals you can think of, and yet it looks so complicated in binary:

Decimal 0.1 In Binary ( To 1369 Places) - Photo by [2]
The bits go on forever; no matter how many of those bits you store in a computer, you will never end up with the binary equivalent of decimal 0.1.
0.1 is one-tenth, or 1/10. To show it in binary, divide binary 1 by binary 1010, using binary long division:
Computing One-Tenth In Binary - Photo by [2]

The division process would repeat forever because 100 re-appear as the working portion of the dividend. Recognizing this, we can abort the division and write the answer in repeating bicimal notation, as 0.00011.
When working with floating-point numbers, it is important to remember that they are floating-point numbers, rather than real numbers or rational numbers or any other such thing. You have to take into account their properties and not the properties everyone wants them to have. Do this and you automatically avoid most of the commonly-cited "pitfalls" of working with floating-point numbers.
  • Floating Binary Point Types :

Float and Double are floating binary point types. In other words, they represent a number like this: 10001.10010110011.
Decimal is a floating decimal point type. In other words, they represent a number like this: 12345.65789.

Precision is the main difference: Float is 7 digits (32 bit), Double is 15:16 digits (64 bit), and Decimal is 28:29 significant digits (128 bit).
Decimals have much higher precision and are usually used within financial applications that require a high degree of accuracy. Decimals are much slower (up to 20X times in some tests [4]) than a double/float. Decimals versus Floats/Doubles cannot be compared without a cast whereas Floats versus Doubles can.
  • Question Answer :

As 0.1 cannot be perfectly represented in binary, while double has 15 to 16 decimal digits of precision, and float has only 7. So, they both are less than 0.1.
I'd say the answer depends on the rounding mode when converting the double to float. float has 24 binary bits of precision, and double has 53.
In binary, 0.1 is:
0.1₁₀ = 0.0001100110011001100110011001100110011001100110011…₂
            ^        ^         ^   ^
            1       10        20  24

So if we round up at the 24th digit, we'll get:
0.1₁₀ ~ 0.000110011001100110011001101
            ^        ^         ^   ^
            1       10        20  24

, which is greater than both of the exact value and the more precise approximation at 53 digits.
So, yes 0.1 float is greater than 0.1 double. This expression returns true! 
  • Examples :

It’s important to note that some decimals with terminating bicimals don’t exist in floating-point either. This happens when there are more bits than the precision allows for. For example,
0.500000000000000166533453693773481063544750213623046875
converts to :
0.100000000000000000000000000000000000000000000000000011
, but that’s 54 bits. Rounded to 53 bits it becomes :
0.1000000000000000000000000000000000000000000000000001
, which in decimal is :
0.5000000000000002220446049250313080847263336181640625.
Such precisely specified numbers are not likely to be used in real programs, so this is not an issue that’s likely to come up.

Interesting fact: 1/3 is a repeating decimal = 0.333333333333333333333……....
But in Ternary (The base-3 numeral system) it’s only 0.1 !

____________________________________________________________________

References: