Skip to content

Built-in E4M3 format: largest finite value is 480, but OCP OFP8 E4M3's is 448 #14

Open

Description

@sw23

This is bug 3 of 3 identified by comparing floating point number conversion results across a few open-source implementations. The implementation appears to have missed the NaN encoding for FP8 E4M3 so produces a value outside of the supported range. Feel free to cancel if this is expected/intended.

Below is the full AI-generated summary:

The README says the built-in E4M3 "is consistent with the homonymous format in the December 2023 revision of the OCP 8-bit Floating Point Specification (OFP8)", and emax = 8 with no infinities matches it. But OCP E4M3 also reserves S.1111.111 for NaN, so its largest finite value is 1.75·2⁸ = 448, not 1.875·2⁸ = 480. CPFloat computes xmax = 2^emax · (2 − 2^(1−p)) = 480, so it produces a value that doesn't exist in the format and overflows too late:

#include <math.h>
#include <stdio.h>
#include <string.h>
#include "cpfloat_binary64.h"

static double round_to(const char *format, cpfloat_saturation_t sat, double x) {
  optstruct *o = init_optstruct();
  strcpy(o->format, format);
  cpfloat_populate_optstruct_from_format(o);
  o->round = CPFLOAT_RND_NE;
  o->saturation = sat;
  double y;
  cpfloat(&y, &x, 1, o);
  free_optstruct(o);
  return y;
}

int main(void) {
  printf("%g\n", round_to("E4M3", CPFLOAT_SAT_NO, 480));  /* 480, want nan (480 is not finite in OCP E4M3) */
  printf("%g\n", round_to("E4M3", CPFLOAT_SAT_NO, 470));  /* 480, want nan (above 464 overflows) */
  printf("%g\n", round_to("E4M3", CPFLOAT_SAT_USE, 500)); /* 480, want 448 */
  printf("%g\n", round_to("E4M3", CPFLOAT_SAT_NO, 448));  /* 448 (correct) */
  return 0;
}

cc -std=c11 -Icpfloat/src t.c -lm && ./a.out at 3583976 prints 480 480 480 448.

In OCP E4M3, values above the midpoint 464 overflow (to NaN, since the format has no Infinity), saturation clamps to 448, and directed modes toward zero give ±448. Infinite inputs are affected the same way (±480 when saturating). The E5M2 format is fine: it has infinities, so xmax = 57344 is right.

Where: cpfloat_populate_optstruct_from_format() sets only precision, emin and emax for E4M3, and xmax is derived from them (src/cpfloat_template.h lines 381 and 1298). The MATLAB test (test/cpfloat_test.m, under "Modification for OCP compliant q43") uses the same formula, so it expects 480 too. Since xmax can't express OCP E4M3 from precision/emin/emax alone, the fix needs a special case for this format (xmax = 448, overflow bound 464), or a note in the README that E4M3 is IEEE-style rather than OCP E4M3.

Use the following links to see the expected conversion results:

Activity

  1. added a commit that references this issue on Oct 13, 2026
    9dfa9ec
  2. mogrob commented on Oct 13, 2026

    @mogrob

    Thanks for this report. This is unfortunately expected – currently, CPFloat only supports IEEE-like formats, and E4M3 is not one of them as it uses a different encoding to store the only NaN it has.

  3. sw23 commented on Oct 14, 2026

    @sw23
    Author

    Got it, makes sense - figured I would report it, in case it wasn't known, but feel free to close

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions