# Help defining a custom IBMFloat32 primitive type

**URL:** <https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904>\
**Category:** General Usage\
**Tags:** question, primitive-type\
**Created:** [November 15, 2025, 7:17pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904 "2025-11-15T19:17:18Z")\
**Posts on this page:** 15\
**Page:** 2

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [November 16, 2025, 4:30pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/21 "2025-11-16T16:30:21Z")

</div>

If you want to keep the bit-level approach (as opposed to doing a final computation in `Float64`), then you either use `leading_zeros` or modify your loop. You also need to adjust the biases. The function now looks similar the previous bit-level code:

```julia-auto
function juliohm2(b::UInt32)
    s = b & 0x80000000
    e = b & 0x7f000000
    f = b & 0x00ffffff

    # if fraction is zero, value is zero
    iszero(f) && return zero(Float64)

    # shift sign bit to the rightmost position
    sieee = Int64(s >> 31)

    # shift exponent bits to the right (3 bytes = 24 bits)
    # and multiply by 4 to convert from base 16 to base 2
    # hence, the resulting shift is 24 - 2 = 22 bits
    eieee = Int64(e >> 22)
    eieee += 1023 - 4*64 # adjust bias

    # normalize fraction until the leading digit is equal to 1,
    # and adjust the exponent accordingly (opposite shift)
    while iszero(f & 0x00800000)
        f <<= 1
        eieee -= 1
    end
    f = (f << 1) & 0x00ffffff # drop leading bit
    eieee -= 1 # account for dropped leading bit
    fieee = UInt64(f) << (52 - 24) # pad with zeros to the right

    reinterpret(Float64, sieee << 63 | eieee << 52 | fieee)
end

```

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 16, 2025, 7:05pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/22 "2025-11-16T19:05:55Z")

</div>

Thank you @matthias314 , I am learning a lot about these bit manipulations from your code snippets. I will try to simplify the code further based on the last version you shared.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 16, 2025, 8:08pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/23 "2025-11-16T20:08:54Z")

</div>

> [@matthias314](#):
>
> `eieee += 1023 - 4*64 # adjust bias`

@matthias314 regarding this line, shouldn’t it just be `1023 - 64`? Where is the `4` coming from?

My understanding from your previous comments:

```julia
  # exponent of 64 means 0 in the IBM standard, and
  # exponent of 1023 means 0 in the IEEE standard
  # hence, the bias adjustment is -64 + 1023 = 959
  eieee += 959

```

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 16, 2025, 8:25pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/24 "2025-11-16T20:25:58Z")

</div>

> [@matthias314](#):
>
> `fieee = UInt64(f) << (52 - 24) # pad with zeros to the right`

Regarding this line, could you please elaborate on how you arrived at `(52 - 24)`?

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [November 16, 2025, 8:53pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/25 "2025-11-16T20:53:19Z")

</div>

The IBM floats exponents are to base 16 and the IEEE floats exponents to base 2.

---

<div class="post-metadata">

**Author:** ![matthias314](https://avatars.discourse-cdn.com/v4/letter/m/a88e4f/32.png) [@matthias314](https://discourse.julialang.org/u/matthias314)\
**Post date:** [November 16, 2025, 8:56pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/26 "2025-11-16T20:56:01Z")

</div>

The fraction is 24 bits for IBM and 52 bits for IEEE floats.

I would like to suggest again that you look at some examples from the Wikipedia page and see what the various steps of the code mean for them.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 16, 2025, 9:03pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/27 "2025-11-16T21:03:30Z")

</div>

I am finally grasping these shifts…

The 32-bit IBM floating point has the following layout:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/7/2/7253b0679efea06b2f5c0a5278b0edee50e72d83.png)

The 64-bit IEEE floating point has the following layout:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/8/8/88bebb40b70a0c7413a949f8af0f4605681d699f.png)

Here is an updated implementation:

```julia
function Base.convert(::Type{Float64}, x::IBMFloat32)
  # extract sign, exponent, and fraction bits
  b = reinterpret(UInt32, x)
  s = b & 0x80000000
  e = b & 0x7f000000
  f = b & 0x00ffffff

  # if fraction is zero, value is zero
  iszero(f) && return zero(Float64)

  # position sign bit at the leftmost bit of 8 bytes
  sieee = Int64(s) << 32

  # shift exponent bits to the right (3 bytes = 24 bits)
  # and multiply by 4 to convert from base 16 to base 2
  # hence, the resulting shift is 24 - 2 = 22 bits
  eieee = Int64(e >> 22)

  # exponent of 64 in base 16 means 0 in the IBM standard,
  # and exponent of 1023 in base 2 means 0 in the IEEE standard
  # hence, the bias adjustment is 1023 - 4*64 = 767
  eieee += 767

  # shift fraction bits to the left until the leading digit is 1
  # and then drop the leading 1 as it is implicit in IEEE
  shift = leading_zeros(f) - 8 # ignore sign and exponent bits
  fieee = UInt64((f << (shift + 1)) & 0x00ffffff)
  eieee -= (shift + 1)

  # pad fraction with zeros to the right, and since IEEE
  # uses 52 bits for the fraction and IBM uses 24 bits,
  # we need to shift 52 - 24 = 28 bits to the left
  fieee <<= 28

  # now that the exponent bits have been adjusted, shift
  # them to their final position in the IEEE representation
  eieee <<= 52

  # reinterpret bits as Float64
  reinterpret(Float64, sieee | eieee | fieee)
end

```

---

<div class="post-metadata">

**Author:** ![Palli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/palli/32/3380_2.png) [@Palli](https://discourse.julialang.org/u/Palli)\
**Post date:** [November 16, 2025, 9:10pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/28 "2025-11-16T21:10:07Z")

</div>

> [@juliohm](#):
>
> an old file format that uses 4-byte IBM floating point

Which? The conversion is here (to Float32, not sure you get NaN for out of range, it’s huge you likely don’t need to support all of it):

> <https://github.com/slimgroup/SegyIO.jl/blob/22770f6dfafaea9d3ca2d92739e6e41778754723/src/types/IBMFloat32.jl#L5>

also supporting that format in case it’s what you needed… I believe there are very few, maybe only also some FDA format needing hexadecimal.

The standard also allows IEEE:

> **[seg\_y\_rev2\_1-oct2023.pdf](https://library.seg.org/pb-assets/technical-standards/seg_y_rev2_1-oct2023.pdf)**

In case helping (with e.g. PythonCall.jl): ibm2ieee [Client Challenge](https://pypi.org/project/ibm2ieee/)

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 16, 2025, 9:15pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/29 "2025-11-16T21:15:19Z")

</div>

Hi @Palli, thank you for sharing the links, I am aware of them already. I am writing a new package that supports all SEGY revisions from revision 0.0 (before 1975) to revision 2.1 (2023). It is in very good shape already, except for the IBM float that is almost there.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 16, 2025, 9:21pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/30 "2025-11-16T21:21:46Z")

</div>

@matthias314 I am very happy with this implementation, thank you for the incredible help. Do you have any additional suggestion to improve readability or performance?

I understand that the only place where we are not doing explicit bit manipulation is the bias adjustment with `eieee += 767`. Is there an equivalent adjustment in terms of bit shifts?

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [November 17, 2025, 8:20am UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/32 "2025-11-17T08:20:29Z")

</div>

Just a minor comment which may or may not be obvious to everyone.

If you have two (reasonably fast) implementations for this conversion and want to verify that they are doing the same thing, you can just run through all the 2^32 bit patterns.

---

<div class="post-metadata">

**Author:** ![halleysfifthinc](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/halleysfifthinc/32/206280_2.png) [@halleysfifthinc](https://discourse.julialang.org/u/halleysfifthinc)\
**Post date:** [November 17, 2025, 8:22pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/33 "2025-11-17T20:22:26Z")

</div>

You might find my package VaxData.jl helpful as a reference, since it sounds like you have similar goals supporting (reading, converting, etc) historic floating point representations.

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [November 17, 2025, 9:43pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/34 "2025-11-17T21:43:33Z")

</div>

The function `ibm_to_ieee_64` is located in the file `fp.c` that is included in the zip file `3rd_Ed__NumbersAndComputers.zip` in the repo, and the [repo license file](https://github.com/rkneusel9/NumbersAndComputers/blob/master/LICENSE) specifies the MIT license, so I think you are safe to publish the code.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [November 18, 2025, 9:45am UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/35 "2025-11-18T09:45:30Z")

</div>

For future reference: the solution I marked in this thread is ~10x faster than the one shared in the book (checked with BenchmarkTools.jl).

Thank you everyone for the help and for the additional links. This thread will certainly help future readers interested in this old floating point type.

---

<div class="post-metadata">

**Author:** ![PeterSimon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/petersimon/32/25193_2.png) [@PeterSimon](https://discourse.julialang.org/u/PeterSimon)\
**Post date:** [November 18, 2025, 6:14pm UTC](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904/36 "2025-11-18T18:14:45Z")

</div>

Even though this issue has been marked as solved, I’m going to add one more bit…

In May 2024, Cleve Moler, the famous numerical analyst and founder of Matlab, [posted a blog entry](https://blogs.mathworks.com/cleve/2024/05/25/ibm-hexadecimal-floating-point/) on this same topic. He included a historical review, annotated sample Matlab codes, and a link to [more robust Matlab codes](https://blogs.mathworks.com/cleve/files/Hex_ieee.m) (easily converted to Julia) that convert to/from IBM System/360 hexadecimal format, in both single and double precision. [I obtained his permission](https://blogs.mathworks.com/cleve/?p=11324#reply_2622786) to use these codes in other open-source software. So that is another good source of information on this topic.

[Previous page](https://discourse.julialang.org/t/help-defining-a-custom-ibmfloat32-primitive-type/133904.md?page=1)
