Figuring out round, floor and ceil with integer division
blog.pkh.me
blog.pkh.me
This is indeed clearly documented [1], I guess I never looked closely enough. I found some discussion on the dev-python list [2] which shows at least I'm not the only one surprised by this!
Well that and it returns an integer when rounding to 0 digits, but that’s less of an issue since the langage also switched to “true division”.
That… is exactly the behaviour python implements, and what GP was surprised by…
It is also, incidentally, the IEEE 754 recommendation.
Python 2 used to round away from 0.
> But I know the regulator will expect round(0.5) = 1, not 0.
That's not necessarily the most helpful, as there's at least two tie-breakings which provide that, but differ in their treatment of negative numbers (round up or round to +∞, and round away from zero or round to ∞).
So, consider the source of the complaint.
So, for example what happens if you divide -1 by -INT_MIN? As you probably know, Abs(INT_MIN) is larger than INT_MAX, and so it is not possible to perform -1 / INT_MIN, as well as -1LL / INT64_MIN. It will crash your program, your emulator/sandbox and your server. So, be careful!
Edit: Actually further tests with INT_MIN / - 1 did lead to a crash. Maybe the first result was due to constant folding or something.
int divF(int a, int b) { return a / b - (a % b < 0); }
int divC(int a, int b) { return a / b + (a % b > 0); }
They should be faster as it avoids branching: https://godbolt.org/z/qrraj8s6jFWIW, the final version also suffers from integer overflow limitations. If the difference between an and INT_MIN/INT_MAX (depending on whether you floor or ceil) is <= b/2, you will have integer overflow.
Modern CPUs, and most programming languages, guarantee bit-exactness for floats most of the times, because they implement IEEE 754.
Relatively easy to break (approximate instructions like rsqrtps, FMA instructions, compiler switches like -ffast-math or /fp:fast, MXCSR rounding modes) but I think fixing bugs is probably easier than refactoring everything from FP into fixed-point.
BTW, multiplication/division/rounding instructions are much faster for floats. I think a fixed-point version will only be a performance win when the integers are 8 or 16 bits wide, and the algorithm is vectorizable.
This can be terribly hard to manage in tests if you don't have a threshold infrastructure in place (which is hard and sometimes impossible to setup if you don't want to preserve huge references).
Also, and this is a bit off topic, even if I remained in the float domain for my use case, I would have to re-implement the lib math routines anyway (in my case cbrt and pow) because they are not implemented the same in all the libc, making them again victim to non determinism.
So far I observed better performance with integer arithmetic anyway, but that's not the reason that motivated me.
Also note that there are other situations were you may want to keep integers; typically in the kernel, or on arch were the floating point is not optimal or even available.
That article was written in 2013. A decade has passed, and in modern world these situations became rather rare.
x87 FPU is deprecated and not used by 64-bit programs. Returns values are passed in XMM0 register. Temporary values are either FP32 or FP64. Modern C++ compilers evaluate FP expression from left to right, and they don't use FMA unless explicitly allowed with -ffast-math or /fp:fast compiler switch. Programmers rarely using estimate instructions because modern CPUs complete precise division and square root in 10-15 cycles, little profit from the faster approximations.
The only variable thing which remains is MXCSR register in the thread state.
> I would have to re-implement the lib math routines anyway (in my case cbrt and pow)
Or you can copy-paste them from OpenBSD. The license is permissive, they are implemented in C without assembly, and the code quality is pretty good.
Rounding--which btw I prefer to think of as rounding to a decimal place, not rounding to an integer--for example, can introduce biases because there are 10 digits, 0 through 9, but "11 spots on the dial", from round-down-to-0 to round-up-to-0 and therefore 5 is "the exact center" and always rounding 5 in one direction is going to bias your results in one direction, depending on your dataset. The article is breezing past this stuff.
I'm going to keep reading because I'm sure I'll learn some interesting things, but at the end of the first page my skin is crawling because I can see ambiguities in the examples I'm being shown and I keep thinking "wut?".
That is because the author has a very specific goal which they explain upfront, and the exploration is done in furtherance of that goal. It is not a general treaty on rounding.