The seconds to calendar date conversion doesn't actually need to know about leap seconds.
At the seconds representation level, the same number is repeated twice (or the smearing trategy happens).
So the times before and after leap second get converted the the same date regardless of whether you know about the leap second or not. Edge case is that once second occuring twice. You might have seen that 23:59:60 seconds timestamp somewhere.
Yes, time() and clock_gettime(CLOCK_REALTIME) results are affected by leap seconds.
New leap second will get to your system through NTP. Sadly NTP only distributes indicator flag that leap second is going to be introduced, but not the offset itself. But the distributed time itself is already affected by leap second, so NTP client doesn't really need to know.
(In contrast the other time sync mechanisms - GPS and PTP - use time scale unaffected by leap seconds and distribute it as an additional information with UTC offset. And it's left on client to modify received time on its end. Kernel has a parameters in clock_adjtime() for leap seconds.)
So if you have a passive system that has NTP client then it's time will change for new leap second on runtime. Linux treats UTC time as the dominant one, so that's the one saved to RTC device and will survive reboot.
There is CLOCK_TAI that sounds like it should return TAI time, but it is such a second class citizen to the point that nothing on regular Linux desktop and server distros even set the offset and it returns the same time as CLOCK_REALTIME.
There is a file in /etc with list of leap seconds that is part of some package, so you need to update the system to update this file. I don't believe traditional NTP software updates this package dynamically. But not many software uses it. If some init service script parsed and set the kernel UTC offset then your system's CLOCK_TAI would be one second late from rest of the world until update. But it doesn't affect UTC time on Linux in any way I know.
Clipboard on Wayland is interesting. Clients expose the content through wl_data_source and see wl_data_offer. To receive the clipboard content, they request receive on a wl_data_offer and pass a file descriptor (usually pipe() write end) to compositor, and then read from it. File descriptor is passed to the appropriate wl_data_source origin which is expected to write the content to it.
Both reads and writes ends take some time, I played with it to see how various programs react to a writer that just hangs or takes a lot of time. If the read is handled synchronously then you can block a UI loop. Some implement timeout and some can be blocked indefinitely.
But handling in asynchronously (either on thread or in nonblocking event loop) isn't an automatic win either. For example the new gnome text editor didn't order other user inputs with clipboard; so you would press Ctrl+V, nothing happened initially, continue writing and suddenly the clipboard content was inserted.
And I don't even remember how the OpenGL hidden window inhibiting worked with wl_data_source. Since the window is stuck in eglSwapBuffers, does it get unblocked to handle the clipboard write?
Surprisingly, even though Gnome compositor doesn't have a clipboard manager, it caches the clipboard content sometimes.
I encountered this few months back. Netcat was recv-only client connected to server that was continually sending some logs. Managed to trigger it with netcat killed with Ctrl+\ signal instead of Ctrl+C, but not consistently. My hypothesis was that it has something to do with unACKed data and/or non-empty incoming queue.
the "XOR in hash functions" mentioned at the end most certainly refers to Zobrist hashing, the actually useful XOR trick.
Requires fixed-length keys. Generate random table for each byte position. Then to hash a key, for each position lookup byte in table and XOR the results.
This is used in chess/go/shogi/... engines because the board/position representation is fixed-length and you can undo modes easily - XOR-out byte from previous state and XOR-in from new state.
Another similar trick - XOR doubly linked list: XOR the prev and next pointers (storaged size of node decreases) and you can recover the values when accessing the node from prev or next side and thus already know one of the addresses.
There is another way to compare floats for rough equality that I haven't seen much explored anywhere: bit-cast to integer, strip few least significant bits and then compare for equality.
This is agnostic to magnitude, unlike epsilon which has to be tuned for range of values you expect to get a meaningful result.
Great reading, thanks. Describes how to handle ±0, works with difference to avoid truncation errors.
First half of the paper is arriving at this correct snippet, second part of the paper is about optimizing it.
bool DawsonCompare(float af, float bf, int maxDiff)
{
int ai = *reinterpret_cast<int*>(&af);
int bi = *reinterpret_cast<int*>(&bf);
if (ai < 0)
ai = 0x80000000 - ai;
if (bi < 0)
bi = 0x80000000 - bi;
int diff = ai - bi;
if (abs(diff) < maxDiff)
return true;
return false;
}
I'm unconvinced. Doesnt this just replace the need to choose a suitable epsilon with the need to choose the right number of bits to strip? With the latter affording much fewer choices for degree of "roughness" than does the former.
Not quite. It's basically a combined mantissa and exponent test, so it can be thought of as functionally equivalent to scaling epsilon by a power of two (the shared exponent of the nearly equal floating point values) and then using that epsilon.
I think I'll just use scaled epsilon... though I've gotten lots of performance wins out of direct bitwise trickery with floats (e.g., fast rounding with mantissa normalization and casting).
This is essentially ULP (units in the last place) comparison, and it's a solid approach. One gotcha: IEEE 754 floats have separate representations for +0 and -0, so values straddling zero (like 1e-45 and -1e-45) will look maximally far apart as integers even though they're nearly equal. You need to handle the sign bit specially.
There's another gotcha. Consider positive, normal x and y where ulp(y) != ulp(x). Bitwise comparison, regardless of tolerance, will consider x to be far from y, even though they might be adjacent numbers, e.g. if y = x+ulp(x) but y is a power of 2.
This case actually works because for finite numbers of a given sign, the integer bit representations are monotonic with the value due to the placement of the exponent and mantissa fields and the implicit mantissa bit. For instance, 1.0 in IEEE float is 0x3F800000, and the next immediate representable value below it 1.0-e is 0x3F7FFFFF.
Signed zero and the sign-magnitude representation is more of an issue, but can be resolved by XORing the sign bit into the mantissa and exponent fields, flipping the negative range. This places -0 adjacent to 0 which is typically enough, and can be fixed up for minimal additional cost (another subtract).
I interpreted OP's "bit-cast to integer, strip few least significant bits and then compare for equality" message as suggesting this kind of comparison (Go):
with the sensitivity controlled by ignoreBits, higher values being less sensitive.
Supposing y is 1.0 and x is the predecessor of 1.0, the smallest value of ignoreBits for which equiv would return true is 24.
But a worst case example is found at the very next power of 2, 2.0 (bitwise 0x40000000), whose predecessor is quite different (bitwise 0x3FFFFFFF). In this case, you'd have to set ignoreBits to 31, and thus equivalence here is no better than checking that the two numbers have the same sign.
Yeah, that's effectively quantization, which will not work for general tolerance checks where you'd convert float similarity to int similarity.
There are cases where the quantization method is useful, hashing/binning floats being an example. Standard similarity checks don't work there because of lack of transitivity. But that's fundamentally a different operation than is-similar.
Sure, but that operator can propagate a carry all the way to the most significant bit, so a check for bitwise equality after "strip[ping] few least significant bits" will yield false in some cases. The pathologically worst case for single precision, for example, is illustrated by the value 2.0 (bitwise 0x40000000) and its predecessor, which differ in all bits except the sign.
Rather than stripping bits, you can just compare if the bit-casted numbers are less than N apart (choose an appropriate N that works for your data; a good starting point is 4).
This breaks down across the positive/negative boundary, but honestly, that's probably a good property. -0.00001 is not all that similar to +0.00001 despite being close on the number line.
It also requires that the inputs are finite (no INF/NAN), unless you are okay saying that FLT_MAX is roughly equal to infinity.
That works well for sorting/bucketing/etc in a few places, but as a comparison it's prone to false negatives (your values are close and computed to not be close), so you're restricted to algorithms tolerant of that behavior.
Similar to make, it does mtime chronological comparison of dependencies with target to determinate if dependencies changed. This is just so flawed and simple to fool by operations on filesystem that do not change mtime (move, rename):
1) pick a source file and make a copy of it for for later
2) edit selected source file and rebuild
3) move the copy to it's original location
4) try to rebuild, nothing happens
I might be missing your sarcasm, but this is a common approach for large scale builds. Virtual filesystems are used to provide a pre-computed tree hash as a xattr. In a more typical case, you can read the git tree hash.
Not sure it was meant as sarcasm really. I just think so many build (and other) problems could have been avoided it a file hash was available on every file by default.
In the current POSIX paradigm yes, it would be expensive. But if the hash was defined as the hash of fixed blocks, it wouldn't be expensive. The raciness depends, a lot, on the semantics we would define. (In the context of a build system, it's no different than that the file could get a new mtime after we read the mtime.)
By not tracking file metadata through an index file, mtime-only incremental build systems trade a lot of reliability for only slightly more simplicity. https://apenwarr.ca/log/20181113
Copy (1) and edit (2) both bump mtime, usually. It's not obvious that in the workflow you describe ninja is problematic, rather than the workflow itself (which is atypical).
Copy and edit do, but move (aka rename) generally does not, and that is the part that is problematic.
I don't think the described sequence of operations is all that unusual. Not the most common case for sure, but hardly unlikely in the grand scheme of things.
ninja fails to detect that file changed from last build - all it's mtime, ctime, inode and size can change, yet it's not detected as long as mtime is not newer than target.
Again, this is just a weird workflow, and you're assuming copy/edit don't bump mtime. That usually isn't the case. If you're doing this weird thing, you can just run `touch` when you move files over existing files like this to explicitly bump mtime.
I run into this issue when building against different environments, each with a
1. A library depends on a system package. To test against the different versions of the system package, the library is compiled within a container.
2. To minimize the incremental rebuild time, the `build` directory is mounted into the build container. Even when using a different version of the system package, this allows re-use of system-independent portions of the build.
3. When switching to a build container with a different version of the system package, the mtime of the system package is that of its compilation, not that of the build container's initialization. Therefore, the library is erroneously considered up-to-date.
Because the mtime is the only field checked to see if the library is up to date, I need to choose between having larger disk footprint (separate `build` directory for each build container), slower builds (touch the system package on entering the container, forcing a rebuild), or less safe incremental builds (shared `build` directory, manually touch files when necessary).
Incorrect, I only assume move/rename of backup to original location doesn't change it's mtime (which it doesn't with default flags or from IDE or file manager). And I don't think this is a weird or obscure workflow, I do it all the time - have two versions of a file or make a backup before some experimental changes, and restore it later.
I used to do that some 20 years ago when I was learning programming, but now it does seem like a weird workflow when git exists and handles this case well.
Good point. I think it would be fixable by using the Change Time instead of the Modify Time, because that changes when moving the copy over the original.
I wouldn't call wayland-client a callback hell. All callbacks are called at expected time when you call wl_display_dispatch() (and its variants) or during wl_display_roundtrip(). GLFW also works with callbacks and nobody complains about that.
There is no async involved so function coloring argument doesn't really apply here.
I don't share author's hate for them, but they are definitely more verbose than popping form event queue and switch statement on event type ala SDL loop. Plenty of callbacks just set parameters in some state struct and do not propagate further. And you need to fill the vtable structs, and register that as listener. This boilerplate is probably the reason why basic window examples have ~200 lines instead of 40. But in larger project this is barely a problem.
Wayland makes it unnecessarily difficult to make simple clients. Gnome still doesn't support server-side window decoration and libdecor is an absolute nightmare and wayland-cursor doesn't even detect the system theme properly.
So the times before and after leap second get converted the the same date regardless of whether you know about the leap second or not. Edge case is that once second occuring twice. You might have seen that 23:59:60 seconds timestamp somewhere.