1"""

2 Machine arithmetic - determine the parameters of the

3 floating-point arithmetic system

4

5 Author: Pearu Peterson, September 2003

6

7"""

8__all__ = ['MachAr']

9

10 from numpy.core.fromnumeric import any

11 from numpy.core._ufunc_config import errstate

12 from numpy.core.overrides import set_module

13

14# Need to speed this up...especially for longfloat

15

16# Deprecated 2021-10-20, NumPy 1.22

17@set_module('numpy')

18 class MachAr:

19 """

20 Diagnosing machine parameters.

21

22 Attributes

23 ----------

24 ibeta : int

25 Radix in which numbers are represented.

26 it : int

27 Number of base-`ibeta` digits in the floating point mantissa M.

28 machep : int

29 Exponent of the smallest (most negative) power of `ibeta` that,

30 added to 1.0, gives something different from 1.0

31 eps : float

32 Floating-point number ``beta**machep`` (floating point precision)

33 negep : int

34 Exponent of the smallest power of `ibeta` that, subtracted

35 from 1.0, gives something different from 1.0.

36 epsneg : float

37 Floating-point number ``beta**negep``.

38 iexp : int

39 Number of bits in the exponent (including its sign and bias).

40 minexp : int

41 Smallest (most negative) power of `ibeta` consistent with there

42 being no leading zeros in the mantissa.

43 xmin : float

44 Floating-point number ``beta**minexp`` (the smallest [in

45 magnitude] positive floating point number with full precision).

46 maxexp : int

47 Smallest (positive) power of `ibeta` that causes overflow.

48 xmax : float

49 ``(1-epsneg) * beta**maxexp`` (the largest [in magnitude]

50 usable floating value).

51 irnd : int

52 In ``range(6)``, information on what kind of rounding is done

53 in addition, and on how underflow is handled.

54 ngrd : int

55 Number of 'guard digits' used when truncating the product

56 of two mantissas to fit the representation.

57 epsilon : float

58 Same as `eps`.

59 tiny : float

60 An alias for `smallest_normal`, kept for backwards compatibility.

61 huge : float

62 Same as `xmax`.

63 precision : float

64 ``- int(-log10(eps))``

65 resolution : float

66 ``- 10**(-precision)``

67 smallest_normal : float

68 The smallest positive floating point number with 1 as leading bit in

69 the mantissa following IEEE-754. Same as `xmin`.

70 smallest_subnormal : float

71 The smallest positive floating point number with 0 as leading bit in

72 the mantissa following IEEE-754.

73

74 Parameters

75 ----------

76 float_conv : function, optional

77 Function that converts an integer or integer array to a float

78 or float array. Default is `float`.

79 int_conv : function, optional

80 Function that converts a float or float array to an integer or

81 integer array. Default is `int`.

82 float_to_float : function, optional

83 Function that converts a float array to float. Default is `float`.

84 Note that this does not seem to do anything useful in the current

85 implementation.

86 float_to_str : function, optional

87 Function that converts a single float to a string. Default is

88 ``lambda v:'%24.16e' %v``.

89 title : str, optional

90 Title that is printed in the string representation of `MachAr`.

91

92 See Also

93 --------

94 finfo : Machine limits for floating point types.

95 iinfo : Machine limits for integer types.

96

97 References

98 ----------

99 .. [1] Press, Teukolsky, Vetterling and Flannery,

100 "Numerical Recipes in C++," 2nd ed,

101 Cambridge University Press, 2002, p. 31.

102

103 """

104

105 def __init__(self, float_conv=float,int_conv=int,

106 float_to_float=float,

107 float_to_str=lambda v:'%24.16e' % v,

108 title='Python floating point number'):

109 """

110

111 float_conv - convert integer to float (array)

112 int_conv - convert float (array) to integer

113 float_to_float - convert float array to float

114 float_to_str - convert array float to str

115 title - description of used floating point numbers

116

117 """

118 # We ignore all errors here because we are purposely triggering

119 # underflow to detect the properties of the runninng arch.

120 with errstate(under='ignore'):

121 self._do_init(float_conv, int_conv, float_to_float, float_to_str, title)

122

123 def _do_init(self, float_conv, int_conv, float_to_float, float_to_str, title):

124 max_iterN = 10000

125 msg = "Did not converge after %d tries with %s"

126 one = float_conv(1)

127 two = one + one

128 zero = one - one

129

130 # Do we really need to do this? Aren't they 2 and 2.0?

131 # Determine ibeta and beta

132 a = one

133 for _ in range(max_iterN):

134 a = a + a

135 temp = a + one

136 temp1 = temp - a

137 if any(temp1 - one != zero):

138 break

139 else:

140 raise RuntimeError(msg % (_, one.dtype))

141 b = one

142 for _ in range(max_iterN):

143 b = b + b

144 temp = a + b

145 itemp = int_conv(temp-a)

146 if any(itemp != 0):

147 break

148 else:

149 raise RuntimeError(msg % (_, one.dtype))

150 ibeta = itemp

151 beta = float_conv(ibeta)

152

153 # Determine it and irnd

154 it = -1

155 b = one

156 for _ in range(max_iterN):

157 it = it + 1

158 b = b * beta

159 temp = b + one

160 temp1 = temp - b

161 if any(temp1 - one != zero):

162 break

163 else:

164 raise RuntimeError(msg % (_, one.dtype))

165

166 betah = beta / two

167 a = one

168 for _ in range(max_iterN):

169 a = a + a

170 temp = a + one

171 temp1 = temp - a

172 if any(temp1 - one != zero):

173 break

174 else:

175 raise RuntimeError(msg % (_, one.dtype))

176 temp = a + betah

177 irnd = 0

178 if any(temp-a != zero):

179 irnd = 1

180 tempa = a + beta

181 temp = tempa + betah

182 if irnd == 0 and any(temp-tempa != zero):

183 irnd = 2

184

185 # Determine negep and epsneg

186 negep = it + 3

187 betain = one / beta

188 a = one

189 for i in range(negep):

190 a = a * betain

191 b = a

192 for _ in range(max_iterN):

193 temp = one - a

194 if any(temp-one != zero):

195 break

196 a = a * beta

197 negep = negep - 1

198 # Prevent infinite loop on PPC with gcc 4.0:

199 if negep < 0:

200 raise RuntimeError("could not determine machine tolerance "

201 "for 'negep', locals() -> %s" % (locals()))

202 else:

203 raise RuntimeError(msg % (_, one.dtype))

204 negep = -negep

205 epsneg = a

206

207 # Determine machep and eps

208 machep = - it - 3

209 a = b

210

211 for _ in range(max_iterN):

212 temp = one + a

213 if any(temp-one != zero):

214 break

215 a = a * beta

216 machep = machep + 1

217 else:

218 raise RuntimeError(msg % (_, one.dtype))

219 eps = a

220

221 # Determine ngrd

222 ngrd = 0

223 temp = one + eps

224 if irnd == 0 and any(temp*one - one != zero):

225 ngrd = 1

226

227 # Determine iexp

228 i = 0

229 k = 1

230 z = betain

231 t = one + eps

232 nxres = 0

233 for _ in range(max_iterN):

234 y = z

235 z = y*y

236 a = z*one # Check here for underflow

237 temp = z*t

238 if any(a+a == zero) or any(abs(z) >= y):

239 break

240 temp1 = temp * betain

241 if any(temp1*beta == z):

242 break

243 i = i + 1

244 k = k + k

245 else:

246 raise RuntimeError(msg % (_, one.dtype))

247 if ibeta != 10:

248 iexp = i + 1

249 mx = k + k

250 else:

251 iexp = 2

252 iz = ibeta

253 while k >= iz:

254 iz = iz * ibeta

255 iexp = iexp + 1

256 mx = iz + iz - 1

257

258 # Determine minexp and xmin

259 for _ in range(max_iterN):

260 xmin = y

261 y = y * betain

262 a = y * one

263 temp = y * t

264 if any((a + a) != zero) and any(abs(y) < xmin):

265 k = k + 1

266 temp1 = temp * betain

267 if any(temp1*beta == y) and any(temp != y):

268 nxres = 3

269 xmin = y

270 break

271 else:

272 break

273 else:

274 raise RuntimeError(msg % (_, one.dtype))

275 minexp = -k

276

277 # Determine maxexp, xmax

278 if mx <= k + k - 3 and ibeta != 10:

279 mx = mx + mx

280 iexp = iexp + 1

281 maxexp = mx + minexp

282 irnd = irnd + nxres

283 if irnd >= 2:

284 maxexp = maxexp - 2

285 i = maxexp + minexp

286 if ibeta == 2 and not i:

287 maxexp = maxexp - 1

288 if i > 20:

289 maxexp = maxexp - 1

290 if any(a != y):

291 maxexp = maxexp - 2

292 xmax = one - epsneg

293 if any(xmax*one != xmax):

294 xmax = one - beta*epsneg

295 xmax = xmax / (xmin*beta*beta*beta)

296 i = maxexp + minexp + 3

297 for j in range(i):

298 if ibeta == 2:

299 xmax = xmax + xmax

300 else:

301 xmax = xmax * beta

302

303 smallest_subnormal = abs(xmin / beta ** (it))

304

305 self.ibeta = ibeta

306 self.it = it

307 self.negep = negep

308 self.epsneg = float_to_float(epsneg)

309 self._str_epsneg = float_to_str(epsneg)

310 self.machep = machep

311 self.eps = float_to_float(eps)

312 self._str_eps = float_to_str(eps)

313 self.ngrd = ngrd

314 self.iexp = iexp

315 self.minexp = minexp

316 self.xmin = float_to_float(xmin)

317 self._str_xmin = float_to_str(xmin)

318 self.maxexp = maxexp

319 self.xmax = float_to_float(xmax)

320 self._str_xmax = float_to_str(xmax)

321 self.irnd = irnd

322

323 self.title = title

324 # Commonly used parameters

325 self.epsilon = self.eps

326 self.tiny = self.xmin

327 self.huge = self.xmax

328 self.smallest_normal = self.xmin

329 self._str_smallest_normal = float_to_str(self.xmin)

330 self.smallest_subnormal = float_to_float(smallest_subnormal)

331 self._str_smallest_subnormal = float_to_str(smallest_subnormal)

332

333 import math

334 self.precision = int(-math.log10(float_to_float(self.eps)))

335 ten = two + two + two + two + two

336 resolution = ten ** (-self.precision)

337 self.resolution = float_to_float(resolution)

338 self._str_resolution = float_to_str(resolution)

339

340 def __str__(self):

341 fmt = (

342 'Machine parameters for %(title)s\n'

343 '---------------------------------------------------------------------\n'

344 'ibeta=%(ibeta)s it=%(it)s iexp=%(iexp)s ngrd=%(ngrd)s irnd=%(irnd)s\n'

345 'machep=%(machep)s eps=%(_str_eps)s (beta**machep == epsilon)\n'

346 'negep =%(negep)s epsneg=%(_str_epsneg)s (beta**epsneg)\n'

347 'minexp=%(minexp)s xmin=%(_str_xmin)s (beta**minexp == tiny)\n'

348 'maxexp=%(maxexp)s xmax=%(_str_xmax)s ((1-epsneg)*beta**maxexp == huge)\n'

349 'smallest_normal=%(smallest_normal)s '

350 'smallest_subnormal=%(smallest_subnormal)s\n'

351 '---------------------------------------------------------------------\n'

352 )

353 return fmt % self.__dict__

354

355

356 if __name__ == '__main__':

357 print(MachAr())