views:

154

answers:

2

suppose I have the following IronPython script:

def Calculate(input):
    return input * 1.21

When called from C# with a decimal, this function returns a double:

var python = Python.CreateRuntime();
dynamic engine = python.UseFile("mypythonscript.py")

decimal input = 100m; // input is of type Decimal
// next line throws RuntimeBinderException: 
//     "cannot implicitly convert double to decimal"
decimal result = engine.Calculate(input); 

I seem to have two options:

First, I could cast at the C# side: seems like a no-go as I might loose precision.

decimal result = (decimal)engine.Calculate(input); 

Second option is to use System.Decimal in the python script: works, but pollutes the script somewhat...

from System import *

def CalculateVAT(amount):
    return amount * Decimal(1.21)

Is there a short-hand notation DLR that the number 1.21 should be interpreted as a Decimal, much like I would use the '1.21m' notation in C#? Or is there any other way to enforce decimal to be used instead of double?

+3  A: 

Look at the python docs:

Decimal instances can be constructed from integers, strings, or tuples. To create a Decimal from a float, first convert it to a string. This serves as an explicit reminder of the details of the conversion (including representation error).

Unfortunatelly there is no short hand notation you are looking for. Even python has none. The standard way of 'hard coding' a decimal is to pass a string representation to the decimal constructor:

from decimal import Decimal
my_decimal = Decimal("1.21")

If you are writing code that is heavy on .net interop passing (it seems like you are) it may be better just using the .net Decimal datatype right away, as you proposed yourself. However be aware that you passing a floating point number to the decimal constructor in this example. You may get a small error in accuracy from the conversion, if the number has much more decimal places:

from System import *
my_decimal = Decimal(1.21)

So you may be better off using this constructor overload:

from System import *
my_decimal = Decimal(121, 0, 0, False, 2)

Not the most beautiful and shortest way of doing what you want, but the best in terms of performance and accuracy.

Philip Daubmeier
Thanks. I will stick with the Decimal(1.21) notation. In this case, I can live with the constructor from a floating point, as there will be no precision loss in my case. I was more concerned about the result of the calculation.
jeroenh
+3  A: 

seems like a no-go as I might lose precision.

Yeah, you do that in the other method too:

Decimal(1.21)

Once you've written 1.21 that's a floating point number and you've already lost the full precision before even passing it to the Decimal constructor. That's why Python's native Decimal class must be initialised from a string, not a float:

decimal.Decimal('1.21')

There doesn't seem to be a similar constructor for .NET's Decimal, but you could perhaps do it using the constituent parts constructor:

System.Decimal(121, 0, 0, False, 2)
bobince
Seems like we simultaneously posted nearly identical answers :)
Philip Daubmeier