ACSUtils Wiki

An ACS library for ZDoom-based ports

User Tools

Site Tools



ACSUtils provides a rich math library and covers for several missing ZDoom functions.


What is "num"?

num means “any numeric type” (either int or fixed). Functions accepting num accept both ints and fixeds, e.g:

num max(num a, num b)

translates to:

“The function max accepts two numbers and returns a number. If both arguments are ints, it returns an int, and if both arguments are fixeds, it returns a fixed”.

This allows max to be used both ways:

max(2, 5) == 5
max(2.0, 5.0) == 5.0
Don't mix ints and fixeds when using num. nums in a function signature are either all int or all fixed! E.g., this is incorrect:
max(5, 2.0)

It will return 2.0 because max uses a simple a > b comparison that only works correctly if both numbers are ints or both numbers are fixeds. 2.0 (65536) is greater than 5.

However, not all arguments of a function may be num. Some arguments may be required to be int or fixed, or the function may always return int or fixed:

num OneOfTheArgumentsIsRequiredToBeInt(num a, int b)
fixed ThisFunctionAlwaysReturnsFixed(num a, num b)


All angles throughout ACSUtils are fixed-point angles (turns), and they are allowed to be outside the 0.0..1.0 range. All such cases are handled correctly by both ZDoom and ACSUtils.

Also, ZDoom expects inverted actor pitch (positive = looking down) in GetActorPitch and SetActorPitch. ACSUtils doesn't invert pitch angles, so remember to invert pitch when retrieving it with GetActorPitch and when setting it with SetActorPitch.


ACSUtils provides a set of mathematical constants. All fixed-point constants have a lot of digits after the point to maximize precision. Actually more digits than needed.

Pi and Tau

  • #define PI 3.1415926535897932384626433832795
  • #define TAU 6.2831853071795864769252867665590

The numbers Pi and Tau.


#define MATH_E 2.7182818284590452353602874713526624977572470937

The number e. Called MATH_E to avoid conflicts with variable name e.

Significant square roots and logarithms

Precalculated to maximum precision. The precalculated logarithms are used to implement ln and log10.

  • #define SQRT_2 1.41421356237 – sqrt(2)
  • #define LOG2_E 1.44269504089 – log2(e)
  • #define LOG2_10 3.32192809489 – log2(10)

Generic math functions


num abs(num x)

Returns the absolute value of X:

  • If X is zero or positive, returns X.
  • If X is negative, returns -X.
Beware of using this function with INT_MIN.

abs(INT_MIN) == INT_MIN because -INT_MIN == INT_MIN. This is the only case when abs returns a negative number.


int sgn(int x)

Returns the sign of X:

  • If X is positive, returns 1.
  • If X is negative, returns -1.
  • If X is zero, returns 0.

Example usage

// Make X velocity 15.0, but with the same sign as foo
int xvel = sgn(foo) * 15.0;


num min(num a, num b)

Returns the lesser of two numbers.


num max(num a, num b)

Returns the greater of two numbers.


num clamp(num x, num mininum, num maximum)

Limits X to the specified range:

  • If X < mininum, returns the minumum.
  • If X is between mininum and maximum, returns X.
  • If X > maximum, returns the maximum.


fixed lerp(fixed a, fixed b, fixed alpha)

Linear interpolation function commonly used in game development. Performs linear interpolation (or extrapolation) between A and B and returns the result.

Alpha can be outside of 0.0..1.0 range. In that case, it's linear extrapolation.

example usage

Simple movement animation between two points:

for (int time = 0; time < 1.0; time += 0.05)
    int x = lerp(x1, x2, time); 
    int y = lerp(y1, y2, time);
    DrawSomething(x, y);


fixed IntDiv(int a, int b)

An equivalent of FixedDiv for integers that works for any two integers as long as the result fits into a fixed-point number. Divides integer A by integer B and returns the result as a fixed-point number.

If the result is greater than 32767, the function returns 0.

Can also be used as a more readable alternative to FixedDiv(a<<16, b<<16).


int cmp(num a, num b)

Compares two numbers and returns the result:

  • If A > B, returns 1.
  • If A = B, returns 0.
  • If A < B, returns -1.


fixed fract(fixed x)

Returns the fractional part of X, e.g fract(12.345) == 0.345.

The result is not affected by the sign of the number, e.g. fract(-12.345) == 0.345. The result is always between 0.0 and 1.0.


int ipow(int x, int y)

Returns xy. Uses a simple multiplication loop.


fixed fpow(fixed x, fixed y)

returns xy. Supports powers below 1.0. Uses a simple loop with FixedMul or FixedDiv, depending on whether the power is below 1.0.


ACSUtils provides a full set of trigonometric functions:

  • sin is already built into ZDoom.
  • cos is already built into ZDoom.
  • fixed tan(fixed angle) – implemented as FixedDiv(sin(x), cos(x)).
  • fixed cot(fixed angle) – implemented as FixedDiv(cos(x), sin(x)).
  • fixed sec(fixed angle) – implemented as FixedDiv(1.0, sin(x)).
  • fixed cosec(fixed angle) – implemented as FixedDiv(1.0, cos(x)).

Inverse trigonometric functions:

  • fixed atan(fixed x) – implemented as VectorAngle(1.0, x).
  • fixed acot(fixed x) – implemented as 0.25 - atan(x).
  • fixed asin(fixed x) – implemented as atan(FixedDiv(x, FixedSqrt(1.0 - FixedMul(x, x)))).
  • fixed acos(fixed x) – implemented as ang(2 * atan(FixedSqrt(FixedDiv(1.0 - x, 1.0 + x)))).
  • fixed asec(fixed x) – implemented as acos(FixedDiv(1.0, x)).
  • fixed acosec(fixed x) – implemented as asin(FixedDiv(1.0, x)).

Vector math

Vector math functions come in 2D and 3D versions. All vector math functions operate only on fixed-point vectors. No integer versions are available.

Vectors are passed as arguments named x, y, z.

If a function returns a vector, it uses Multiple return values, returning vector components in the same XYZ order, so r1 is X, r2 is Y and r3 is Z.

If a function accepts more than one vector, the first vector is passed as x1, y1, z1, and the second one as x2, y2, z2.


  • 2D: VectorLength – already built into ZDoom.
  • 3D: fixed VectorLength3D(fixed x, fixed y, fixed Z)

These functions return the length of the input vector. VectorLength3D is implemented efficiently as VectorLength(VectorLength(x, y), z).

Dot product

  • 2D: fixed dot2(fixed x1, fixed y1, fixed x2, fixed y2)
  • 3D: fixed dot3(fixed x1, fixed y1, fixed z1, fixed x2, fixed y2, fixed z2)

These functions return the sum of components of a vector obtained by componentwise multiplication of the two input vectors, also known as the dot product. They are shorthands for writing out FixedMul(x1, x2) + FixedMul(y1, y2) + ….


  • 2D: fixed, fixed normalize2d(fixed x, fixed y)
  • 3D: fixed, fixed, fixed normalize3d(fixed x, fixed y, fixed z)

These functions return the input vector scaled to have length 1.0, also known as normalizing the input vector, and return the result using Multiple return values.


normalize3d(x, y, z);
x = r1;
y = r2;
z = r2;
normalize2d(x, y);
int normalizedX = r1;
int normalziedY = r2;


fixed, fixed RotateVector(fixed x, fixed y, fixed angle)

Rotates the vector by the angle and returns the result using Multiple return values.

Example usage

RotateVector(x, y, angle);
int newX = r1;
int newY = r2;
// Rotate a point around the origin
// Use RotatePoint instead of this example code
RotateVector(x - originX, y - originY, angle);
int newX = r1 + originX;
int newY = r2 + originY;


fixed, fixed RotateVector(fixed x, fixed y, fixed cos, fixed sin)

A faster version of RotateVector that accepts cos(angle) and sin(angle) instead of computing them.

Example usage

int c = cos(angle);
int s = sin(angle);

// Rotate first vector
RotateVectorCS(x1, y1, c, s);
x1 = r1;
y1 = r2;

// Rotate second vector without recalculating sine and cosine
RotateVectorCS(x2, y2, c, s);
x2 = r1;
y2 = r2;


fixed, fixed RotatePoint(fixed x, fixed y, fixed originX, fixed originY, fixed angle)

Rotates the point around the specified origin by the angle and returns the result using Multiple return values.

Example usage

RotatePoint(x, y, originX, originY, angle);
int newX = r1;
int newY = r2;


  • fixed SqVectorLength(fixed x, fixed y)
  • fixed SqVectorLength3D(fixed x, fixed y, fixed z)

Returns vector length squared.

Because this function returns a fixed, it won't work correctly for vectors longer than 256.0.


fixed, fixed VectorToAngles(fixed x, fixed y, fixed z)

Converts the vector's direction to a pair of angles (angle, pitch) and returns them using Multiple return values.

Remember that ZDoom uses negated pitch angles on actors. ACSUtils functions don't use negated pitches.

Example usage

// This function makes the actor look in the direction
// specified as a 3D vector.
function void LookInDirection(int tid, int x, int y, int z)
    VectorToAngles(x, y, z);
    int angle = r1;
    int pitch = r2;
    SetActorAngle(tid, angle);
    SetActorPitch(tid, -pitch); // SetActorPitch expects negated pitch.


fixed, fixed, fixed AnglesToVector(fixed angle, fixed pitch)

Converts a pair of angles (angle, pitch) to a 3D vector of length 1.0, which points in the same direction, and returns it using Multiple return values.

Remember that ZDoom uses negated pitch angles on actors. ACSUtils functions don't use negated pitches.

Example usage

AnglesToVector(GetActorAngle(0), -GetActorPitch(0)); // Un-negate actor pitch
int dirX = r1;
int dirY = r2;
int dirZ = r3;


Base-2 logarithms are the fundamental algorithm. All other bases are implemented using the property “log_b(x) = log_2(x) / log_2(b) if b > 1”.

All logarithm functions return a fixed, but there are two versions of every logarithm function: one accepting an integer X and one accepting a fixed-point X. The integer-accepting functions have the i prefix.


  • fixed log2(fixed x)
  • fixed ilog2(int x)

Returns the base-2 logarithm of X.


  • fixed log10(fixed x)
  • fixed ilog10(int x)

Returns the base-10 logarithm of X. Implemented by dividing log2(X) by the precalculated constant LOG2_10.


  • fixed ln(fixed x)
  • fixed iln(int x)

Returns the natual (base-e) logarithm of X. Implemented by dividing log2(X) by the precalculated constant LOG2_E.


  • fixed logb(fixed x, fixed base)
  • fixed ilogb(int x, fixed base)

Returns an arbitrary-base logarithm of X. Calculates FixedDiv(log2(x), log2(base)).

Only bases higher than 1.0 are supported.
Using these functions with bases very close to 1.0 may produce a result that doesn't fit into a fixed.

Note that even the integer version has a fixed-point base.

math.txt · Last modified: 2019/08/26 21:26 by korshun