-
Notifications
You must be signed in to change notification settings - Fork 208
Use integer division in encode_phys to prevent rounding errors with int64 #611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not convinved this is the right approach. Truncating may be worse than rounding in some existing use cases, so we should avoid breaking them / changing to different values unexpectedly. OTOH, you can always do the calculation yourself and assign to .raw
instead. Which might be better for just the few cases where large integers cause inaccuracy.
elif isinstance(value, int) and isinstance(self.factor, int): | ||
value = value // self.factor | ||
else: | ||
value = int(round(value / self.factor)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
round()
already returns an integer if ndigits
is omitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Round was the original code. My contribution is only the //
division, which is my use case is the right thing to do. What do you suggest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you touch / edit this code and such a useless call is found, please remove it as part of your change.
I think TS has a point here. It should be possible to reliably pass
This could be a good solution since 64-bit double can only hold up to 52 bits (+ sign bit) in the mantissa, so any values higher than that will get truncated in the float division.
Is it a use case to have non-integer factors? When do we get into this use case? Does the standard permit float scale factors? Another alternative would be to skip the division altogether if the factor is 1. In most cases factor is not used at all, and I'd like to suggest that |
I'm okay with bypassing the division altogether if the factor is one, as in
For example we want a quantity expressed as a percentage 0.0 ... 100.0 , but the bus representation is 0 ... 65535. I'd put in I see it as a breaking change if suddenly integer division is applied for a perfectly valid use-case that previously returned a properly rounded value. An integer factor can only upscale, such as mapping a quantity in meters into a common range with other variables that work with millimeters: That's a subtle difference but could change some systems' behavior in non-trivial and hard to detect ways. Pretty high risk for fixing an edge case which should better be avoided altogether by doing the calculation as needed outside the library, then using the By the way, if I read this code (current or from the PR) correctly, then we don't even apply the conversion if the OD variable is already a float? |
I still would expect
A simple fix is to use integer division only if both operands are int and the dividend is not fully representable in float. |
While testing
int64
values with SDOs, I observed an unexpected behavior:Upon investigation, it appears that the issue originates from the
encode_phys
function:The problem lies in the fact that the division is performed using floating-point arithmetic rather than integer arithmetic, leading to rounding that causes a loss of up to 10 bits of precision.
To address this, we should detect whether the input value is an integer and, if so, perform integer division (
//
) instead of floating-point division. Additionally, it may be prudent to emit a warning whenfactor
is not an integer, as this could lead to precision loss.