-
-
Notifications
You must be signed in to change notification settings - Fork 1k
weather: Fix incorrect rounding for negative temperatures #2194
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
Conversation
|
Build size and comparison to main:
|
|
Would it be worth extracting out rounding integer division to some shared code? It's somewhat annoying that this isn't part of the C++ stdlib really :( Maybe an implementation similar to this? https://github.com/lucianpls/Rounding-Integer-Division |
|
What do you think of this? I've made a RoundedDiv function in the Utility namespace, that uses C++20 concepts so that we can have keep the code type-agnostic, while making sure that it only works for expected values. Didn't you also use a rounded division somewhere in the AoD code? Would this solution also work there? |
|
Wow that looks great. I didn't know that concepts allow this, I should read up on it. Yes I think this would work for AOD, I guess that would go in a separate PR though? I want to double check correctness properly before approving, but otherwise LGTM |
mark9064
left a comment
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 think this doesn't work as C++ demotes the dividend from signed to unsigned when the divisor is unsigned:
Suppose the following
int a;
// -1 / 5, rounded
a = (-1 + (-1 >= 0 ? 5 : -5) / 2) / 5;
// a = 0 as expected
a = (-1 + (-1 >= 0 ? 5U : -5U) / 2) / 5U;
// uh oh, a = 429496728
// more simply
a = -1 / 2u;
// a = 2147483647I didn't even know this was a thing??
|
Oh yeah of course. Integer promotion rules are really stupid. This conversion to unsigned also only happens when the values are |
of course... 😅 I love the C++ spec too |
|
I was curious about this piece of c++ code so I tried to check how it works, but It turned out it doesn't worked as I expected: It looks like, you cannot use unsigned (i.e. 100u) as divisor, because My knowledge of modern c++ is limited. It started to work when I have replaced and used 100 instead of 100u in those two lines |
Make weather service use it.
4a13235 to
635aa0c
Compare
|
@vkareh thanks for taking a look at this! The whole expression gets "promoted" to an unsigned integer if any of the terms are unsigned (and an |
| // returns the arcsin of `arg`. asin(-32767) = -90, asin(32767) = 90 | ||
| int16_t Asin(int16_t arg); | ||
|
|
||
| static constexpr auto RoundedDiv(std::integral auto dividend, std::integral auto divisor) -> decltype(dividend / divisor) { |
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 think we should note the overflow properties in a docstring. abs(dividend) + abs(divisor) / 2 must be less than the type maximum I think? Also I think it would be useful to note whether both the dividend and divisor can be negative
| // returns the arcsin of `arg`. asin(-32767) = -90, asin(32767) = 90 | ||
| int16_t Asin(int16_t arg); | ||
|
|
||
| static constexpr auto RoundedDiv(std::integral auto dividend, std::integral auto divisor) -> decltype(dividend / divisor) { |
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 think adding a docstring/comment what we expect from this function would be helpful. And as @mark9064 said what are the constraints on the inputs (if any)
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 all for ? operators but where things are strictly math based...might as well write using strictly math.
int16_t fixed_rounding(int16_t value, int16_t divisor) {
int16_t signed_value = ((uint16_t)value >> 14u) & 0x2; //if signed 2 otherwise 0
int16_t half_divisor = (divisor / 2);
//add a half, subtract divisor conditionally on sign
return (value + half_divisor - half_divisor*signed_value) / divisor;
}
//same as above except we don't calculate divisor*
int16_t fixed_rounding_b(int16_t value, int16_t divisor) {
int16_t signed_value = (uint16_t)value >> 15u; //if signed 1 otherwise 0
int16_t half_divisor = (divisor / 2);
return (value + half_divisor - divisor * signed_value) / divisor;
}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.
Or...if you trust bithacky madness, no * needed.
int16_t fixed_rounding(int16_t value, int16_t divisor) {
int16_t signed_value = value >> 15u; // true evil: we use >>'s signed behavior to propagate the leading 1 across all bits, eg: we have 0xffff or 0x0000
int16_t half_divisor = (divisor / 2);
return (value + half_divisor - (divisor & signed_value)) / divisor; //we replace the * by "1" with an &
}|
How about these? https://github.com/lucianpls/rounding_integer_division (LOL, just realised I proposed this right at the start and then forgot about it, sorry for the double post) // Round half toward zero integer division
// If T signed, divisor cannot be std::numeric_limits<T>::min()
// Adapted from https://github.com/lucianpls/rounding_integer_division
// Under the MIT license
template<std::integral T>
T RoundedDiv(T dividend, T divisor) {
bool neg = divisor < 0;
if (neg) {
// overflows if divisor is minimum value for T
divisor = -divisor;
}
T m = dividend % divisor;
T h = divisor / 2;
T res = dividend / divisor + (!(dividend < 0) & (m > h)) - ((dividend < 0) & ((m + h) < 0));
if (neg) {
res = -res;
}
return res;
}Should work for everything other than dividing with the negative type minimum, which would be a totally fine edge case IMO |
|
Closing since #2390 merged :) |
Thanks @tokiwee for reporting this issue!
Closes #2185 (if GitHub un-deletes it).