-
Notifications
You must be signed in to change notification settings - Fork 1
/
wbHalfFloat.pas
162 lines (149 loc) · 4.45 KB
/
wbHalfFloat.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
unit wbHalfFloat;
interface
// http://galfar.vevb.net/wp/2011/16bit-half-float-in-pascaldelphi/
type
THalfFloat = type Word;
PHalfFloat = ^THalfFloat;
const
MinHalf: Single = 5.96046448e-08; // Smallest positive half
MinHalfNorm: Single = 6.10351562e-05; // Smallest positive normalized half
MaxHalf: Single = 65504.0; // Largest positive half
// Smallest positive e for which half (1.0 + e) != half (1.0)
HalfEpsilon: Single = 0.00097656;
HalfNaN: THalfFloat = 65535;
HalfPosInf: THalfFloat = 31744;
HalfNegInf: THalfFloat = 64512;
function FloatToHalf(Float: Single): THalfFloat;
function HalfToFloat(Half: THalfFloat): Single;
implementation
function FloatToHalf(Float: Single): THalfFloat;
var
Src: LongWord;
Sign, Exp, Mantissa: LongInt;
begin
Src := PLongWord(@Float)^;
// Extract sign, exponent, and mantissa from Single number
Sign := Src shr 31;
Exp := LongInt((Src and $7F800000) shr 23) - 127 + 15;
Mantissa := Src and $007FFFFF;
if (Exp > 0) and (Exp < 30) then
begin
// Simple case - round the significand and combine it with the sign and exponent
Result := (Sign shl 15) or (Exp shl 10) or ((Mantissa + $00001000) shr 13);
end
else if Src = 0 then
begin
// Input float is zero - return zero
Result := 0;
end
else
begin
// Difficult case - lengthy conversion
if Exp <= 0 then
begin
if Exp < -10 then
begin
// Input float's value is less than HalfMin, return zero
Result := 0;
end
else
begin
// Float is a normalized Single whose magnitude is less than HalfNormMin.
// We convert it to denormalized half.
Mantissa := (Mantissa or $00800000) shr (1 - Exp);
// Round to nearest
if (Mantissa and $00001000) > 0 then
Mantissa := Mantissa + $00002000;
// Assemble Sign and Mantissa (Exp is zero to get denormalized number)
Result := (Sign shl 15) or (Mantissa shr 13);
end;
end
else if Exp = 255 - 127 + 15 then
begin
if Mantissa = 0 then
begin
// Input float is infinity, create infinity half with original sign
Result := (Sign shl 15) or $7C00;
end
else
begin
// Input float is NaN, create half NaN with original sign and mantissa
Result := (Sign shl 15) or $7C00 or (Mantissa shr 13);
end;
end
else
begin
// Exp is > 0 so input float is normalized Single
// Round to nearest
if (Mantissa and $00001000) > 0 then
begin
Mantissa := Mantissa + $00002000;
if (Mantissa and $00800000) > 0 then
begin
Mantissa := 0;
Exp := Exp + 1;
end;
end;
if Exp > 30 then
begin
// Exponent overflow - return infinity half
Result := (Sign shl 15) or $7C00;
end
else
// Assemble normalized half
Result := (Sign shl 15) or (Exp shl 10) or (Mantissa shr 13);
end;
end;
end;
function HalfToFloat(Half: THalfFloat): Single;
var
Dst, Sign, Mantissa: LongWord;
Exp: LongInt;
begin
// Extract sign, exponent, and mantissa from half number
Sign := Half shr 15;
Exp := (Half and $7C00) shr 10;
Mantissa := Half and 1023;
if (Exp > 0) and (Exp < 31) then
begin
// Common normalized number
Exp := Exp + (127 - 15);
Mantissa := Mantissa shl 13;
Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
// Result := Power(-1, Sign) * Power(2, Exp - 15) * (1 + Mantissa / 1024);
end
else if (Exp = 0) and (Mantissa = 0) then
begin
// Zero - preserve sign
Dst := Sign shl 31;
end
else if (Exp = 0) and (Mantissa <> 0) then
begin
// Denormalized number - renormalize it
while (Mantissa and $00000400) = 0 do
begin
Mantissa := Mantissa shl 1;
Dec(Exp);
end;
Inc(Exp);
Mantissa := Mantissa and not $00000400;
// Now assemble normalized number
Exp := Exp + (127 - 15);
Mantissa := Mantissa shl 13;
Dst := (Sign shl 31) or (LongWord(Exp) shl 23) or Mantissa;
// Result := Power(-1, Sign) * Power(2, -14) * (Mantissa / 1024);
end
else if (Exp = 31) and (Mantissa = 0) then
begin
// +/- infinity
Dst := (Sign shl 31) or $7F800000;
end
else //if (Exp = 31) and (Mantisa <> 0) then
begin
// Not a number - preserve sign and mantissa
Dst := (Sign shl 31) or $7F800000 or (Mantissa shl 13);
end;
// Reinterpret LongWord as Single
Result := PSingle(@Dst)^;
end;
end.