The Ffast and the Furious
gist.github.com
gist.github.com
https://news.ycombinator.com/item?id=32738206 ("Someone’s Been Messing with My Subnormals", 2 days ago, over 120 comments)
if (x >= nextafterf(0.0, INFINITY)) {
I think the idea here is: test if x is large enough so that the subsequent ceilf() will give you an integer i_x >= 1 (because the actual array index will be PIXBUF_WIDTH - i_x, and the max array index is PIXBUF_WIDTH-1). With FTZ/DAZ, nextafterf(0.0, INFINITY) == 0, which breaks the test.But in this scenario, wouldn't you just test "if (x > 0)", since that implies that ceilf(x) must be >= 1?
Still, I think one of the great wins with denormals is that they make "x == y iff x-y == 0" true; absent denormals you can have two normalized x and y, x != y, such y-x evaluates to 0. So for an example, maybe something that involves y > x and yet ceil(y-x) == 0, which is surprising. The only setting I can think of is something like making a histogram (with flipped indices, like your pixel row) of differences of values, so that ceil(y-x) == 0 leads to an invalid histogram index.
Some values are x=1e-32 and y=1.0000011e-32, which parse (via sscanf) to:
x = 0:00010100:10011111011000100011111 = (-1)^0 * 2^( 20-127) * (1+5222687/2^23)
y = 0:00010100:10011111011000100101110 = (-1)^0 * 2^( 20-127) * (1+5222702/2^23)
and then the denormalized difference d = x-y should be: d = 0:00000000:11110000000000000000000 = (-1)^0 * 2^(1-127) * (0+7864320/2^23)
Having some specific values sidesteps concerns about nextafterf(). In this case I think y is many ulps above x, not just 1.Did I get that right?
It's only later that it gets treated as zero by DAZ: in the FP comparison, and then the call to ceil(), both of which use the FPU.
edit: confirmed, you can see it in action on this gist. When compiled with -ffast-math then the denormal does not compare greater than zero (it is flushed to zero by the comparison), but its bit pattern is integer value 1, which is nextafter(0).
https://gist.github.com/ridiculousfish/bfa7ed6b1d76c114e19b3...
https://gist.github.com/moyix/846afb7fcec7c618a6ef52d93b4ca7...
Your gist demonstrates this: notice in the bottom right that xmm0 contains `v2_int64 = {0x1, 0x0}` so the bottom half containing the result is nonzero. (The top half is zero as it's unused).
The "info" command rounds the value to 0 for compactness, but if you run `p $xmm0.v2_double` you will also see it is nonzero when interpreted as a double (I get 4.94e-324).
A fun aside: that `mulss` instruction you stopped at is just there to raise an underflow FP exception; its result is discarded. https://github.com/bminor/glibc/blob/595c22ecd8e87a27fd19270...
Unfortunately, the committee has never been willing to define behavior for non-standard arithmetic, despite multiple people (Ian Ollmann has suggested it a few times, and others have as well) requesting it over the years.
1. A non-fast-math function calling a fast-math function (or the other way around) should result in the floating point environment to be changed on entering the function, and reset on leaving the function. 2. A function calling an other function of the same kind shouldn't touch the floating point environment. 3. Function pointers to different kind of functions shouldn't be convertible to each other (that is `void ()() __attribute__((fast_math))` and `void ()()` are unrelated types).
As for a compiler flag altering the default "fast_math" or "no_fast_math" attribute, I'm not sure about. It's probably a bad idea. Libraries that want to make this configurable can always do their own macro.
Altering the floating point environment for everybody in a global constructor within the library is just horrible.
You can change floating point modes dynamically, and it's commonly done, and then it doesn't matter if the library was dynamically or statically linked.
Also, what about threads or fibers? I would guess it is thread-local but not fiber-local?
The problem is a constructor in the .so being called as it's loaded. There is no way to guarantee state is safe between calls to functions loaded from shared libraries or when they're linked without expensive checks. The point is that there is state being mutated due to the fact we rely on shared libraries and allow dynamic libraries to mutate that state when they're loaded.
This is not a problem with statically linked libraries. And also note, I made a point of talking about shared libraries - not dynamic linking. They're not equivalent. The issue here is that the libraries are shared and do bad things, not that they're loaded dynamically. It would be fine if the library wasn't shared!