As C# does not offer a underlying base class for Numbers, you have to write one that can work with all numbers without specifying the Type directly

## Introduction

As I am working on my Project Morestachio, I wanted to allow users to either supply numbers from code but also allow number operations in my Template. That was where the trouble started and I needed to operate on two numbers that might not be the same or have unknown types. As I already had to write a parser to get the number from the text template, I decided to allow all numbers to be wrapped into the *Number.cs* class and implemented all common operations on it.

**Hint**: The source on github contains references to the `Morestachio`

Project within the `MorestachioFormatter`

region. If you want to use only the *Number.cs* class, you are safe to delete this whole region. The *Number.cs* attached to this tip does not contain this region!

## Background

*Number.cs* is of interest for everyone who got numbers but without knowing what the number represents. *Number.cs* can parse any number from `string`

according the rules of C# (Integral numeric types (C# reference) and Real literals).

## Using the Code

*Number.cs* has three different ways in which to create a new instance of *Number.cs*:

`bool Number.TryParse(string, CultureInfo, out Number)`

.
This tries to parse a `string`

into a number based on the literal and the C# rules.

`Number.ctor(long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal)`

Use the constructor to create a new number with the internal .NET type

`implicit operator Number(Long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal)`

*Number.cs* implements the implicit casting operator for all .NET Number types

You can get a list of all supported .NET types by calling `Number.CsFrameworkIntegralTypes`

and `Number.CsFrameworkFloatingPointNumberTypes`

respectively. Note that those lists are only informatial.

Now that you got your instance of *Number.cs*, you can make operations with it. There are two general ways in operating with *Number.cs*:

- Call the operation functions like:
`Add(Number): Number`

`Substract(Number): Number`

`Multiply(Number): Number`

`Divide(Number): Number`

`Modulo(Number): Number`

`ShiftLeft(Number): Number`

`ShiftRight(Number): Number`

`GreaterThen(Number): bool`

`LessThen(Number): bool`

`Equals(Number): bool`

`Same(Number): bool`

`IsNaN(): bool`

`Max(Number): Number`

`Min(Number): Number`

`Pow(Number): Number`

`Log(Number): Number`

`Log10(Number): Number`

`Negate(): Number`

`Abs(): Number`

**Round(Number): Number**

`Sin(): Number`

`Sinh(): Number`

`Sqrt(): Number`

`Tanh(): Number`

`Cos(): Number`

`Cosh(): Number`

`Acos(): Number`

`Asin(): Number`

`Atan(): Number`

`Atan2(): Number`

`Truncate(): Number`

`Ceiling(): Number`

`Floor(): Number`

**Note**: `Equals(Number)`

will first check if both numbers are of the same .NET type and then call `Same(Number) `

for value equality check

- Use C# operators.
*Number.cs* implements the following operators on itself:
`Number `

`+ Number`

`Number++`

`Number - Number `

`Number--`

`Number << Number `

`Number >> Number `

`Number == Number `

`Number != Number `

`Number < Number `

`Number > Number `

`Number <= Number `

`Number >= Number `

In addition to these operations on itself (`Number + Number`

or `Number < Number`

), you can also use the following operators on all other .NET number types:

`Number + long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal`

`Number - long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal`

`Number * long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal`

`Number / long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal`

`Number % long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal`

These operators just call the methods from point 1.

*Number.cs* takes care of the implicit conversion of values from an operation. That means like in cs if you add two numbers together where one number is a floating point number, the result will always be a floating point number of the same type and if you add two integers together, the result will always be of the type that has more precision. This is done by a rating of numbers that exists within the `Number.CsFrameworkFloatingPointNumberTypes`

and `Number.CsFrameworkIntegralTypes`

. The function `Number.GetOperationTargetType`

will return the type that takes prevalence over another. The operation then always looks the same like this:

Example `Add`

Method:

public Number Add(Number other)
{
var targetType = GetOperationTargetType(this, other);
if (targetType == typeof(decimal))
{
return new Number(ToDecimal(null) + other.ToDecimal(null));
}
if (targetType == typeof(double))
{
return new Number(ToDouble(null) + other.ToDouble(null));
}
if (targetType == typeof(float))
{
return new Number(ToSingle(null) + other.ToSingle(null));
}
if (targetType == typeof(ulong))
{
return new Number(ToUInt64(null) + other.ToUInt64(null));
}
if (targetType == typeof(long))
{
return new Number(ToInt64(null) + other.ToInt64(null));
}
if (targetType == typeof(uint))
{
return new Number(ToUInt32(null) + other.ToUInt32(null));
}
if (targetType == typeof(int))
{
return new Number(ToInt32(null) + other.ToInt32(null));
}
if (targetType == typeof(ushort))
{
return new Number(ToUInt16(null) + other.ToUInt16(null));
}
if (targetType == typeof(short))
{
return new Number(ToInt16(null) + other.ToInt16(null));
}
if (targetType == typeof(byte))
{
return new Number(ToByte(null) + other.ToByte(null));
}
if (targetType == typeof(sbyte))
{
return new Number(ToSByte(null) + other.ToSByte(null));
}
throw new InvalidCastException($"Cannot convert {other.Value}
({other.Value.GetType()}) or {Value} ({Value.GetType()}) to a numeric type");
}

The same as over `other`

number type the object *Number.cs* is implemented as a `readonly struct`

and is immutable. The code is tested and implemented for:

- netstandard2.0
- netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0
- net46 net461 net462
- net47 net471 net472

### Other Methods

*Number.cs* has methods for checking ether an `Number`

or an object for ether beeing an Floating point number or an Integral number.

`static IsFloatingPointNumber(Number|object): bool`

`static IsIntegralNumber(Number|object): bool`

It also contains fields for:

`static readonly Zero: Number`

//a field representing an 0 int `static readonly MinusOne: Number`

//a field representing an negative 1 int `static readonly NaN: Number`

//this is a wrapper to the `double.NaN`

value

To be comparable and equaltable with all possible values, *Number.cs* also implements the following interfaces:

`IComparable`

`IComparable<Number|long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal>`

`IEquatable<Number|long|ulong|int|uint|byte|sbyte|short|ushort|float|double|decimal>`

At last, you can also use the `AsParsableString`

method to get a reparsable `string`

.

## A Word on Performance

I recently did some performance tests as I was interested in how much time was spent on casting and evaluation of the right operation type and I was not surprised that the *Number.cs* class is around ~2000 times slower than the native operation. But this was mostly due to unoptimised code. I tested 10.000.000 add operations with the C# native `+`

operator on 2 `int`

s and done the same with two *Number.cs* classes. I (unsurprisingly) found that 2 major performance problems where the enumeration of the list of `Number.CsFrameworkIntegralTypes`

, `Number.CsFrameworkFloatingPointNumberTypes`

and to some extent surprisingly for me, the performance of the `Number.Value`

getter. So I hard-coded the `Number.GetOperationTargetType`

and removed the access to the `Number.Value`

and access the value directly via its member variable. That helped a lot and *Number.cs* is now "only" ~140 times slower than a native operation.

## Points of Interest

I looked into a lot of code on github and everywhere else and was not able to find something like this general purpose so I decided to write it myself. I will keep developing the *number.cs* class as I might want to support more functions like added support for `Math`

functions in the future, so checkout the original file.

## The Morestachio Project

*Number.cs* is part of Morestachio, an project for Text Transformation completely written in C#. It would be very kind of you if you check it out:

## History

- 28
^{th} June, 2020: Init commit - 16
^{th} September, 2020: Updated version to 2.0 containing performance fixes and additional math methods - 25
^{th} September, 2020: Updated version to 3.0 containing more `Math`

functions and better code comments

A nice guy.

And WPF Developer.

And asp.net.

like everything I can get my hand on in .net.

But never java.