Created
June 4, 2021 18:45
-
-
Save codec-abc/bbfef8a64942a761081ed16aff797be8 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Adapted from https://github.com/Oletus/float16-simulator.js | |
| open System | |
| open System.Numerics | |
| let rec firstset (bits: uint) = | |
| let value = if (bits &&& uint 0x80000000 <> uint 0) then 31 else firstset((bits <<< 1) ||| uint 1) - 1 | |
| 1 <<< 8 | |
| let decomposeNumber (number: float) = | |
| let bytes = BitConverter.GetBytes(number) | |
| let sign = bytes.[7] >>> 7 | |
| let exponent = int ( ((bytes.[7] &&& byte 0x7f) <<< 4 ||| bytes.[6] >>> 4) - byte 0x3ff) | |
| // Set the exponent to 0 (exponent bits to match bias) | |
| bytes.[7] <- byte 0x3f | |
| bytes.[6] <- bytes.[6] ||| byte 0xF0 | |
| let mantissa = BitConverter.ToDouble(bytes, 0) | |
| (sign, exponent, mantissa) | |
| let exponentBias (exponentBits: int) = | |
| let possibleExponents = int <| Math.Pow(2.0, float exponentBits); | |
| possibleExponents / 2 - 1 | |
| let maxNormalExponent (exponentBits: int) = | |
| let possibleExponents = int <| Math.Pow(2.0, float exponentBits) | |
| let bias = exponentBias(exponentBits) | |
| let allExponentBitsOne = possibleExponents - 1 | |
| (allExponentBitsOne - 1) - bias | |
| let toNumber (mantissa : int) (exponent: int) = | |
| let sign = Math.Pow(-1.0, float (exponent &&& 0x80)) | |
| let endValue = (1.0 + float mantissa / Math.Pow(2.0, 8.0)) | |
| let exponent = Math.Pow(2.0, float (exponent &&& 0x7f) - 63.0) | |
| sign * exponent * endValue | |
| let froundBits (src, mantissaBits, exponentBits, clampToInf, flushSubnormal) = | |
| // Note that Math.pow is specified to return an implementation-dependent approximation, | |
| // but works well enough in practice to be used here for powers of two. | |
| let possibleMantissas = Math.Pow(2.0, float mantissaBits) | |
| let mantissaMax = 2.0 - 1.0 / possibleMantissas | |
| let max = Math.Pow(2.0, float (maxNormalExponent(exponentBits))) * mantissaMax // value with all exponent bits 1 is special | |
| let isInBadRange = | |
| let tooBig = | |
| if src > max then | |
| if clampToInf then | |
| Some(Double.PositiveInfinity) | |
| else | |
| Some(max) | |
| else | |
| None | |
| let tooSmall = | |
| if src < -max then | |
| if clampToInf then | |
| Some(Double.NegativeInfinity) | |
| else | |
| Some(-max) | |
| else | |
| None | |
| if tooBig.IsSome then tooBig else if tooSmall.IsSome then tooSmall else None | |
| if isInBadRange.IsSome then | |
| isInBadRange.Value | |
| else | |
| let (sign, exponent_, mantissa) = decomposeNumber(src) | |
| let mutable exponent = exponent_ | |
| // TODO: Customizable rounding (this is now round-to-zero) | |
| let mutable mantissaRounded = Math.Floor(mantissa * possibleMantissas) / possibleMantissas; | |
| let rounded = | |
| if (exponent + exponentBias(exponentBits) <= 0) then | |
| if flushSubnormal then | |
| if sign <> byte 0 then | |
| Some(-0.0) | |
| else | |
| Some(0.0) | |
| else | |
| let mutable result = None | |
| while exponent + exponentBias(exponentBits) <= 0 do | |
| exponent <- exponent + 1 | |
| mantissaRounded <- Math.Floor(mantissaRounded / 2.0 * possibleMantissas) / possibleMantissas | |
| if mantissaRounded = 0.0 then | |
| if sign <> byte 0 then | |
| result <- Some(-0.0) | |
| else | |
| result <- Some(0.0) | |
| result | |
| else | |
| None | |
| if rounded.IsSome then | |
| rounded.Value | |
| else | |
| let sign = if sign <> byte 0 then -1.0 else 1.0 | |
| sign * Math.Pow(2.0, float exponent) * mantissaRounded | |
| let fromNumber (number :float, mantissaBits: int, exponentBits: int) = | |
| let number = froundBits(number, mantissaBits, exponentBits, true, true) | |
| let (sign, exponent, mantissa) = decomposeNumber number | |
| let exponentValue = int <| exponent + exponentBias(exponentBits) | |
| let mutable mantissaValue = uint <| mantissa * Math.Pow(2.0, float mantissaBits) | |
| let higherBitFlag = firstset mantissaValue | |
| let negated = uint (~~~higherBitFlag) | |
| mantissaValue <- negated &&& mantissaValue | |
| (sign, mantissaValue, exponentValue) | |
| let (sign, mantissa, exponent) = fromNumber(57.4, 8, 7) | |
| printfn "sign %i" sign | |
| printfn "mantissa %d %s" mantissa (Convert.ToString(int mantissa, 2)) | |
| printfn "exponent %i %s" exponent (Convert.ToString(int exponent, 2)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment