Click here to Skip to main content
15,893,381 members
Articles / Web Development / HTML

Head-to-head benchmark: C++ vs .NET

Rate me:
Please Sign up or sign in to vote.
4.64/5 (338 votes)
4 Jul 2011LGPL356 min read 1.2M   2.3K   202  
How fast is C++ compared to C#? Let's compare code ported directly between the two languages.
#ifndef _COMMON_FIXEDPOINT_H
#define _COMMON_FIXEDPOINT_H

#include <limits>
#include "ClipConvert.h"

namespace Math
{
	// ClipConvertFixedPoint::Do() converts from one fixed-point representation to
	// another, or between fixed-point and integer representations.
	//
	// To be efficient, the compiler must optimize "if(constant expr)", eliminate 
	// unreachable code, and put the inlining decision after these optimizations.
	// I just wish I could tell how well the compiler optimizes. In many cases 
	// this routine should reduce to "return TO_INT(from >> constant)", or 
	// "return TO_INT(from) << constant".
	//
	// This was a lot harder to write than I imagined! Note: this is a struct, not 
	// a function, due to a bug in eVC 4, with which this code is no longer tested.
	template<int TO_FRAC, typename TO_INT, int FROM_FRAC, typename FROM_INT>
	struct ClipConvertFixedPoint
	{
		inline static TO_INT Do(FROM_INT from)
		{
			#if _MSC_VER > 1202
			// Ignore the inevitable compiler warning 'shift count negative or too big'
			#pragma warning(push)
			#pragma warning(disable:4293)
			#endif

			const static int FROM_INTBITS = num_traits<FROM_INT>::precision_bits - FROM_FRAC;
			const static int TO_INTBITS = num_traits<TO_INT>::precision_bits - TO_FRAC;
			
			if (TO_INTBITS >= FROM_INTBITS) {
				// Moving to a wider or just-as-wide type (in terms of whole-number 
				// bits). There is no possibility of overflow, except if converting
				// from signed to unsigned at the same FRAC and integer size--a case 
				// I'm not bothering to handle.
				if (TO_FRAC > FROM_FRAC) // implies sizeof(TO_INT) > sizeof(FROM_INT)
					return TO_INT(from) << (TO_FRAC-FROM_FRAC);
				else 
					return TO_INT(from >> (FROM_FRAC-TO_FRAC));
			} else {
				// TO_INTBITS < FROM_INTBITS
				if (sizeof(TO_INT) < sizeof(FROM_INT)) {
					FROM_INT to;
					if (TO_FRAC <= FROM_FRAC)
						to = from >> (FROM_FRAC-TO_FRAC);
					else
						to = from << (TO_FRAC-FROM_FRAC);
					
					if (to != (FROM_INT)(TO_INT)to) {
						// Overflow.
						return from < 0 ? num_traits<TO_INT>::min_int : num_traits<TO_INT>::max_int;
					} else
						return TO_INT(to);
				} else { 
					// sizeof(TO_INT) >= sizeof(FROM_INT) and TO_INTBITS < FROM_INTBITS
					// implies TO_FRAC >= FROM_FRAC (and if TO_FRAC == FROM_FRAC, the 
					// conversion is from unsigned to signed at the same integer size,
					// but that case is not handled, i.e. not clipped.)
					TO_INT to = TO_INT(from) << (TO_FRAC-FROM_FRAC);
					// Normally, shifting right will restore the old value:
					if (TO_INT(from) != (to >> (TO_FRAC-FROM_FRAC))) {
						// There is an overflow iff it doesn't.
						return from < 0 ? num_traits<TO_INT>::min_int : num_traits<TO_INT>::max_int;
					} else
						return to;
				}
			}

			#if _MSC_VER > 1202
			#pragma warning(pop)
			#endif
		}
	};

	////////////////////////////////////////////////////////////////////////////////
	// FixedPoint //////////////////////////////////////////////////////////////////
	// FixedPoint<FRAC,intT> holds a fixed-point number and acts a lot like a
	// floating-point number.  FRAC is the number of fraction bits and intT is
	// the underlying integer type.
	//
	// Only one operation offers any kind of overflow protection: conversion from 
	// another type. If conversion from int, float, double or another FixedPoint
	// type would overflow, then the result is the maximum or minimum FixedPoint
	// value (e.g. (double)(FixedPoint<8,int16>)200 == 127.99609375). 
	// C++ makes it very hard to detect most overflows, so for performance reasons,
	// I generally don't try. An expression such as the following can detect the
	// overflow: IsInfinite((FixedPoint<8,int16>)200).
	//
	// When using eVC/VC6, you must use the SPECIALIZE_FIXEDPOINT_TRAITS() macro
	// in FixedPoint.cpp for each specialization of FixedPoint that you need;
	// otherwise you will get linker errors trying to access constants such as
	// UNIT and MAX_INTEGER. That's because the constants are considered variables
	// under those compilers. Visual Studio 7 and above do not need FixedPoint.cpp.
	//
	// Tip: add the following debugger visualizer to 
	// C:\Program Files\Microsoft Visual Studio 9.0\Common7\Packages\Debugger\autoexp.dat:
	//
	// Math::FixedPoint<*> {
	//     preview( #( $e.N / (double)(1 << SHIFT) ) )
	// }
	template<int FRAC, typename intT = int32>
	struct FixedPoint {
	protected:
		struct Prescale {};
		FixedPoint(intT n, Prescale) : N(n) {}
	public:
		typedef FixedPoint<FRAC,intT> self_t;
		typedef intT int_t;
		enum { 
			SHIFT = FRAC, 
			TOTALBITS = sizeof(intT) * CHAR_BIT,
		};
		DECLARE_TRAIT_CONSTANT(intT, UNIT, intT(1) << SHIFT);
		DECLARE_TRAIT_CONSTANT(intT, MASK, UNIT - 1);
		DECLARE_TRAIT_CONSTANT(intT, MIN_N, numeric_limits<intT>::is_signed ? intT(1) << (TOTALBITS-1) : 0);
		DECLARE_TRAIT_CONSTANT(intT, MAX_N, ~MIN_N);
		DECLARE_TRAIT_CONSTANT(intT, MIN_INTEGER, MIN_N >> FRAC);
		DECLARE_TRAIT_CONSTANT(intT, MAX_INTEGER, MAX_N >> FRAC);

		intT N;
		FixedPoint()                      { N = 0; assert(sizeof(self_t)==sizeof(intT)); }
		#if _MSC_VER > 1202
		// In EVC, this constructor conflicts with the template constructor below.
		FixedPoint(const self_t& rhs)     { N = rhs.N; }
		#endif
		FixedPoint(int8 i)                { *this = i; }
		FixedPoint(uint8 i)               { *this = i; }
		FixedPoint(int16 i)               { *this = i; }
		FixedPoint(uint16 i)              { *this = i; }
		FixedPoint(int32 i)               { *this = i; }
		FixedPoint(uint32 i)              { *this = i; }
		FixedPoint(int64 i)               { *this = i; }
		FixedPoint(uint64 i)              { *this = i; }
		FixedPoint(double d)              { *this = d; }
		FixedPoint(float f)               { *this = f; }
		
		template<int FRAC2, typename INT2>
		explicit FixedPoint(const FixedPoint<FRAC2,INT2>& rhs) {
			N = ClipConvertFixedPoint<FRAC,intT,FRAC2,INT2>::Do(rhs.N);
		}

		self_t FracPart() const           { return Prescaled(N & MASK); }
		intT WholePart() const             { return N >> SHIFT; } // 2.2 => 2; -2.2 => -3
		intT Floor() const                 { return N >> SHIFT; } // Just another name for it
		intT Ceil()  const                 { return (N + (UNIT-1)) >> SHIFT; }
		intT Round() const                 { return (N + (UNIT/2)) >> SHIFT; }
		intT ShiftedFloor (int fractionBits) const { return N >> (SHIFT - fractionBits); }
		self_t FractionalFloor (int fractionBits) const { 
			int clearBits = SHIFT - fractionBits;
			return Prescaled(N >> clearBits << clearBits);
		}
		self_t FractionalCeil (int fractionBits) const {
			int clearMask = (1 << (SHIFT - fractionBits)) - 1;
			return Prescaled((N + clearMask) & ~clearMask); 
		}
		void SwapWith(self_t &other) 
		   { intT temp = N; N = other.N; other.N = temp; }
		self_t Abs()  const { return Prescaled(N < 0 ? -N : N); }
		static self_t One() { return Prescaled(UNIT); }
		static self_t Zero() { return self_t(); }
		static self_t Epsilon() { return Prescaled(1); }
		static self_t Min() { return Prescaled(MIN_N); }
		static self_t Max() { return Prescaled(MAX_N); }
		static self_t Prescaled(intT prescaled) { return self_t(prescaled, Prescale()); }
		
		self_t Sqrt()
		{
			if (N <= MAX_INTEGER)
				return Prescaled(::Sqrt(N << FRAC));
			else
				// Compute lower-precision answer
				return Prescaled(::Sqrt(N >> (FRAC&1)) << (FRAC+1)/2);
		}

		/*struct explicit_numeric_conversion {
			intT N;
			explicit_numeric_conversion(intT n) { N=n; } 
		};
		operator typename explicit_numeric_conversion() const { return explicit_numeric_conversion(N); }*/
		operator typename int8()   const { return ClipConvertFixedPoint<0,int8,FRAC,intT>::Do(N); }
		operator typename uint8()  const { return ClipConvertFixedPoint<0,uint8,FRAC,intT>::Do(N); }
		operator typename int16()  const { return ClipConvertFixedPoint<0,int16,FRAC,intT>::Do(N); }
		operator typename uint16() const { return ClipConvertFixedPoint<0,uint16,FRAC,intT>::Do(N); }
		operator typename int32()  const { return ClipConvertFixedPoint<0,int32,FRAC,intT>::Do(N); }
		operator typename uint32() const { return ClipConvertFixedPoint<0,uint32,FRAC,intT>::Do(N); }
		operator typename int64()  const { return ClipConvertFixedPoint<0,int64,FRAC,intT>::Do(N); }
		operator typename uint64() const { return ClipConvertFixedPoint<0,uint64,FRAC,intT>::Do(N); }
		operator double() const       { return (double)N / (double)(1 << SHIFT); }
		operator float () const       { return (float)N / (float)(1 << SHIFT); }

		self_t& operator= (double d) 
		{
			double d2 = (double)ldexp(d, SHIFT);
			N = ClipConvert<intT>(d2);
			return *this; 
		}
		self_t& operator= (float f)
		{ 
			float f2 = (float)ldexp(f, SHIFT);
			N = ClipConvert<intT>(f2);
			return *this; 
		}
		self_t& operator= (int8 rhs)   { N = ClipConvertFixedPoint<FRAC,intT,0,int8>::Do(rhs);   return *this; }
		self_t& operator= (uint8 rhs)  { N = ClipConvertFixedPoint<FRAC,intT,0,uint8>::Do(rhs);  return *this; }
		self_t& operator= (int16 rhs)  { N = ClipConvertFixedPoint<FRAC,intT,0,int16>::Do(rhs);  return *this; }
		self_t& operator= (uint16 rhs) { N = ClipConvertFixedPoint<FRAC,intT,0,uint16>::Do(rhs); return *this; }
		self_t& operator= (int32 rhs)  { N = ClipConvertFixedPoint<FRAC,intT,0,int32>::Do(rhs);  return *this; }
		self_t& operator= (uint32 rhs) { N = ClipConvertFixedPoint<FRAC,intT,0,uint32>::Do(rhs); return *this; }
		self_t& operator= (int64 rhs)  { N = ClipConvertFixedPoint<FRAC,intT,0,int64>::Do(rhs);  return *this; }
		self_t& operator= (uint64 rhs) { N = ClipConvertFixedPoint<FRAC,intT,0,uint64>::Do(rhs); return *this; }
		self_t& operator= (const self_t& rhs)  { N = rhs.N;  return *this; }
		self_t& operator+= (const self_t& rhs) { N += rhs.N; return *this; }
		self_t& operator-= (const self_t& rhs) { N -= rhs.N; return *this; }
		self_t& operator*= (const self_t& rhs) { *this = *this * rhs; return *this; }
		self_t& operator/= (const self_t& rhs) { *this = *this / rhs; return *this; }
		self_t& operator<<= (int amt)          { N <<= amt;  return *this; }
		self_t& operator>>= (int amt)          { N >>= amt;  return *this; }
		self_t& operator&= (const self_t& r) { N &= r.N; return *this; }
		self_t& operator|= (const self_t& r) { N |= r.N; return *this; }
		self_t& operator^= (const self_t& r) { N ^= r.N; return *this; }
		bool operator> (const self_t& rhs) const { return N > rhs.N; }
		bool operator< (const self_t& rhs) const { return N < rhs.N; }
		bool operator>=(const self_t& rhs) const { return N >= rhs.N; }
		bool operator<=(const self_t& rhs) const { return N <= rhs.N; }
		bool operator==(const self_t& rhs) const { return N == rhs.N; }
		bool operator!=(const self_t& rhs) const { return N != rhs.N; }
		bool operator> (intT rhs) const { return N > (rhs << SHIFT); }
		bool operator< (intT rhs) const { return N < (rhs << SHIFT); }
		bool operator>=(intT rhs) const { return N >= (rhs << SHIFT); }
		bool operator<=(intT rhs) const { return N <= (rhs << SHIFT); }
		bool operator==(intT rhs) const { return N == (rhs << SHIFT); }
		bool operator!=(intT rhs) const { return N != (rhs << SHIFT); }
		self_t operator- ()      const { return Prescaled(-N); }
		self_t operator~ ()      const { return Prescaled(~N); }
		self_t operator<<(int r) const { return Prescaled(N << r); }
		self_t operator>>(int r) const { return Prescaled(N >> r); }
		self_t operator+ (const self_t& r) const { return Prescaled(N + r.N); }
		self_t operator- (const self_t& r) const { return Prescaled(N - r.N); }
		self_t operator+ (intT rhs) const { return Prescaled(N + (rhs << SHIFT)); }
		self_t operator- (intT rhs) const { return Prescaled(N - (rhs << SHIFT)); }
		self_t MulDiv(self_t mul, self_t div) const { return Math::MulDiv(*this, mul.N, div.N); }
		self_t MulDiv(intT mul, intT div)       const { return Math::MulDiv(*this, mul, div); }
		self_t operator* (intT r) const { return Prescaled(N * r); }
		self_t operator/ (intT r) const { return Prescaled(N / r); }
		self_t operator* (const self_t& b) const 
		{
			intT afrac =   N & MASK;
			intT bfrac = b.N & MASK;
			self_t result = self_t((N >> FRAC) * (b.N >> FRAC));
			result.N += afrac * bfrac >> Frac;
			return whole;
		}
		self_t operator/ (const self_t& b) const
		{
			intT whole = N / b.N;
			intT remainder = N % b.N;
			remainder = (remainder << FRAC) / b.N;
			assert(remainder < (1 << FRAC));
			self_t r;
			r.N = (whole << FRAC) + remainder;
			return r;
			// TODO: test negative numbers: 7 / -2.5, -7 / 2.5, -7 / -2.5
		}
		self_t operator% (const self_t& r) const { return Prescaled(N % r.N); }
		self_t operator& (const self_t& r) const { return Prescaled(N & r.N); }
		self_t operator| (const self_t& r) const { return Prescaled(N | r.N); }
		self_t operator^ (const self_t& r) const { return Prescaled(N ^ r.N); }

		// pre-increment & decrement
		self_t& operator ++ ()    { this->N += UNIT; return *this; }
		self_t& operator -- ()    { this->N -= UNIT; return *this; }
		// post-increment & decrement
		self_t operator ++ (int)  { self_t copy(*this); this->N += UNIT; return copy; }
		self_t operator -- (int)  { self_t copy(*this); this->N -= UNIT; return copy; }
	};
}

#endif // _COMMON_FIXEDPOINT_H

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer None
Canada Canada
Since I started programming when I was 11, I wrote the SNES emulator "SNEqr", the FastNav mapping component, the Enhanced C# programming language (in progress), the parser generator LLLPG, and LES, a syntax to help you start building programming languages, DSLs or build systems.

My overall focus is on the Language of your choice (Loyc) initiative, which is about investigating ways to improve interoperability between programming languages and putting more power in the hands of developers. I'm also seeking employment.

Comments and Discussions