Port Float16 conversion from Rust
parent
b1974aab2d
commit
d0ee8df1af
|
@ -4,13 +4,13 @@ require "./spec_helper"
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
# Disabled as half-precision floats are not supported:
|
# Disabled as half-precision floats are not supported:
|
||||||
# { %(0.0), "f9 00 00" },
|
{ %(0.0), "f9 00 00" },
|
||||||
# { %(-0.0), "f9 80 00" },
|
{ %(-0.0), "f9 80 00" },
|
||||||
# { %(1.0), "f9 3c 00" },
|
{ %(1.0), "f9 3c 00" },
|
||||||
# { %(1.5), "f9 3e 00" },
|
{ %(1.5), "f9 3e 00" },
|
||||||
# { %(65504.0), "f9 7b ff" },
|
{ %(65504.0), "f9 7b ff" },
|
||||||
# { %(0.00006103515625), "f9 04 00" },
|
# { %(0.00006103515625), "f9 04 00" }, TODO: Something about the presentation
|
||||||
# { %(-4.0), "f9 c4 00" },
|
{ %(-4.0), "f9 c4 00" },
|
||||||
# { %(5.960464477539063e-8), "f9 00 01" },
|
# { %(5.960464477539063e-8), "f9 00 01" },
|
||||||
# { %(Infinity), "f9 7c 00" },
|
# { %(Infinity), "f9 7c 00" },
|
||||||
# { %(NaN), "f9 7e 00" },
|
# { %(NaN), "f9 7e 00" },
|
||||||
|
|
|
@ -84,7 +84,7 @@ class CBOR::Lexer
|
||||||
when 0xe0..0xf8
|
when 0xe0..0xf8
|
||||||
consume_simple_value(read_size(byte - 0xe0))
|
consume_simple_value(read_size(byte - 0xe0))
|
||||||
when 0xf9
|
when 0xf9
|
||||||
raise ParseError.new("Half-precision floating point numbers are not supported")
|
Token::FloatT.new(value: Float32.new(read(UInt16)))
|
||||||
when 0xfa
|
when 0xfa
|
||||||
Token::FloatT.new(value: read(Float32))
|
Token::FloatT.new(value: read(Float32))
|
||||||
when 0xfb
|
when 0xfb
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
# Returns a Float32 by reading the 16 bit as a IEEE 754 half-precision floating
|
||||||
|
# point (Float16).
|
||||||
|
def Float32.new(i : UInt16)
|
||||||
|
# Check for signed zero
|
||||||
|
if i & 0x7FFF_u16 == 0
|
||||||
|
return (i.unsafe_as(UInt32) << 16).unsafe_as(Float32)
|
||||||
|
end
|
||||||
|
|
||||||
|
half_sign = (i & 0x8000_u16).unsafe_as(UInt32)
|
||||||
|
half_exp = (i & 0x7C00_u16).unsafe_as(UInt32)
|
||||||
|
half_man = (i & 0x03FF_u16).unsafe_as(UInt32)
|
||||||
|
|
||||||
|
# Check for an infinity or NaN when all exponent bits set
|
||||||
|
if half_exp == 0x7C00_u32
|
||||||
|
# Check for signed infinity if mantissa is zero
|
||||||
|
if half_man == 0
|
||||||
|
return ((half_sign << 16) | 0x7F80_0000_u32).unsafe_as(Float32)
|
||||||
|
else
|
||||||
|
# NaN, keep current mantissa but also set most significiant mantissa bit
|
||||||
|
return ((half_sign << 16) | 0x7FC0_0000_u32 | (half_man << 13)).unsafe_as(Float32)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Calculate single-precision components with adjusted exponent
|
||||||
|
sign = half_sign << 16
|
||||||
|
# Unbias exponent
|
||||||
|
unbiased_exp = ((half_exp.unsafe_as(Int32)) >> 10) - 15
|
||||||
|
|
||||||
|
# Check for subnormals, which will be normalized by adjusting exponent
|
||||||
|
if half_exp == 0
|
||||||
|
# Calculate how much to adjust the exponent by
|
||||||
|
e = half_man.unsafe_as(UInt16).leading_zeros_count - 6
|
||||||
|
|
||||||
|
# Rebias and adjust exponent
|
||||||
|
exp = (127 - 15 - e) << 23
|
||||||
|
man = (half_man << (14 + e)) & 0x7F_FF_FF_u32
|
||||||
|
return (sign | exp | man).unsafe_as(Float32)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Rebias exponent for a normalized normal
|
||||||
|
exp = (unbiased_exp + 127).unsafe_as(UInt32) << 23
|
||||||
|
man = (half_man & 0x03FF_u32) << 13
|
||||||
|
(sign | exp | man).unsafe_as(Float32)
|
||||||
|
end
|
Loading…
Reference in New Issue