用歧视的联合和记录类型表示浮点

时间:2017-07-19 23:22:45

标签: f# floating-point record domain-model discriminated-union

对于我的项目,有必要将IEEE 754浮点类型“分解”为内部表示。我有以下类型:

type Sign =
   | Positive
   | Negative
type Number =
   { exponent: int32 // The exponent is de-biased when converting from floating-point types. Exponent values here range from −2,147,483,648 to 2,147,483,647.
     mantissa: bigint } // There is no implicit leading 1 bit. If the original float was normal, the converting code prepends the originally hidden 1 bit to the mantissa. Subnormals do not have this bit prepended to the mantissa.
type Infinity = Infinity
type SuperFloat =
   | Number of Sign * Number
   | Infinity of Sign * Infinity
   | NaN

这可以用一些语法快捷方式更好地编写以保存类型,或者这是我必须忍受的最小数量的类型来覆盖所有可能的浮动类型?

  • NaN payloads不需要支持。
  • 内部表示不会暴露给外部代码,也不需要与其他.NET语言的互操作性。

1 个答案:

答案 0 :(得分:4)

首先,Infinity类型并不真正有用:因为它只有一个值,所以它不会对任何信息进行编码。

然后,Sign情况下你不需要Number,因为数字本身有“烘焙”的标志 - 即尾数可以是负数(顺便说一句,因此你的表示是不仅仅是矫枉过正,而且实际上不正确)。

所以第二次迭代将是:

type Sign = 
   | Positive 
   | Negative 
type Number = { 
   exponent: int32 
   mantissa: bigint } 
type SuperFloat = 
   | Number of Number 
   | Infinity of Sign
   | NaN

下一点是有争议的,取决于预期用途,但我个人会摆脱Sign类型并用两个单独的案例编码无穷大符号:

type Number = { 
   exponent: int32 
   mantissa: bigint } 
type SuperFloat = 
   | Number of Number 
   | PositiveInfinity
   | NegativeInfinity
   | NaN

最后,如果你愿意的话,你可以将指数和尾数直接带到DU下面,但是那个更有争议,甚至更多取决于用法:

type SuperFloat = 
   | Number of exponent: int32 * mantissa: bigint
   | PositiveInfinity
   | NegativeInfinity
   | NaN

更新

由于我在评论中提醒负零是一件事,所以我必须带回Sign类型。无论如何它都在这里,我们也可以将它用于无限:

type Sign = Positive | Negative
type SuperFloat = 
   | Number of sign: Sign * exponent: int32 * mantissa: bigint
   | Infinity of Sign
   | NaN

然而,请注意此类型允许使用不正确的值,因为尾数仍可为负数。不幸的是,bigint不能成为非负面的。

我认为将尾数设为bigint是有原因的(你需要处理任意大数?),但如果没有具体原因,我建议改为使用uint64代替。编码双精度浮点数就足够了。