"Convert To Number" should use decimal.Decimal
"Convert To Number" should use decimal, instead of documenting float precision problem.
def _convert_to_number(self, item, precision=None):
number = self._convert_to_number_without_precision(item)
if precision is not None:
number = float(round(number, self._convert_to_integer(precision)))
return number
could be substituted by (not thoroughly tested, can do, if there is demand)
def _remove_exponent(self, dec):
"Normalize decimal number"
return dec.quantize(Decimal(1)) if dec == dec.to_integral() else dec.normalize()
def _convert_to_number(self, item, precision=None):
if isinstance(item, Decimal):
parsed = item
elif isinstance(item, str):
parsed = Decimal(number.strip().replace(",", "."))
elif isinstance(item, int):
parsed = Decimal(item)
elif isinstance(item, float):
parsed = Decimal(item)
after_point_numbers = -parsed.as_tuple().exponent
if precision and after_point_numbers > precision:
parsed = round(parsed, precision)
exponent = parsed.as_tuple().exponent
if exponent > 0:
return self._remove_exponent(parsed)
return parsed
I agree in general, but we cannot change the return value type from float to Decimal due to backwards compatibility reasons. We could add an argument for controlling the type and change the default type after a long deprecation period, but because floats typically work fine, I don't think that's worth the effort. There are, however, other alternatives:
- Add an argument controlling the return value type without a plan to change the default. It could then also support other number types like
Fractionandcomplexin the future. - Add a new keyword like
Convert To Decimal. Easier to discover than the above, but less flexible. I don't want to add similar keywords for other number types. - Add a generic keyword for converting to any type Robot's argument conversion supports.
I ended up using Decimals out of necessity:
- Need to prevail number as it was. For example, voltmeter value 3.10000 and 3.1 is not the same. First is pretty accurate, second not so much. For accurate report, I must not loose these zeroes, I have to log it as it was. Workaround is log raw string value, convert to float and compute with it, but you don't need to with Decimals.
- Same problem. If device said 0.3 and I subtract 0.2, I need to have 0.1 as result. Again test_report/measure protocol look weird if result is 0.09999999999999998. Rounding is solution (if you know/remember to which level you want to round), but solution to problem, which didn't need to exist at first place.
I am aware, it's slower and more code than float, yet, especially in testing/measuring, rather slow and right.
I'm aware of benefits of using Decimal, but it doesn't change the fact that we cannot change the default return value. Do you have opinions about the alternatives I proposed?
Add an argument controlling the return value type without a plan to change the default. It could then also support other number types like Fraction and complex in the future.
This I found most plausible, some "type=Decimal" keyword argument . Convert To Decimal and other would pollute keywords. I would like still fight about default but this will do nicely too. Maybe later implement some "Global Config" for these situations/backward compatibility and such, issues?
Ok, let's implement type argument then. Implementation should use our type conversion logic and could be something as simple as this:
return TypeInfo.from_type_hint(type).convert(value)
The above implementation would happily convert to any type, though, and we probably wanted this to always return a number. Instead of checking the type, I believe we should check the return value with something like this:
result = TypeInfo.from_type_hint(type).convert(value)
if not isinstance(result, Number): # `from numbers import Number` needed for this to work
raise TypeError(...)
return result
Using the existing type conversion logic would be simple and would also automatically support all numeric types the conversion logic supports. At the moment it means int, float and Decimal, but I believe it would be a good idea to add converters also to Fraction and possibly also to complex. These would require their own issues, though.
Regarding to changing the default from float to Decimal, I pretty strongly believe it's not worth the effort (it would need a deprecation period) and I'm not sure is Decimal even better in the common case. I doubt a global config for changing argument default values is a good idea either, but at least it's not in the scope of this issue.
Are your @ilfirin-ms interested to implement this enhancement? If yes, I can add this to RF 7.4 scope.