1(*
2    Title:      Real32 structure.
3    Author:     David Matthews
4    Copyright   David Matthews 2018
5
6    This library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License version 2.1 as published by the Free Software Foundation.
9    
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14    
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18*)
19
20(*
21    This structure implements 32-bit real values, at least on X86.  On other
22    platforms it is whatever "float" is.
23    N.B.  This uses the X87 floating point instructions on X86/32.  The precision
24    on the X87 is set to 64-bits which is correct for the Real.real operations
25    but involves an extra stage of rounding for Real32.real.  That means that
26    the results may not be strictly accurate.
27*)
28
29structure Real32: REAL where type real = Real32.real =
30struct
31    open Real32 (* Inherit the type and the built-in functions. *)
32    open IEEEReal
33
34    fun fromLarge IEEEReal.TO_NEAREST = fromRealRound
35    |   fromLarge IEEEReal.TO_ZERO = fromRealTrunc
36    |   fromLarge IEEEReal.TO_POSINF = fromRealCeil
37    |   fromLarge IEEEReal.TO_NEGINF = fromRealFloor
38
39    (* Defined to use the current rounding mode. *)
40    val fromInt = fromReal o Real.fromInt (* TODO *)
41    and fromLargeInt = fromReal o Real.fromLargeInt
42    
43    val zero = fromInt 0 and one = fromInt 1 and four = fromInt 4
44
45    local
46        (* The General call is now only used to get constants. *)
47        val doFloatFloat : int*unit->real = RunCall.rtsCallFull2 "PolyRealGeneral"
48        and doFloatInt  : int*unit->int = RunCall.rtsCallFull2 "PolyRealGeneral"
49        fun callFloat n x = doFloatFloat(n, x)
50        and callFloatToInt n x = doFloatInt(n, x)
51    in
52        val radix : int = callFloatToInt 30 ()
53        val precision : int = callFloatToInt 31 ()
54        val maxFinite : real = callFloat 32 ()
55        val minNormalPos : real = callFloat 33 ()
56        val minPos : real = callFloat 34()
57    end
58
59    val posInf : real = one/zero;
60    val negInf : real = ~one/zero;
61
62    infix 4 == != ?=;
63    
64    val op != : real * real -> bool = not o op ==
65
66    local
67    in
68        (* isNan can be defined in terms of unordered. *)
69        fun isNan x = unordered(x, x)
70
71        (* NAN values do not match and infinities when multiplied by 0 produce NAN. *)
72        fun isFinite x = x * zero == zero
73    
74        val copySign : (real * real) -> real = rtsCallFastFF_F "PolyRealFCopySign"
75        
76        (* Get the sign bit by copying the sign onto a finite value and then
77           testing.  This works for non-finite values and zeros. *)
78        fun signBit r = copySign(one, r) < zero
79
80        (* If we assume that all functions produce normalised results where
81           possible, the only subnormal values will be those smaller than
82           minNormalPos. *)
83        fun isNormal x = isFinite x andalso abs x >= minNormalPos
84    
85        fun class x =
86            if isFinite x then if x == zero then ZERO
87               else if abs x >= minNormalPos then NORMAL
88               else SUBNORMAL
89            else if isNan x then NAN
90               else (* not finite and not Nan *) INF
91    
92        fun sign x = 
93            if isNan x then raise General.Domain
94            else if x == zero then 0 else if x < zero then ~1 else 1
95    end
96        
97    fun sameSign (x, y) = signBit x = signBit y
98
99    (* Returns the minimum.  In the case where one is a NaN it returns the
100       other. In that case the comparison will be false. *)
101    fun min (a: real, b: real): real = if a < b orelse isNan b then a else b
102    (* Similarly for max. *)
103    fun max (a: real, b: real): real = if a > b orelse isNan b then a else b
104
105    fun checkFloat x =
106        if isFinite x then x
107        else if isNan x then raise General.Div else raise General.Overflow
108
109    (* On certain platforms e.g. mips, toLarge does not preserve
110       the sign on nans.  We deal with the non-finite cases here. *)
111
112    (* Use the Real versions for the moment. *)
113    fun toManExp r =
114        if not (isFinite r) orelse r == zero
115            (* Nan, infinities and +/-0 all return r in the mantissa.
116               We include 0 to preserve its sign. *)
117        then {man=r, exp=0}
118        else
119        let
120            val {man, exp} = Real.toManExp(toLarge r)
121        in
122            {man = fromRealRound man, exp = exp }
123        end
124
125    and fromManExp {man, exp} =
126        if not (isFinite man) orelse man == zero
127        (* Nan, infinities and +/-0 in the mantissa all return
128           their argument. *)
129        then man
130        else fromRealRound(Real.fromManExp{man=toLarge man, exp=exp})
131    
132    fun compare (r1, r2) =
133        if r1 == r2 then General.EQUAL
134        else if r1 < r2 then General.LESS
135        else if r1 > r2 then General.GREATER
136        else raise Unordered
137
138    fun compareReal (r1, r2) =
139        if r1 == r2 then EQUAL
140        else if r1 < r2 then LESS
141        else if r1 > r2 then GREATER
142        else UNORDERED
143
144    fun op ?= (x, y) = unordered(x, y) orelse x == y
145
146    (* Although these may be built in in some architectures it's
147       probably not worth treating them specially at the moment. *)
148    fun *+ (x: real, y: real, z: real): real = x*y+z
149    and *- (x: real, y: real, z: real): real = x*y-z
150
151    val realFloor = rtsCallFastF_F "PolyRealFFloor"
152    and realCeil  = rtsCallFastF_F "PolyRealFCeil"
153    and realTrunc  = rtsCallFastF_F "PolyRealFTrunc"
154    and realRound  = rtsCallFastF_F "PolyRealFRound"
155
156    val rem = rtsCallFastFF_F "PolyRealFRem"
157
158    (* Split a real into whole and fractional parts. The fractional part must have
159       the same sign as the number even if it is zero. *)
160    fun split r =
161    let
162        val whole = realTrunc r
163        val frac = r - whole
164    in
165        { whole = whole,
166          frac =
167            if not (isFinite r)
168            then if isNan r then r else (* Infinity *) if r < zero then ~zero else zero
169            else if frac == zero then if signBit r then ~zero else zero
170            else frac }
171    end
172
173    (* Get the fractional part of a real. *)
174    fun realMod r = #frac(split r)
175    
176    val nextAfter = rtsCallFastFF_F "PolyRealFNextAfter"
177    
178    fun toLargeInt mode r = Real.toLargeInt mode (toLarge r)
179
180    local
181        (* These are defined to raise Domain rather than Overflow on Nans. *)
182        fun checkNan x = if isNan x then raise Domain else x
183        (* If int is fixed we use the hardware conversions otherwise we convert
184           it to real and use the real to arbitrary conversions. *)
185    in
186        val floor   =
187            if Bootstrap.intIsArbitraryPrecision
188            then LargeInt.toInt o toLargeInt IEEEReal.TO_NEGINF else FixedInt.toInt o floorFix o checkNan
189        and ceil    =
190            if Bootstrap.intIsArbitraryPrecision
191            then LargeInt.toInt o toLargeInt IEEEReal.TO_POSINF else FixedInt.toInt o ceilFix o checkNan
192        and trunc   =
193            if Bootstrap.intIsArbitraryPrecision
194            then LargeInt.toInt o toLargeInt IEEEReal.TO_ZERO else FixedInt.toInt o truncFix o checkNan
195        and round   =
196            if Bootstrap.intIsArbitraryPrecision
197            then LargeInt.toInt o toLargeInt IEEEReal.TO_NEAREST else FixedInt.toInt o roundFix o checkNan
198    
199        fun toInt IEEEReal.TO_NEGINF = floor
200         |  toInt IEEEReal.TO_POSINF = ceil
201         |  toInt IEEEReal.TO_ZERO = trunc
202         |  toInt IEEEReal.TO_NEAREST = round
203    end
204
205    (* The order of evaluation here is important.  See Test175. *)
206    fun fmt fm =
207        let val doFmt = Real.fmt fm in fn r => doFmt (toLarge r) end
208    
209    val toString = Real.toString o toLarge
210    
211    (* Scan input source for a valid number.  The format is the same as
212       for double precision.  Convert it using the current rounding mode. *)
213    fun scan getc src =
214        case Real.scan getc src of
215            NONE => NONE
216        |   SOME (r, a) => SOME(fromReal r, a)
217
218    val fromString = StringCvt.scanString scan
219
220    (* toDecimal: It's particularly important to handle the nan case
221       here because toLarge loses the sign bit on some architectures. *)
222    fun toDecimal r =
223    let
224        val sign = signBit r
225        val kind = class r
226    in
227        case kind of
228            ZERO => { class = ZERO, sign = sign, digits=[], exp = 0 }
229          | INF  => { class = INF, sign = sign, digits=[], exp = 0 }
230          | NAN => { class = NAN, sign = sign, digits=[], exp = 0 }
231          | _ => (* NORMAL/SUBNORMAL *) Real.toDecimal(toLarge r)
232    end
233        
234    (* Convert from decimal.  This is defined to use TO_NEAREST.
235       We need to handle NaNs specially because fromRealRound loses
236       the sign on a NaN. *)
237    local
238        val posNan = abs(zero / zero)
239        val negNan = ~posNan
240    in
241        fun fromDecimal { class = INF, sign=true, ...} = SOME negInf
242        |   fromDecimal { class = INF, sign=false, ...} = SOME posInf
243        |   fromDecimal { class = NAN, sign=true, ... } = SOME negNan
244        |   fromDecimal { class = NAN, sign=false, ... } = SOME posNan
245        |   fromDecimal arg = Option.map fromRealRound (Real.fromDecimal arg)
246    end
247
248    structure Math =
249    struct
250        type real = real
251
252        val sqrt  = rtsCallFastF_F "PolyRealFSqrt"
253        and sin   = rtsCallFastF_F "PolyRealFSin"
254        and cos   = rtsCallFastF_F "PolyRealFCos"
255        and atan  = rtsCallFastF_F "PolyRealFArctan"
256        and exp   = rtsCallFastF_F "PolyRealFExp"
257        and ln    = rtsCallFastF_F "PolyRealFLog"
258        and tan   = rtsCallFastF_F "PolyRealFTan"
259        and asin  = rtsCallFastF_F "PolyRealFArcSin"
260        and acos  = rtsCallFastF_F "PolyRealFArcCos"
261        and log10 = rtsCallFastF_F "PolyRealFLog10"
262        and sinh  = rtsCallFastF_F "PolyRealFSinh"
263        and cosh  = rtsCallFastF_F "PolyRealFCosh"
264        and tanh  = rtsCallFastF_F "PolyRealFTanh"
265
266        val atan2 = rtsCallFastFF_F "PolyRealFAtan2"
267        val pow = rtsCallFastFF_F "PolyRealFPow"
268
269        (* Derived values. *)
270        val e = exp one
271        val pi = four * atan one
272    end
273    
274
275    (* Converter for literal constants.  Copied from Real. *)
276    local
277        fun convReal (s: string) : real =
278        let
279            (* Set the rounding mode to TO_NEAREST whatever the current
280               rounding mode.  Otherwise the result of compiling a piece of
281               code with a literal constant could depend on what the rounding
282               mode was set to. We should always support TO_NEAREST. *)
283            val oldRounding = IEEEReal.getRoundingMode()
284            val () = IEEEReal.setRoundingMode IEEEReal.TO_NEAREST
285            val scanResult = StringCvt.scanString scan s
286            val () = IEEEReal.setRoundingMode oldRounding
287        in
288            case scanResult of
289                NONE => raise RunCall.Conversion "Invalid real constant"
290              | SOME res => res
291        end
292    in
293        (* Install this as a conversion function for real literals. *)
294        val (): unit = RunCall.addOverload convReal "convReal"
295    end
296   
297end;
298
299
300val () = RunCall.addOverload Real32.>= ">="
301and () = RunCall.addOverload Real32.<= "<="
302and () = RunCall.addOverload Real32.>  ">"
303and () = RunCall.addOverload Real32.<  "<"
304and () = RunCall.addOverload Real32.+ "+"
305and () = RunCall.addOverload Real32.- "-"
306and () = RunCall.addOverload Real32.* "*"
307and () = RunCall.addOverload Real32.~ "~"
308and () = RunCall.addOverload Real32.abs "abs"
309and () = RunCall.addOverload Real32./ "/";
310
311
312(* Install print function. *)
313local
314    fun print_real _ _ (r: Real32.real) =
315        PolyML.PrettyString(Real32.fmt (StringCvt.GEN(SOME 10)) r)
316in
317    val () = PolyML.addPrettyPrinter print_real;
318end;
319