---
title: 'What Actually Runs When You Call Math.sin(). Java source, fdlibm, and what HotSpot does instead'
url: 'https://tutego.de/blog/javainsel/2026/08/what-actually-runs-when-you-call-math-sin-java-source-fdlibm-and-what-hotspot-does-instead'
markdown: 'https://tutego.de/blog/javainsel/2026/08/what-actually-runs-when-you-call-math-sin-java-source-fdlibm-and-what-hotspot-does-instead.md'
date: '2026-08-06'
description: 'Not every operation in a Java program has a corresponding machine instruction. Some operations can be lowered directly to hardware, while others require a software algorithm. For library methods, the API contract determines which results are acceptable, and that contract in turn determines which im…'
taxonomy:
  category:
    - Allgemein
---

# What Actually Runs When You Call Math.sin(). Java source, fdlibm, and what HotSpot does instead

![Illustration einer tropischen Insel mit Palmen und einem hohen Berg](https://tutego.de/blog/javainsel/user/themes/tutego/images/insel-cover.jpg)# Java Blog für Programmierer

[Java Blog für Programmierer](https://tutego.de/blog/javainsel/)

## What Actually Runs When You Call Math.sin(). Java source, fdlibm, and what HotSpot does instead

06.08.2026 · [Christian Ullenboom](https://tutego.de/blog/javainsel/author/admin/)

Not every operation in a Java program has a corresponding machine instruction. Some operations can be lowered directly to hardware, while others require a software algorithm. For library methods, the API contract determines which results are acceptable, and that contract in turn determines which implementation substitutions the runtime may perform.

#### 1.2.1. Machine Support and Software Algorithms

Programmers are used to thinking of arithmetic operations as processor operations. For basic floating-point arithmetic, that model largely holds: addition, subtraction, multiplication, and division map directly to machine instructions. Square root is the clearest further example.

Transcendental functions break the model. Sine, cosine, exponential, logarithm, and general power are normally computed by software algorithms assembled from ordinary arithmetic. Those algorithms perform argument reduction, evaluate polynomial or table approximations, reconstruct the result, and handle special values such as NaN, infinities, and signed zero.

**Historical `Exception`: x87**

x86 still includes historical x87 instructions for transcendental functions, among them `FSIN`, `FCOS`, `FPTAN`, and the logarithm instruction `FYL2X`. Modern compilers and math libraries rarely use them.

The x87 instruction set relies on an outdated stack-based register model and processes one value at a time. Its transcendental instructions also have limited accuracy. `FSIN`, for example, reduces its argument using a 66-bit approximation of π, so its precision deteriorates for large arguments and for values close to a multiple of π.

Current implementations therefore use software algorithms built from ordinary floating-point instructions rather than the x87 transcendental instructions.

On current instruction sets, the operations relevant here fall into three groups.

*Table 2. Machine support for different operations*

| Operation | Typical machine support | How the result is obtained |
|---|---|---|
| `+`, `-`, `*`, `/` on `float` and `double` | Direct | One instruction per operation, with the rounding required by Java |
| Square root (`sqrt`) | Direct | One correctly rounded square-root instruction |
| Fused multiply-add (`fma`) | Architecture-dependent | One instruction with a single final rounding where the processor provides FMA; otherwise a software fallback |
| Rounding operations (`rint`, `floor`, `ceil`) | Architecture-dependent | Typically lowered to one or a small number of instructions on current instruction sets |
| Exponential and logarithmic functions (`exp`, `log`, `log10`) | No generally used direct instruction | Argument reduction followed by polynomial or table approximation |
| General power (`pow`) | No generally used direct instruction | Special-case handling followed by logarithm and exponential computation |
| Sine, cosine, and tangent (`sin`, `cos`, `tan`) | No generally used direct instruction | Argument reduction followed by polynomial approximation |
| Inverse trigonometric functions (`asin`, `acos`, `atan`, `atan2`) | No generally used direct instruction | Software algorithm throughout |
| Hyperbolic functions (`sinh`, `cosh`, `tanh`) | No generally used direct instruction | Software algorithms built largely on the exponential function |
| Cube root and hypotenuse (`cbrt`, `hypot`) | No generally used direct instruction | Software algorithms; `hypot(…)` scales its arguments to avoid intermediate overflow |

The processor is not short of specialized instructions; they simply apply to different operations. Methods such as `Integer.bitCount(…​)`, `numberOfLeadingZeros(…​)`, and `reverseBytes(…​)` can each lower to a single instruction, while `Math.sin(…​)` remains a library operation.

That difference has a cost. A floating-point multiplication takes only a few cycles, whereas a transcendental function usually requires many instructions. `Math.sin(…​)` becomes more expensive still when a large argument requires the slow path of argument reduction. A machine instruction is therefore only one possible implementation mechanism. Where no suitable instruction exists, an implementation must provide an algorithm. Where an instruction does exist, the runtime may still need a fallback for platforms that do not provide it. The cost of software algorithms also gives the runtime an incentive to replace a default implementation with a faster one, but only as far as the contract of the method allows.

#### 1.2.2. Contracts: `Math` and `StrictMath`

Machine support does not determine the API contract. For the numerical library methods discussed here, `Math` and `StrictMath` define two different contractual models. Java once distinguished between strict and non-strict evaluation of floating-point expressions. JEP 306\[[1](https://file+.vscode-resource.vscode-cdn.net/c:/Users/christian/Dropbox/Eigene_Dokumente/VectorAPI/30_lane-wise_unary-binary-ternary.adoc#_footnotedef_1 "View footnote.")\] removed that distinction in Java 17. Floating-point expressions are now always evaluated strictly, and the `strictfp` modifier has become a no-op for which `javac` issues a warning.

This change did not make `Math` and `StrictMath` equivalent. The two mechanisms address different concerns:

- `strictfp` governed the evaluation of floating-point expressions.
- `StrictMath` governs the results returned by selected library methods.

##### 1.2.2.1. `StrictMath`: A Specified Result

For a defined set of functions, `StrictMath` requires the result produced by the specified **fdlibm** algorithm when executed according to Java’s arithmetic rules. The name fdlibm stands for **Freely Distributable Math Library**, a collection of algorithms for IEEE 754 floating-point arithmetic developed at Sun in the 1990s.

For the affected `StrictMath` methods, the relationship to fdlibm is not an OpenJDK implementation choice. The class documentation says so outright, and pins the binding down in remarkable detail, right down to the file naming convention of the C sources.

> To help ensure portability of Java programs, the definitions of some of the numeric functions in this package **require** that they produce the same results as certain published algorithms. These algorithms are available from the well-known network library `netlib` as the package „Freely Distributable Math Library,“ `fdlibm`. These algorithms, which are written in the C programming language, are then to be understood to be transliterated into Java and executed with all floating-point and integer operations following the rules of Java arithmetic. The following transformations are used in the transliteration: \[…\]
> 
> The Java math library is defined with respect to `fdlibm` version 5.3 with a fix to `pow` so that its error bounds conform to the quality of implementation criteria for `pow`. Where `fdlibm` provides more than one definition for a function (such as `acos`), use the „IEEE 754 core function“ version (residing in a file whose name begins with the letter `e`). The methods which require `fdlibm` semantics are `sin`, `cos`, `tan`, `asin`, `acos`, `atan`, `exp`, `log`, `log10`, `cbrt`, `atan2`, `pow`, `sinh`, `cosh`, `tanh`, `asinh`, `acosh`, `atanh`, `hypot`, `expm1`, and `log1p`.

— Java SE 27 and JDK 27 Early-Access API Specification[`java.lang.StrictMath`](https://download.java.net/java/early_access/jdk27/docs/api/java.base/java/lang/StrictMath.html "https://download.java.net/java/early_access/jdk27/docs/api/java.base/java/lang/StrictMath.html")The API specification is one of the three documents that make up the Java SE platform specification, alongside the language specification and the virtual machine specification. The fdlibm requirement therefore binds every conforming implementation.

What is required is a result, not a construction. The documentation describes the C algorithms as transliterated into Java and executed under Java’s arithmetic rules. This constrains what leaves the method without prescribing how a particular implementation must be organized internally. OpenJDK implements this contract through Java ports in the package-private `java.lang.FdLibm` class. A call such as `StrictMath.sin(…​)` therefore follows this default source-level path:

```java
public static double sin(double a) {
    return FdLibm.Sin.compute(a);
}
```

**History**

In older JDK releases, several `StrictMath` methods were declared `native` and eventually called fdlibm routines written in C. The migration away from the native implementation happened gradually, with individual algorithms translated from C to Java over several releases. The transcendental functions followed in JDK 21. The class documentation tracks the change: where it once described the C algorithms as executed under Java’s floating-point rules, it now describes them as transliterated into Java.

The Java ports remain closely based on fdlibm 5.3. The original library is no longer maintained as an independent project, but descendants of its code survive in FreeBSD’s msun, musl libc, Julia’s openlibm, and the Go standard library.

A virtual machine may compile or inline this code, but it may not substitute an algorithm that produces a different result.

`StrictMath` therefore guarantees reproducibility of the specified result. It does not necessarily guarantee a result that is closer to the exact mathematical value than the result returned by `Math`.

##### 1.2.2.2. `Math`: Constrained Implementation Freedom

For many transcendental functions, `Math` specifies an error bound and, where applicable, monotonicity requirements. It does not require the fdlibm result. IEEE 754-2019 defines functions such as sine, cosine, exponential, logarithm, and power as recommended operations with correctly rounded semantics, but it does not require every programming environment to provide them. Java generally specifies weaker, method-specific guarantees for these functions.\[[2](https://file+.vscode-resource.vscode-cdn.net/c:/Users/christian/Dropbox/Eigene_Dokumente/VectorAPI/30_lane-wise_unary-binary-ternary.adoc#_footnotedef_2 "View footnote.")\]

The Java source implementation commonly delegates to `StrictMath`:

```java
@IntrinsicCandidate
public static double sin(double a) {
    return StrictMath.sin(a);
}
```

This delegation defines the default implementation. It does not restrict the runtime to that implementation. A virtual machine may substitute another algorithm whenever the replacement continues to satisfy the `Math` contract.

Square root is the important exception. IEEE 754 requires square root to be correctly rounded. For a given input and rounding mode, there is exactly one admissible floating-point result. Java reflects that requirement in the contract of `Math.sqrt(…​)`: the result must be correctly rounded rather than merely remain within an ulp bound. `Math.sqrt(…​)` and `StrictMath.sqrt(…​)` therefore return the same result. The documented list of methods bound to fdlibm semantics shows the same fact from the other side: `sqrt` does not appear on it.

For functions such as `sin(…​)`, `cos(…​)`, and `pow(…​)`, no comparable requirement eliminates the implementation freedom.

#### 1.2.3. Runtime Implementation

A Java method body describes the default source-level path, but not necessarily the code executed after compilation. As discussed earlier, HotSpot may recognize selected methods as intrinsics and replace their Java-level invocation during JIT compilation. The source code extract above shows that `Math.sin(…​)` is marked with `@IntrinsicCandidate`; whether HotSpot actually provides an intrinsic for the method is determined by its internal table of known intrinsics. For a supported call, the JIT compiler may emit compiler-generated code or invoke a runtime stub instead of following the Java-level path.

HotSpot provides an intrinsic for `Math.sin(…​)`, but not for `StrictMath.sin(…​)`. For `Math.sin(…​)`, HotSpot may therefore replace the default delegation path with a runtime stub that implements the function using platform-specific instruction sequences. On x86-64, the relevant stubs are based on routines contributed from Intel’s scalar **Compiler Math Library (LIBM)**\[[3](https://file+.vscode-resource.vscode-cdn.net/c:/Users/christian/Dropbox/Eigene_Dokumente/VectorAPI/30_lane-wise_unary-binary-ternary.adoc#_footnotedef_3 "View footnote.")\] and implement the function using SSE and AVX instruction sequences. The implementation is incorporated directly into HotSpot; the JVM does not dynamically link against the external Intel library at runtime. This substitution is permitted because `Math.sin(…​)` does not require the fdlibm result. It requires only a result within the specified error bound and with the required monotonicity properties.

A direct call to `StrictMath.sin(…​)`, by contrast, remains bound to the specified result. The JIT compiler may compile and inline `FdLibm.Sin.compute(…​)`, but it may not substitute an algorithm that changes that result.

Square root illustrates how the contract distinction remains separate from the hardware/software distinction. Both `Math.sqrt(…​)` and `StrictMath.sqrt(…​)` carry `@IntrinsicCandidate`, and HotSpot defines an intrinsic for each. A hardware square-root instruction and a conforming software implementation must agree bit for bit, so a single `sqrtsd` instruction can replace either method body without weakening either contract.

An intrinsic is therefore not hardware taking over from software. It is one implementation replacing another under the constraints of the API contract. The complete path is therefore:

operation → API contract → permitted implementation → generated machine code

**Choosing Between Math and StrictMath**

`Math` is the default choice. It gives the runtime freedom to select an implementation that satisfies the documented error bounds while taking advantage of platform-specific optimizations.

For methods with specified fdlibm semantics, `StrictMath` is appropriate when results must be reproducible bit for bit across machines, virtual machines, or vendors. Typical cases include deterministic simulations, distributed computations whose nodes must produce identical values, and golden-value tests that run on multiple platforms.

What separates the two classes is implementation freedom on one side and a specified numerical result on the other, not speed on one side and accuracy on the other.

Kategorien: [Allgemein](https://tutego.de/blog/javainsel/category/allgemein/)

[← Alle Beiträge](https://tutego.de/blog/javainsel/)

---

## Navigation

- Previous: [Mein Guava-Buch unter CC BY-SA 4.0 veröffentlicht](https://tutego.de/blog/javainsel/2026/08/mein-guava-buch-unter-cc-by-sa-4-0-veroeffentlicht.md)
