IronLanguages / ironpython3

Implementation of Python 3.x for .NET Framework that is built on top of the Dynamic Language Runtime.
Apache License 2.0
2.51k stars 290 forks source link

Unexpected output precision and/or rounding results in IronPython 3.4.0 #1626

Open AlGerngro opened 1 year ago

AlGerngro commented 1 year ago

Description

print - whether in conjunction with round or not - does not yield the expected output of decimal values as in 2.7

Steps to Reproduce

While I first observed the behavior in some breaking Unit tests while porting my .NET application to IronPython 3.4.0 using the new nupkg, a simple console example should do:

IronPython 3.4.0 (3.4.0.1000)
[.NETFramework,Version=v4.6.2 on .NET Framework 4.8.4515.0 (64-bit)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> print(685.86)
685.86000000000001
>>> f = float(685.86)
>>> print(f)
685.86000000000001
>>> print(round(f,2))
685.86000000000001
>>> print(round(f))
686
>>> print(round(f,1))
685.89999999999998
>>>

Therefore, I'd think of an output rather than a numeric issue.

Expected behavior: While I am aware that not every decimal number can be rendered in full fidelity, I would expect the same output as was the input argument; or in case of round the output being truncated to the specified number of decimal places. This was the case in IronPython 2.7.

Actual behavior: Although the result may be correct from a numeric point of view, the numbers always seem to be output with the same number of decimal places, which is never the one provided as an argument. Especially, rounding to 1 digit should not give me 14 digits.

Versions

If this is indeed expected, then I am not sure how to produce a normalized output.

slozier commented 1 year ago

Python 3 changed the str function of numbers to be equivalent to repr and so the values you're seeing is the repr for those numbers in IronPython. With IronPython 2 str was essentially applying %.12g formatting. You could do something like to get a similar representation as IronPython 2 (note that it won't match the trailing .0 if your float is an integer):

def to_str(val):
    return "%.12g" % val

Note that there is an outstanding task to try and match the shortest representation used by CPython: https://github.com/IronLanguages/ironpython2/issues/102.