More methods for FastMath

I’ve been indirectly proposed by @normen to contribute to jMonkey. I have my own math library for jMonkey projects, so I thought you could find it useful.

Are you interested in more utility methods for FastMath?

Here are some methods I use :

public static final int SIGN_BIT_MASK = 0x80000000;
public static final int BIT_COUNT_EXCLUDING_SIGN = 31;

/**
 * Indexifies a normal that is either -1, 0 or 1. In other words, negative numbers become 0 and positive numbers, including 0, become 1.
 *
 * @param normal A normal that should be either -1, 0 or 1. If it's smaller than -1, it will treated as -1. If it's bigger than 1, it will be treated as 1.
 *
 * @return An index that is either 0 (negative) or 1 (null or positive normal).
 */
public static int indexifyNormalZeroPositive(int normal) {
    return ~((normal | 1) - 1 >> BIT_COUNT_EXCLUDING_SIGN) & 1;
}

/**
 * Indexifies a normal that is either -1, 0 or 1. In other words, negative numbers become 0, positive numbers become 1 and zero is left unchanged.
 *
 * @param normal A normal that should be either -1, 0 or 1. If it's smaller than -1, it will treated as -1. If it's bigger than 1, it will be treated as 1.
 *
 * @return An index that is either 0 (negative and null normal) or 1 (positive normal).
 */
public static int indexifyNormal(int normal) {
    return ~(normal - 1 >> BIT_COUNT_EXCLUDING_SIGN) & 1;
}

/**
 * Gets the sign of the supplied number. The method being "zero position" means that the sign of zero is 1.
 *
 * @param number The number to get the sign from.
 *
 * @return The number's sign.
 */
public static int getSignZeroPositive(int number) {
    return (number & SIGN_BIT_MASK) >> BIT_COUNT_EXCLUDING_SIGN | 1;
}

/**
 * Gets the negative sign of the supplied number. So, in other words, if the number is negative, -1 is returned but if the number is positive or zero, then zero is returned.
 *
 * @param number The number to get its negative sign.
 *
 * @return -1 if the number is negative, 0 otherwise.
 */
public static int getNegativeSign(int number) {
    return number >> BIT_COUNT_EXCLUDING_SIGN;
}

/**
 * Gets the negative sign of the supplied number. So, in other words, if the number is negative, -1 is returned but if the number is positive or zero, then zero is returned. It
 * does not check if the parameter is NaN.
 *
 * @param number The number to get its negative sign.
 *
 * @return -1 if the number is negative, 0 otherwise.
 */
public static int getNegativeSign(float number) {
    return Float.floatToRawIntBits(number) >> BIT_COUNT_EXCLUDING_SIGN;
}

/**
 * Checks if the supplied number is a power of 2.
 *
 * @param number The number to check against.
 *
 * @return True if the given number is a power of 2, false otherwise.
 */
public static boolean isPowerOfTwo(int number) {
    return number > 0 && ((number & (number - 1)) == 0);
}

/**
 * Cycles between the inclusive minimum and the exclusive maximum.
 *
 * @param min          The inclusive minimum.
 * @param exclusiveMax The exclusive maximum.
 * @param index        The index to use to cycle.
 *
 * @return The cycle index.
 */
public static int cycle(int min, int exclusiveMax, int index) {
    return index >= exclusiveMax ? min : (index < min ? exclusiveMax - 1 : index);
}
1 Like

It’s not clear to me that three bit operations plus a subtraction is any better than one comparison:
normal < 0 ? 0 : normal

It’s not really clear when or why these would be used and certainly it would be a rare case, eh?

Every line of code added to the engine is a line that needs to be maintained in the future. So it should have some reasonable expectation of getting used by more than one or two people, I guess.

I do appreciate that you are trying to contribute, though.

2 Likes
public static boolean isPowerOfTwo(int number) {
    return number > 0 && ((number & (number - 1)) == 0);
}

This method is already present in FastMath.

I’m not quite sure because I haven’t tested it but ifs are not performant by definition. They block the CPU so that it cannot load more instructions until the condition is evaluated

You are right but I find the method cycle is really general.

Thank you for your explanation :slight_smile:

Wooaaaaah, I didn’t know. And it’s pretty the same actually ahah

I’m far from an expert on the matter but I would imagine that the compiler can optimize such a simple if statements out if your code is indeed faster.

Also, even without the compiler being able to do so I don’t think the difference is noticeable in any way during normal use.

Additionally, I’m pretty sure that going into functions also brings a performance cost due to the need of putting a new scope on the stack. Of course, as it is a one liner that needs no new variables it may very well be that the compiler in lines the function or something but in that case you are still relying on the compilers ability to optimize code in order to have any sort of benefit.

This is also not taking into account the CPU’s ability to use branch prediction, which may very well make this whole question of which way is faster pointless.

Isn’t this method some kind of arithmetic module operation? What does it do?

It cycles through an inclusive minimum and exclusive maximum. Actually, looking at it right now, it is optimized for a very specific case : loop through some sort of array with a minimum and maximum, one index at the time. It’s a really old method that I made.

So yeah, never mind :stuck_out_tongue:

Then, for a start, when I’ll see some javadoc that I find not descriptive enough, I’ll improve it and ask for feedback.

1 Like

Except the CPU will evaluate both if branches ahead of time. Heard about the meltdown thing? it’s exploiting this mechanism.

“if” statements are not slow on classic CPU architectures (that’s different on a GPU context though). I’d be surprised if a microbench results end up in favor of bit shift operations…

In a video game 95% of the time your CPU will be waiting for your GPU to finish to render a frame. You’re not going to have any gain from the 0.0001 nanosec you save using a bit shift instead of a if statement.

Also some of these methods are redundant with built in Java methods. getNegativeSign for example is basically Math.signum without the checks. It may be slightly faster, but again… what’s the global gain?

The Cycle method is basically a modulo operation, something like → (index + 1) % (exclusiveMax - min) + min

Java has plenty of tools, it’s nice to be aware of them to not reinvent the wheel. Also a lot of very talented coders implemented and optimized java, and, even if there can be exceptions, most of the time a built in method will be faster or safer to use than anything you can think of.

A last word on FastMath. This class is very badly named, it’s not specially faster than the Math class, it just gives you convenient math operations on floats instead of doubles, and contains additional math methods that are not present in Math.

I realize that sound a lot dismissive, and I don’t want do discourage you from contributing to the engine. But you’ll find we are very picky to what’s added to core central classes usually, as Paul stated.

3 Likes

This presumes that index will never change by more than one, eh? I think that’s what you meant by “optimized”… but I suspect it will perform “not at all” when your index is increasing by two or more.

…it’s also just a modulo operation with a bounds, right?

int i = (index - min) % (exclusiveMax - min);
return i < 0 ? exclusiveMax - i : min + i;

…or something to that effect. And that should deal with any index.

It’s still a relatively niche use-case. I can’t think of a case where I’d actually use it and I do all kinds of strange array stuff. It’s possible that you are predisposed to a certain set of idioms that leads you into this need.

perhaps it’s worth adding these to a new wiki page with extra math functions ?

Do you know of javas logical right shift?

public static int indexifyNormalZeroPositive(int normal) {
    return (normal >>> BIT_COUNT_EXCLUDING_SIGN) ^ 1;
}

Yeah, sorry about that :sweat_smile: I thought I could contribute by giving a part of my math class and I picked the most generalized stuff. This method was made two years ago when I hadn’t even finished college yet, so by responding to @aegroto, I realized to was a shitty method.

That’s why I said :

See this video to see when I created the mentioned method :

Yes, I know about it and XOR, however I’m still not fluent enough with bitwise operations to use them naturally. If it works, then thank you for improving my code!

Anyway, thank you everyone for your answers.