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