Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

Tuesday, February 2, 2010

Truncate in C# and C++

The Problem:  A colleague and I were working on a problem in .NET when trying to truncate a number.  The Math.Truncate method would cut off any digits beyond the decimal point without having the ability to specify the precision.

We needed a function that would truncate at various precision levels.

The Solution:  Our solution was to first multiply the number to truncate by the proper tens, hundreds, or thousands of the precision, use Math.Truncate and finally divide by the same to get our decimal position.

C# Code:
public static decimal Truncate(decimal num, int pos)
{
    int64 lDecimalPosition = 1;
    decimal tmp = 0;

    for (int i=0; i
    {
        lDecimalPosition *= 10;
    }
    
    // multiply by 10, 100, 1000, etc...
    tmp = num * lDecimalPosition;
    tmp = Math.Truncate(tmp);
    // divide by 10, 100, 1000, etc...
    tmp = tmp / lDecimalPosition;

    return tmp;
}

Next I tried to convert this to C++ as an experiment.  C++ does not have a Math.Truncate method so I needed to rely on implicit conversion.

The Bug:  C++ does not define the maximum values for types; they are defined by the compiler vendor and platform.  It is not possible to be certain that a large value using a double will not be truncated too short when it is implicitly converted to long long int.  As a matter of fact long long int is currently only supported by C++0x. (see the below function)

C++ Code:
double truncate(double z, int decimal)
{
    unsigned long lDecimalPosition = 1;
    unsigned long tmp = 0;
    long long int llMax = numeric_limits::max();
    
    for (int i=0; i<decimal; i++)
    {
        lDecimalPosition *= 10;
    }
    if (llMax < (z * lDecimalPosition) )
    {
        throw 10;
    }
    tmp = z * lDecimalPosition;

    /* explicit conversion to double to prevent 
    truncate after the division, otherwise 
    value would be truncated to long long int 
    automatically once the division has been 
    calculated. */
    z = (double) tmp / lDecimalPosition;
    return z;
}

WARNING: This code will function only with the simplest of double values passed in.  Since the type "Double" is stored as a floating point value there will be precision loss at some point.

A simple program demonstrates this:
#include <cstdlib>
#include <iostream>
#include <iomanip>
using namespace std;

int main(int argc, char** argv) {
    cout << setprecision(17);     
    cout << "1.1 as a double " << (double) 1.1 << endl;    
    return (EXIT_SUCCESS);
}

output:

1.1 as a double 1.1000000000000001

Ouch!

Friday, January 15, 2010

WakeOnLan ported to C#

Paul Mutton wrote a great article on how to use Wake-On-Lan which includes a tutorial on how to create a Magic Packet sender in Java.  He even provides the source of his java implementation: http://www.jibble.org/wake-on-lan/

I took the liberty of converting it to C# with a few minor modifications.  It allows a user to specify a Port number manually.

WHY C#?  Well why not?  I'm using C# at work so it comes naturally.  Mono gives me the ability to run code on both Windows and Linux.  Yes, so does Java.  There are a variety of tools out there and I like to familiarize myself with a good number of them.  In this spirit, I am currently writing a small connection testing tool in Java using Swing.  Maybe I'll write another port of Wake-On-LAN in C++ for fun.

Here is the C# code below:


using System;
using System.Net;
using System.Net.Sockets;

namespace WakeOnLan
{
    class Program
    {       
        static void Main(string[] args)
        {
            string macAdd = "";
            string ip = "";
            string port = "9";
            int iPort = 0;

            if (args.Length < 2) {
                System.Console.WriteLine("Usage: wakeonlan <broadcast-ip> <mac-address> [<port>]");
                System.Console.WriteLine("Example: wakeonlan 192.168.0.255 00:0D:61:08:22:4A");
                System.Console.WriteLine("Example: wakeonlan 192.168.0.255 00-0D-61-08-22-4A");
                System.Environment.Exit(1);
            }

            ip = args[0];
            macAdd = args[1];
            if (args.Length > 2)           
                port = args[2];

            if (!IsNumeric(port))
            {
                System.Console.WriteLine("Port entered was not a number...");
                return; // quit
            }                     

            iPort = Int32.Parse(port);

            if (iPort > 65535 || iPort < 0)
            {
                System.Console.WriteLine("Port value is invalid");
                return;
            }

            byte[] mac = getMacBytes(macAdd);
            //byte[] mac = new byte[] { 0x00, 0x0F, 0x1F, 0x20, 0x2D, 0x35 };
            WakeUp(mac, iPort, ip);
        }

        ///
        /// Check whether string can be converted to a number value
        ///
        private static bool IsNumeric(string num)
        {
            try
            {
                double d = Double.Parse(num);
                return true;
            }
            catch
            {
                return false;
            }
        }

        ///
        /// Sends a Wake-On-Lan packet to the specified MAC address.
        ///
        private static void WakeUp(byte[] mac, Int32 iPort, string myIP)
        {
            //
            // WOL packet is sent over UDP 255.255.255.0:40000.
            //
            UdpClient client = new UdpClient();
            client.Connect(myIP, iPort);

            //
            // WOL packet contains a 6-bytes trailer and 16 times a 6-bytes sequence 
            // containing the MAC address.
            byte[] packet = new byte[17 * 6];

            //
            // Trailer of 6 times 0xFF.
            //
            for (int i = 0; i < 6; i++)
                packet[i] = 0xFF;

            //
            // Body of magic packet contains 16 times the MAC address.
            //
            for (int i = 1; i <= 16; i++)
                for (int j = 0; j < 6; j++)
                    packet[i * 6 + j] = mac[j];

            //
            // Submit WOL packet.
            //
            client.Send(packet, packet.Length);
            System.Console.WriteLine("Wake-on-LAN packet sent.");
        }
   
        private static byte[] getMacBytes(String macStr)
        {
            byte[] bytes = new byte[6];
            string[] hex = macStr.Split(new Char[] { ':', '-' });
            if (hex.Length != 6) {
                throw new ArgumentException("Invalid MAC address.");
            }
            try {
                for (int i = 0; i < 6; i++) {
                    Int32 intVal = Int32.Parse(hex[i], System.Globalization.NumberStyles.AllowHexSpecifier);
                    bytes[i] = (byte)intVal;
                }
            }
            catch (FormatException e) {
                throw new ArgumentException("Invalid hex digit in MAC address.");
            }
            return bytes;
        }
    }
}