Updates to my Haskell Posit Library

104 views
Skip to first unread message

Nathan Waivio

unread,
Nov 11, 2023, 8:02:38 PM11/11/23
to Unum Computing
Hi All,
I've made a few changes recently to my Haskell Posit library, some based on discussions from this board.  I've also read some mention of a new Posit Standard.  I'm quite interested in having my library based on the Posit Standard, so I thought I'd open up a discussion about my recent changes and share some thoughts.  I have felt free to experiment with some changes since the Posit Standard did not discuss them.

1. Automatic rewriting of fused operations
So in my recent pull request located here: Fused ops rewrite rules, I implemented functionality for the compiler to attempt to automatically rewrite the code to use various fused operations.  The functionality is somewhat fragile and from what I read in "The End of Error", that's not great.  I've also use the functionality to find places in the code where I could explicitly write the fused operations.

2. Removed various constants and functions
I read on this board about the Posit Standard perhaps describing more special functions.  I've removed some of the special functions I've implemented previously (I was not really happy with the implementation), and renamed some of the specific values to not use the names of Greek letters to not pollute the namespace.

3. Implemented a printing of Posit Numbers to round trip
My previous implementation did not fully round trip the printing and reading of Posits.  I've now changed to the following algorithm:

  decimalPrec posit =
    let regimeFormat = findRegimeFormat @es posit
        regimeCount = countRegimeBits @es regimeFormat posit
        fractionSize = max (fromIntegral (nBits @es) - fromIntegral (signBitSize @es) - regimeCount - fromIntegral (exponentSize @es)) 0  -- fractionSize is at least zero
    in succ.ceiling $ (fromIntegral fractionSize + 1) * log10Of2 + halflog10Of2

The round-tripping was tested to P32 exhaustively, it also seems to work up-to at least P256 in my spot checks.

4. Implementation of an `approxEq` function
Approximately Equal, is something I've found I need in practice.  After thinking of rather complex implementations I've thought of a rather simple implementation that uses existing Posit Standard functionality:
  approxEq a b =
    let a' = convert a :: Posit (Prev es)
        b' = convert b :: Posit (Prev es)
    in a' == b'



Thanks,
Nathan.

Nathan Waivio

unread,
Nov 11, 2023, 8:07:35 PM11/11/23
to Unum Computing
Opps, I accidentally hit the Post button, but wanted to mention that I'm looking forward to the feedback, but will be out on travel for the next week, so I will be slow to reply.

And to expand on above, the Posit (Prev es), is the lower ES precision of the Posit Number, for instance if someone is testing approximate equal of a P128 it will convert to a P64 and then use the standard `==` function.

Thanks,
Nathan.

John Gustafson

unread,
Nov 14, 2023, 9:05:36 AM11/14/23
to Nathan Waivio, Unum Computing
I really hope that your ability to find operations that can be fused, automatically, is done as a preprocessor and not as a compiler option. Compiler options live in the Makefile and lead to irreproducibilities. But if you run a preprocessor that finds and declares fusable operations and emits a new version of the source code that has those groupings visible, you will still be compliant with the Posit Standard. Also, the programmer may notice some operations that should not be fused.

An example of something that should not be fused is the multiply-add in sqrt(x*x – y*y). if you first compute x*x, then do a fused multiply-add to compute (x*x) – y*y, and x = y, about half the time rounding error will produce a slightly negative difference and the square root will throw a spurious exception. Stuff like that.

Nice work on the round tripping of input and output to human-readable format! Perhaps as the result of doing that, you found the minimum number of decimal characters that can represent any P32 value. Values near ±1 need 10 significant digits but don't need the "e" notation for the exponent. Values very large or very small in magnitude need the "e" notation and up to three characters for the exponent, like "1e–35", but they need very few significant digits to express the posit uniquely. That means it should be possible to express posits in a human-readable form, pointing to a unique posit, with fewer (maximum) characters than is possible with floats.

John

--
You received this message because you are subscribed to the Google Groups "Unum Computing" group.
To unsubscribe from this group and stop receiving emails from it, send an email to unum-computin...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/unum-computing/26e67109-d38e-4864-96d7-606f61f3fca5n%40googlegroups.com.

Nathan Waivio

unread,
Nov 28, 2023, 9:23:36 PM11/28/23
to Unum Computing
Hi Dr. Gustafson,
Currently the rewrite rules are a compiler option that is disabled by default.  It is somewhat experimental and I'll be opening an issue with GHC to try and improve the stability.

I like the example you brought up.  An `fma` hardware operation would have problems with `sqrt(x*x-y-y)`, but with the software capability I've implemented an `fmms a b c d = (a * b) - (c * d)` fused operation and that does automatically get detect and is replaced with the fused operation.

In my experience, for a decimal "round-trip" the algorithm needs an additional digit from what would be expected by just the significance of the posit number.

One interesting property, that I've failed to mention, for my proposed `approxEq` is that for algorithms that step up in precision can still be tested for a fixed point at the original target precision.  This property allows the algorithm to terminate before reaching a fixed point at the higher precision, reducing the total amount of computation needed.

I'm interested in what other ideas people have.

Thanks,
Nathan.
Reply all
Reply to author
Forward
0 new messages