开发者

optimizing with IEEE floating point - guaranteed mathematical identities?

开发者 https://www.devze.com 2022-12-15 13:10 出处:网络
I am having some trouble with IEEE floating point rules preventing compiler optimizations that seem obvious.For example,

I am having some trouble with IEEE floating point rules preventing compiler optimizations that seem obvious. For example,

char foo(float x) {
    if (x == x) 
       开发者_如何学运维return 1;
    else 
       return 0; 
}

cannot be optimized to just return 1 because NaN == NaN is false. Okay, fine, I guess.

However, I want to write such that the optimizer can actually fix stuff up for me. Are there mathematical identities that hold for all floats? For example, I would be willing to write !(x - x) if it meant the compiler could assume that it held all the time (though that also isn't the case).

I see some reference to such identities on the web, for example here, but I haven't found any organized information, including in a light scan of the IEEE 754 standard.

It'd also be fine if I could get the optimizer to assume isnormal(x) without generating additional code (in gcc or clang).

Clearly I'm not actually going to write (x == x) in my source code, but I have a function that's designed for inlining. The function may be declared as foo(float x, float y), but often x is 0, or y is 0, or x and y are both z, etc. The floats represent onscreen geometric coordinates. These are all cases where if I were coding by hand without use of the function I'd never distinguish between 0 and (x - x), I'd just hand-optimize stupid stuff away. So, I really don't care about the IEEE rules in what the compiler does after inlining my function, and I'd just as soon have the compiler ignore them. Rounding differences are also not very important since we're basically doing onscreen drawing.

I don't think -ffast-math is an option for me, because the function appears in a header file, and it is not appropriate that the .c files that use the function compile with -ffast-math.


Another reference that might be of some use for you is a really nice article on floating-point optimization in Game Programming Gems volume 2, by Yossarian King. You can read the article here. It discusses the IEEE format in quite detail, taking into account implementations and architecture, and provides many optimization tricks.


I think that you are always going to struggle to make computer floating-point-number arithmetic behave like mathematical real-number arithmetic, and suggest that you don't for any reason. I suggest that you are making a type error trying to compare the equality of 2 fp numbers. Since fp numbers are, in the overwhelming majority, approximations, you should accept this and use approximate-equality as your test.

Computer integers exist for equality testing of numerical values.

Well, that's what I think, you go ahead and fight the machine (well, all the machines actually) if you wish.

Now, to answer some parts of your question:

-- for every mathematical identity you are familiar with from real-number arithmetic, there are counter examples in the domain of floating-point numbers, whether IEEE or otherwise;

-- 'clever' programming almost always makes it more difficult for a compiler to optimise code than straightforward programming;

-- it seems that you are doing some graphics programming: in the end the coordinates of points in your conceptual space are going to be mapped to pixels on a screen; pixels always have integer coordinates; your translation from conceptual space to screen space defines your approximate-equality function

Regards

Mark


If you can assume that floating-point numbers used in this module will not be Inf/NaN, you can compile it with -ffinite-math-only (in GCC). This may "improve" the codegen for examples like the one you posted.


You could compare for bitwise equality. Although you might get bitten for some values that are equivalent but bitwise different, it will catch all those cases where you have a true equality as you mentioned. And I am not sure the compiler will recognize what you do and remove it when inlining (which I believe is what you are after), but that can easily be checked.


What happened when you tried it the obvious way and profiled it? or examined the generated asm?

If the function is inlined with values known at the call site, the optimizer has this information available. For example: foo(0, y).

You may be surprised at the work you don't have to do, but at the very least profiling or looking at what the compiler actually does with the code will give you more information and help you figure out where to proceed next.

That said, if you know certain things that the optimizer can't figure out itself, you can write multiple versions of the function, and specify the one you want to call. This is something of a hassle, but at least with inline functions they will all be specified together in one header. It's also quite a bit easier than the next step, which is using inline asm to do exactly what you want.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号