clash-lang / clash-compiler

Haskell to VHDL/Verilog/SystemVerilog compiler
https://clash-lang.org/
Other
1.44k stars 151 forks source link

Template Haskell does not preserve transfinite floating point values #1812

Open DigitalBrains1 opened 3 years ago

DigitalBrains1 commented 3 years ago

Transfinite numbers are not preserved by Template Haskell, as -ddump-splices shows:

Test12.hs:13:10-55: Splicing expression
    listToVecTH [- 0 :: Float, unpack 0x7FC00000]
  ======>
    (0.0 :> (5.104235503814077e38 :> Nil))

(these values should have been negative zero and a positive quiet NaN with a zero payload, but end up as positive zero and infinity).

This has been closed as wontfix in GHC issue #13124.

pbreuer commented 3 years ago

I'll just comment since I allegedly know about IEEE floating point format (IANA(language)L, however). It is my impression that Haskell is not using the IEEE format internally, just a close approximation of it. Thus when you unpack bits into a Float and vice versa you may not get what you expect on that account alone, and that may be a bug, if it is one, but it's not exactly Clash that is doing it, and you should be able to work around it for synthesis (by defining your own data type with bits as you like them), plus in any case Clash will not synthesize anything to do with Float or Double at the moment, so your problem is moot from the point of view of synthesis (still valid as a simulation problem).

I am not sure if all of that would have been mandatory in the IEEE standard anyway, as it is a little vague about whether one MAY or MUST have quiet/unquiet NaNs and definitely which is quiet and which is unquiet is left up to the implementation (I remember that! And I am so not going to read that standard again ...).

My impression in particular is that for Double (not Float), Haskell is using a souped up version of the 32-point format internally, which drops the leading 1 on a fractional sequence of bits, so 0.10011 would be stored as 11. I would have expected instead that it use the 64-point format, which stores 10011 in the expectation that the trouble to cut the storage requirement is not worth it in terms of speed.

I am not sure if either Haskell Float or Double is using the so-called "subnormal" variant in the IEEE format to improve the granularity of the representation near zero. I imagine not, but I have not checked.

BUT, in any case, the platform (i.e. hardware/PC) implementation may be the one that is simply being dragged up through some sequence of libraries in some circumstances. The bottom line is that IEEE allows a lot of different implementations and it would need sifting through a lot of pages to see if that was allowed or not, and more hard work to find out who and what is responsible and why.

There may well be a strategic problem here for people intending to use Clash to drive a synthesis that interfaces closely with standard IEEE arithmetic libraries in vhdl or verilog, but since once can't synthesise that stuff yet, it is hypothetical.

My own solution has been to define my own datatype, FLOAT, which contains nothing but Unsigned 32, whose bits I define. Hooking floating point arithmetic operations up to the IEEE libraries for synthesis is a question then of writing ones own ANN intercepts for primitives like addFLOAT, mulFLOAT, which will hookup with the library calls in the generated vhdl or verilog ...

PTB

christiaanb commented 3 years ago

@DigitalBrains1 is working on adding wrappers for the Xilinx Floating-Point LogiCORE IP, so we're fine as long the Haskell model we're building and VHDL/Verilog of that Xilinx IP behave the same. The Xilinx IP is definitely not completely IEEE754 compliant.

This TH issue only showed up when @DigitalBrains1 was working on the test code for those wrappers.