Introduction
C# to JavaScript compilers like Saltarelle are becoming popular among .NET developers as they allow to write C# applications that target the web platform (HTML5/CSS3/JS).
Unfortunately JavaScript's lack of a fixed-point numeric data type makes such platform not suitable for writing client-side business applications.
This article shows how to leverage the power of the C# language and Saltarelle compiler to implement a fixed-point data type class that can be mapped transparently in JavaScript and be used in applications as a good replacement of .NET
Decimal
type.
The Problem
The only numeric data type available in JavaScript is the floating point number type ("Number"). Floats are good for making scientific calculations but fail to be accurate when they are used to store money values containing fractions (cents).
Consider the following JavaScript code:
var a = 0.1;
a = a + 0.2;
if(a==0.3) { window.alert('equal!'); }
else { window.alert('NOT equal!'); }
you won't believe, but the result is "NOT equal" !
This because the floating point notation is unable to store the value 0.1 accurately, but can store only a very close approximation of it. This small difference may accumulate and cause unpredictable errors, manifesting as missed checks (as in the example) or incorrect values.
Of course this is not acceptable in a business application where the minimal requirement is that the computer must compute correctly!
The problem is usually solved by adopting a fixed-point notation. Established a level of precision (eg. 4 decimal digits), all numbers are scaled up to eliminate the decimal dot and are stored in memory as integers. All subsequent arithmetic operations carried on the numbers must take into account the scale factor, for example when multiplying a * b
the result needs to be scaled down one time, because each of the operands add its own scale factors, resulting in numbers scaled twice if not corrected.
In this article I present a fixed-point class implementation, called Currency
, that has the following features:
- it's transparently mapped to the JavaScript
Number
type; this means it's not a real class and so it carry no code with it (no methods, no properties, no body class). Similarly to all other numeric types in Saltarelle, there is no way of retrieving its original C# type at runtime (calling GetType()
will not return "Currency" but "Number" instead).
- arithmetic operations are translated into JavaScript as inline code (Saltarelle's
[InlineCode]
attribute decorator does all the magic).
- when performing operations, all numbers are firstly converted to fixed-point, then the operation is calculated and the result is converted again in floating point. This allows to keep numbers stored as normal floating point numbers, without any scale involved for the final user. (The opposite would be keeping numbers stored as scaled-up integers and scale them down when they are referenced--but it's less practical).
- in C# the class is seen as a normal numeric type, similar to .NET's
Decimal
, with operators and implicit/explicit cast conversions defined.
- the precision (scale) of this
Currency
data type is fixed to 4 (scale factor 10000) which should be enough for real life applications involving money. Other scales may be obtained by simple search and replace in the source code, changing the string "10000" into another power of 10 (e.g. "100" for fewer decimal digits).
How does it work
The core of the class functionality resides in the definition of the arithmetic operators. As a rule, when a mathematical operation is required, operands are converted to fixed point by upscaling and truncating; then the operation is calculated and finally the result is converted again to floating point.
For example the addition operation is defined as:
[InlineCode("( (({d1}*10000)>>0) + (({d2}*10000)>>0) )/10000")]
public static Currency operator +(Currency d1, Currency d2)
{
return d1;
}
the [InlineCode]
attribute tells the compiler to ignore the method body ("return d1
") and output instead the inline JavaScript code contained in the string argument.
In the example, the two operands d1
and d2
are:
- scaled up by mean of "* 10000
"
- truncated by mean of ">> 0
"
- numbers are then added together by mean of "+
"
- and scaled down again by mean of "/ 10000
".
The real usefullness of the class is that it completely hides such tedious operations that otherwise had to be hardcoded manually, making the souce code less readable and difficult to maintain. Those who do not use Saltarelle and code directly in JavaScript instead of C#, have no other way; the best they can do is to have an external class library and use method calls instead of operators (e.g. d1.Add(d2)
and so on).
How to Use the code
Add Currency.cs
to your Saltarelle script project and start to use the class as if it was a new data type.
Variables can be initialized with integer or floating point constants. For example:
Currency a = 10;
Currency b = (Currency) 3.14;
As initializing from a float constant (3.14
) may incur in numeric rounding, the cast operator is explicitly needed. Integer numbers are encoded exactly and so they require no cast. Similarly, when converting Currency values to another numeric type, cast is always required, for example:
double c = (double) b;
Conversion to string may be otained simply via .ToString()
. For completeness, Currency implements also the following methods:
.ToFixed([fractionDigints])
.ToPrecision([precision])
and the static methods: Ceiling()
, Floor()
and Round()
.
Testing the class
As a simple test we use the following C# code:
double fa = 0.1;
fa = fa + 0.2;
double fb = 0.3;
if(fa==fb) Window.Alert("equals");
else Window.Alert("NOT equals");
Currency ca = (Currency) 0.1;
ca = ca + (Currency) 0.2;
Currency cb = (Currency) 0.3;
if(ca==cb) Window.Alert("equals");
else Window.Alert("NOT equals");
When running it, the double section fails but the Currency one passes as expected.
Now look at the generated JavaScript code:
var fa = 0.1;
fa = fa + 0.2;
var fb = 0.3;
if (fa === fb) {
window.alert('equals');
}
else {
window.alert('NOT equals');
}
var ca = (0.1 * 10000 >> 0) / 10000;
ca = ((ca * 10000 >> 0) + ((0.2 * 10000 >> 0) / 10000 * 10000 >> 0)) / 10000;
var cb = (0.3 * 10000 >> 0) / 10000;
if (ca === cb) {
window.alert('equals');
}
else {
window.alert('NOT equals');
}
The two sections are very similar, the only extra code added by the compiler is relative to the inline arithmetic operations (now imagine if they had to be written by hand!).
Disclaimer and conclusion
I'm not a math expert and the code presented here is the result of my understanding of the theory, so I recommend to test it appropriately before real use. I wrote it for fun as an exercise in Saltarelle, I hope it inspires more developers to switch from plain JavaScript to full C# for their client-side web applications.
History
- Release 1.0 - Dec, 4 2012