Mathematics7 min readExamples run on PHP 8.5.10

atanh()

Learn how PHP 8.4 atanh() returns the inverse hyperbolic tangent of a number, how it behaves at -1, 1 and outside that range, and how to guard it.

Signature

atanh(float $num): float
Available since PHP 4.1.07.48.08.18.28.38.48.5All versions →

Purpose

atanh() returns the inverse hyperbolic tangent of a number. In other words, it finds the value y for which tanh(y) equals the argument you passed in. It undoes tanh() the same way atan() undoes tan().

You will meet it in statistics (the Fisher z-transformation of a correlation coefficient), in signal scaling and in physics formulas that use hyperbolic functions. It is a plain mathematical helper: it has no side effects and always gives the same result for the same input.

Signature

Parameters

  • num: the value to process. The mathematically meaningful domain is the open interval from -1 to 1. The parameter has no default, so you must always supply it.

Because the parameter is typed float, an integer such as 0 is accepted and converted. A non-numeric string such as "abc" throws a TypeError. In PHP's default coercive mode a numeric string like "0.5" is converted to a float.

Return value and failure cases

The function always returns a float. It does not return false and does not throw for numbers that are out of range. Instead, the result follows floating-point conventions:

  • For a value strictly between -1 and 1, you get a finite float. The sign matches the sign of the input, and atanh(0) is zero.
  • For exactly 1, the result is positive infinity (INF). For exactly -1, it is negative infinity.
  • For any value with an absolute value greater than 1, the result is NAN.

Check for these cases with is_infinite(), is_nan() or is_finite(). Never compare against NAN with ==, because NAN is not equal to anything, including itself.

Example 1: basic values

The function grows slowly near zero and rises steeply as the input approaches 1. Rounding keeps the printed output stable and readable.

Example
<?php
$inputs = [0.5, -0.5, 0.9];
$results = array_map(fn(float $x): float => round(atanh($x), 6), $inputs);
echo json_encode($results);
Output
[0.549306,-0.549306,1.472219]

The result is symmetric: atanh(-x) is the negative of atanh(x). The jump from 0.5 to 0.9 is bigger than the jump from 0 to 0.5, which shows how steep the curve gets near 1.

Example 2: boundaries and out-of-range input

The edges of the domain do not raise errors, so you need to detect them yourself.

Example
<?php
$plusOne = atanh(1.0);
$minusOne = atanh(-1.0);
$outside = atanh(1.5);

echo json_encode([
    'plusOneIsPositiveInfinity' => is_infinite($plusOne) && $plusOne > 0,
    'minusOneIsNegativeInfinity' => is_infinite($minusOne) && $minusOne < 0,
    'outsideIsNan' => is_nan($outside),
]);
Output
{"plusOneIsPositiveInfinity":true,"minusOneIsNegativeInfinity":true,"outsideIsNan":true}

This is the most important behavior to remember. Infinity and NAN can quietly flow into later calculations and spoil totals or averages.

Example 3: a guarded Fisher z-transform

Statisticians often convert a correlation coefficient r with atanh(r). Since a correlation of exactly 1 or -1 would produce infinity, a small wrapper can return null for values that cannot be transformed.

Example
<?php
function fisherZ(float $r): ?float
{
    if ($r <= -1.0 || $r >= 1.0) {
        return null;
    }
    return round(atanh($r), 4);
}

echo json_encode([fisherZ(0.25), fisherZ(0.8), fisherZ(1.0)]);
Output
[0.2554,1.0986,null]

Returning null makes the invalid case explicit, and it is easy to encode as JSON or test for later.

Edge cases

  • Precision: results are ordinary floats, so avoid comparing them with ==. Round them or compare with a small tolerance.
  • Values very near 1: an input like 0.999999 is valid and gives a large but finite result. Rounding in the input data can push a value to exactly 1 and change the result to infinity.
  • Integers: atanh(1) is the same as atanh(1.0), so integer input at the boundary still gives infinity.
  • Round trip: tanh(atanh($x)) should return approximately $x for finite results, but tiny floating-point differences are normal.

Common mistakes

  1. Assuming out-of-range input throws. It returns NAN instead, so validate user data first.
  2. Testing $result === NAN or $result == NAN. Use is_nan().
  3. Confusing it with atan(). atan() is the inverse of the circular tangent and accepts any real number. atanh() is the inverse of the hyperbolic tangent and has a limited domain.
  4. Passing degrees or percentages. A percentage such as 50 must be converted to 0.5 before use.
  5. Printing raw floats in tests. Long decimal tails can differ in presentation, so round or encode deliberately.
  • tanh() computes the hyperbolic tangent, the function that atanh() inverts.
  • atan() is the inverse circular tangent.
  • asinh() is the inverse hyperbolic sine.
  • acosh() is the inverse hyperbolic cosine.
  • is_nan(), is_infinite() and is_finite() help you check the result.

Recap

atanh(float $num): float returns the number whose hyperbolic tangent is $num. It is well defined for values strictly between -1 and 1, returns INF or -INF at the endpoints, and returns NAN outside the range. Guard your inputs, check results with the float-inspection functions, and round before displaying or comparing.

Tested examples

Basic values rounded for stable output
<?php
$inputs = [0.5, -0.5, 0.9];
$results = array_map(fn(float $x): float => round(atanh($x), 6), $inputs);
echo json_encode($results);
Output
[0.549306,-0.549306,1.472219]
Boundaries and out-of-range input
<?php
$plusOne = atanh(1.0);
$minusOne = atanh(-1.0);
$outside = atanh(1.5);

echo json_encode([
    'plusOneIsPositiveInfinity' => is_infinite($plusOne) && $plusOne > 0,
    'minusOneIsNegativeInfinity' => is_infinite($minusOne) && $minusOne < 0,
    'outsideIsNan' => is_nan($outside),
]);
Output
{"plusOneIsPositiveInfinity":true,"minusOneIsNegativeInfinity":true,"outsideIsNan":true}
A guarded Fisher z-transform
<?php
function fisherZ(float $r): ?float
{
    if ($r <= -1.0 || $r >= 1.0) {
        return null;
    }
    return round(atanh($r), 4);
}

echo json_encode([fisherZ(0.25), fisherZ(0.8), fisherZ(1.0)]);
Output
[0.2554,1.0986,null]