Skip to content

Wrong behavior in float values using IronPython in a C# application #841

Open

Description

@jandres861

Description

We are running a sandbox in a .NET windows application (.NET framework 4.7.2 and IronPython 2.7).

Once we call the execution of the specific script, they returned to us a DivisionByZero exception, because a normal decimal value we are sending from .NET are being converted to a very small number (1.03e-311), that causes the Zero Division issue.

Steps to Reproduce

  1. Create the Runtime and Scope information
  2. Load the Python code to execute (only has several arithmetic operations, nothing very complex neither using arrays or collections)
  3. We receive the exception of the execution in a Try/Catch block in .NET.
  4. We "debug" the code using a direct RaiseError and we identified the runtime is assuming a very small number tending to zero, that is the reason of the fail.

Error returned:

"An error occured while executing the underlying Python script (Traceback (most recent call last): :: File "", line 98, in GetTheFailureMode :: File "", line 699, in GetMaxFfsLoss :: File "", line 2043, in GetMaxHoopLoss :: File "", line 2197, in GetHoopMsop :: ZeroDivisionError: Attempted to divide by zero.)"

Parameters from .NET:

"
addToIerance = true
depthPercentage = 7
designFactor = 0.72
designPressureInMpa = 14.1
inBend = false
lengthInMeter = 0.0689999999999884
lineType = "Hybrid"
outerDiameterInMeter = 0.1683
pipeGrade = "X60"
pofDefectCIass = "General"
wallThicknessInMeter = 0.0095
widthInMeter = 0.038
"

Values recovered in Python just calling the method:

"
An error occured while executing the underlying Python script (Traceback (most recent call last): :: File "", line 148, in GetMaxIliFfsLoss :: Exception: (

'WT: 6.95238308496e-310',
'Hybrid', <type 'float'>,
'Float Number: ', 1.034294700449062e-311,
'Float Number String', '0.0095',
'Convert From String To Float', 1.034294700449062e-311,
'Culture Number Format', <System.Globalization.NumberFormatInfo object at 0x000000000000002B [System.Globalization.NumberFormatInfo]>, 1.0343456283479938e-311,
False,
'0.0095',
1.034294700449062e-311,
1.0353302339822869e-311,
1.034294700449062e-311,
1.0334119502000876e-311,
True,
1.0338363493581819e-311,
'X60',
1.0349533679427365e-311))
"

Culture info:

NumberFormat
CurrencyDecimaIDigits = 2
CurrencyDecimalSeparator = "."
CurrencyGroupSeparator = ","


Expected behavior: Do not convert the normal float parameters to very small numbers.

Actual behavior: The float values are converted to a very small numbers that causes a Zero Division Exception.

Versions

IronPython 2.7
.NET Framework 4.7.2

Activity

  1. slozier commented on Aug 22, 2024

    @slozier
    Contributor

    How are you passing the parameters from .NET to Python? It's hard to tell what your doing from the description above. An example to reproduce the issue would be helpful...

  2. jandres861 commented on Aug 22, 2024

    @jandres861
    Author

    How are you passing the parameters from .NET to Python? It's hard to tell what your doing from the description above. An example to reproduce the issue would be helpful...

    Hi, thanks for your quick answer. Let me write some example of the code here, limited as by my company's IT policies ( I'm trying to include screenshots but is not possible for some reason)

    //Create the Runtime
    public RuntimeProvider()
            {
                // set the callback to log errors.
                var options = new Dictionary<string, object>();
                options["Debug"] = false;
    
                // Creates execution engine
                this.myEngine = Python.CreateEngine(options);
    
                // This is for debugging Python code
                // this.myEngine.SetTrace(this.OnTraceback);
                this.myScope = this.myEngine.CreateScope();
            }
    
    // Load the script
    public void LoadScript(string scriptSource, bool sameScope, bool sourceIsFileName)
            {
               
                    this.ScriptScope = this.RuntimeProvider.CreateScope();
                           ScriptSource source;
    
                             source = this.RuntimeProvider.Engine.CreateScriptSourceFromString(scriptSource);
             
                ScriptSources.Add(source);
                
                var compiled = source.Compile();
                this.myExecutions.Add(compiled.Execute(this.ScriptScope));
            }
    
    // Proxy
            private TcoAlgorithmProxy LoadAlgorithmInstance(string algorithmCode, string algorithmDescriptionBuilder)
            {
                var scriptLoader = new ScriptLoader();
                var pythonInstance = this.LoadAlgorithm(algorithmCode, algorithmDescriptionBuilder, scriptLoader);
    
                // Creates the proxy to comunicate the code with the other application domain
                // This proxy is neccesary because the object that you want to sent to the other domain must intherit from MarshalByRefObject
                var algorithm = new AlgorithmProxy(pythonInstance, scriptLoader.RuntimeProvider.Engine);
                return algorithm;
            }
    
    
    // Call function
    protected override double? GetValue(IClusterableFeature anomaly)
            {
                var ffsLoss = this.FfsAssessement.GetMaxIliFfsLoss(anomaly, lineType.Value, inBend.Value, pipeGrade.Value, anomaly.AddTolerance);
                return ffsLoss.Value;
            }
    
    // Specific function call in python
    public double GetMaxIliFfsLoss(
                string lineType,
                double wallThicknessInMeter,
                double designFactor,
                bool inBend,
                string pofDefectClass,
                double lengthInMeter,
                double widthInMeter,
                double depthPercentage,
                double designPressureInMpa,
                string pipeGrade,
                double outerDiameterInMeter,
                bool addTolerance)
            {
                return this.GetValue(
                    () => this.myPythonAlgorithmInstance.GetMaxIliFfsLoss(
                        lineType,
                        wallThicknessInMeter,
                        designFactor,
                        inBend,
                        wallThicknessInMeter.ToString("R"),
                        lengthInMeter,
                        widthInMeter,
                        depthPercentage,
                        designPressureInMpa,
                        pipeGrade,
                        outerDiameterInMeter,
                        addTolerance));

    And finally the simplified code in Python that is being executed:

    def GetMaxIliFfsLoss(self,
    lineType,
    wallThicknessInMeter,
    designFactor,
    inBend,
    pofDefectClass,
    lengthInMeter,
    widthInMeter,
    depthPercentage,
    designPressureInMpa,
    pipeGrade,
    outerDiameterInMeter,
    addTolerance):
    if not isinstance(wallThicknessInMeter, float):
    culture = CultureInfo.CurrentCulture
    number = 0.00950000
    numberStr = number.ToString(culture.NumberFormat)
    raise Exception("WT: " + str(wallThicknessInMeter), lineType,
    type(wallThicknessInMeter),
    "Float Number: ",
    number,
    "Float Number String",
    numberStr,
    "Convert From String To Float",
    Double.Parse("0.0095"),
    "Culture Number Format",
    culture.NumberFormat,
    designFactor,
    inBend,
    pofDefectClass,
    float(pofDefectClass),
    lengthInMeter,
    widthInMeter,
    depthPercentage,
    addTolerance,
    designPressureInMpa,
    pipeGrade,
    outerDiameterInMeter)

    And just using that RaiseError to print in the .NET Exception the values we identified the change to that very small numbers, not the desired ones.

  3. slozier commented on Aug 26, 2024

    @slozier
    Contributor

    It's interesting that Double.Parse("0.0095") would give you 1.034294700449062e-311. Is the code only failing once in a while or is it consistently incorrect? Unfortunately I haven't been able to reproduce. Which version of IronPython 2.7 and of the DLR are you using?

  4. jandres861 commented on Aug 26, 2024

    @jandres861
    Author

    It's interesting that Double.Parse("0.0095") would give you 1.034294700449062e-311. Is the code only failing once in a while or is it consistently incorrect? Unfortunately I haven't been able to reproduce. Which version of IronPython 2.7 and of the DLR are you using?

    As we are iterating over several calls to the python functions, the code works well in the first ones but after that starts to fail and we could never have a full execution of the algorithm.

    The specific version of IronPython is 2.7.9. The solution is build in .NET framework 4.7.2. Seems also related with the Microsoft.Scripting 1.2.2.0.

    Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions