[EDIT]
Modified such that it scales to units.
[/EDIT]
The following should work for a IEC62055-41 unsigned 2-bit-exponent 14-bit-mantissa fixed point value:
public enum As
{
Raw,
Energy,
Power,
Water,
Gas,
Time,
Currency,
}
public struct FixU_2_14
{
private UInt16 _raw;
public UInt16 Bits { get { return _raw; } }
public double this[As unit]
{
get
{
double mantissa = _raw & 0x3FFF;
double n = 1.0;
double m = 0.0;
for (int i = (_raw >> 14) & 0x3; i > 0; --i)
{
n *= 10.0;
m *= 10;
m++;
}
double value = n * mantissa + m * 16384;
switch (unit)
{
case As.Energy:
case As.Water: return value / 10.0;
case As.Currency: return value / 10000.0;
default: return value;
}
}
}
public FixU_2_14(double value, As unit)
{
if (double.IsInfinity(value) || double.IsNaN(value))
{
throw new ArgumentOutOfRangeException("value", value.ToString());
}
switch (unit)
{
case As.Energy:
case As.Water: value *= 10; break;
case As.Currency: value *= 10000; break;
default: break;
}
if (value < 0.0 || value > 1111.0 * 16384.0 - 1000.0)
{
throw new ArgumentOutOfRangeException("value", value.ToString());
}
_raw = 0;
double n = 1.0;
double m = 0.0;
double max = 0.0;
UInt16 exp = 0;
while (true)
{
max = n * 16383 + m * 16384;
if (max >= value) break;
n *= 10;
m *= 10;
m++;
exp++;
}
value -= m * 16384;
value /= n;
_raw = (UInt16)((exp << 14) | (int)value & ((1 << 14) - 1));
}
}
Some usages:
static void WriteFix(double d, As unit)
{
string us = string.Format("{0}", "["+unit.ToString()+"]");
FixU_2_14 fp = new FixU_2_14(d, unit);
double dd = fp[unit];
Console.WriteLine("{0,9} {4,-10} -> 0x{1:x4} = {2,9} {4,-10} (Delta = {3,3})", d, fp.Bits, dd, d - dd, us);
}
...
WriteFix(0.0, As.Raw);
WriteFix(1.0, As.Water);
WriteFix(2.0, As.Currency);
WriteFix(25.6, As.Energy);
WriteFix(1638.3, As.Energy);
WriteFix(16384, As.Water);
WriteFix(18201624, As.Gas);
for (int i = 1; i <= 18; i++) WriteFix(1000000 * i, As.Gas);
This results in
0 [Raw] -> 0x0000 = 0 [Raw] (Delta = 0)
1 [Water] -> 0x000a = 1 [Water] (Delta = 0)
2 [Currency] -> 0x4169 = 1.9994 [Currency] (Delta = 0.000599999999999934)
25.6 [Energy] -> 0x0100 = 25.6 [Energy] (Delta = 0)
1638.3 [Energy] -> 0x3fff = 1638.3 [Energy] (Delta = 0)
16384 [Water] -> 0x7999 = 16383.4 [Water] (Delta = 0.600000000000364)
18201624 [Gas] -> 0xffff = 18201624 [Gas] (Delta = 0)
1000000 [Gas] -> 0xa005 = 999924 [Gas] (Delta = 76)
2000000 [Gas] -> 0xc0b5 = 1999624 [Gas] (Delta = 376)
3000000 [Gas] -> 0xc49d = 2999624 [Gas] (Delta = 376)
4000000 [Gas] -> 0xc885 = 3999624 [Gas] (Delta = 376)
5000000 [Gas] -> 0xcc6d = 4999624 [Gas] (Delta = 376)
6000000 [Gas] -> 0xd055 = 5999624 [Gas] (Delta = 376)
7000000 [Gas] -> 0xd43d = 6999624 [Gas] (Delta = 376)
8000000 [Gas] -> 0xd825 = 7999624 [Gas] (Delta = 376)
9000000 [Gas] -> 0xdc0d = 8999624 [Gas] (Delta = 376)
10000000 [Gas] -> 0xdff5 = 9999624 [Gas] (Delta = 376)
11000000 [Gas] -> 0xe3dd = 10999624 [Gas] (Delta = 376)
12000000 [Gas] -> 0xe7c5 = 11999624 [Gas] (Delta = 376)
13000000 [Gas] -> 0xebad = 12999624 [Gas] (Delta = 376)
14000000 [Gas] -> 0xef95 = 13999624 [Gas] (Delta = 376)
15000000 [Gas] -> 0xf37d = 14999624 [Gas] (Delta = 376)
16000000 [Gas] -> 0xf765 = 15999624 [Gas] (Delta = 376)
17000000 [Gas] -> 0xfb4d = 16999624 [Gas] (Delta = 376)
18000000 [Gas] -> 0xff35 = 17999624 [Gas] (Delta = 376)
Cheers
Andi