|
21 | 21 | can_cast,
|
22 | 22 | write_zeros,
|
23 | 23 | apply_read_scaling,
|
| 24 | + working_type, |
| 25 | + best_write_scale_ftype, |
| 26 | + better_float_of, |
24 | 27 | int_scinter_ftype,
|
25 | 28 | allopen,
|
26 | 29 | make_dt_codes,
|
27 | 30 | native_code,
|
28 | 31 | shape_zoom_affine,
|
29 |
| - rec2dict) |
| 32 | + rec2dict, |
| 33 | + IUINT_TYPES, |
| 34 | + FLOAT_TYPES, |
| 35 | + NUMERIC_TYPES) |
30 | 36 |
|
31 |
| -from ..casting import flt2nmant, floor_log2 |
| 37 | +from ..casting import flt2nmant, FloatingError, floor_log2 |
32 | 38 |
|
33 | 39 | from numpy.testing import (assert_array_almost_equal,
|
34 | 40 | assert_array_equal)
|
@@ -121,6 +127,26 @@ def test_a2f_intercept_scale():
|
121 | 127 | assert_array_equal(data_back, (arr-1) / 2.0)
|
122 | 128 |
|
123 | 129 |
|
| 130 | +def test_a2f_upscale(): |
| 131 | + # Test working type scales with needed range |
| 132 | + info = np.finfo(np.float32) |
| 133 | + # Test values discovered from stress testing. The largish value (2**115) |
| 134 | + # overflows to inf after the intercept is subtracted, using float32 as the |
| 135 | + # working precision. The difference between inf and this value is lost. |
| 136 | + arr = np.array([[info.min, 2**115, info.max]], dtype=np.float32) |
| 137 | + slope = np.float32(2**121) |
| 138 | + inter = info.min |
| 139 | + str_io = BytesIO() |
| 140 | + # We need to provide mn, mx for function to be able to calculate upcasting |
| 141 | + array_to_file(arr, str_io, np.uint8, intercept=inter, divslope=slope, |
| 142 | + mn = info.min, mx = info.max) |
| 143 | + raw = array_from_file(arr.shape, np.uint8, str_io) |
| 144 | + back = apply_read_scaling(raw, slope, inter) |
| 145 | + top = back - arr |
| 146 | + score = np.abs(top / arr) |
| 147 | + assert_true(np.all(score < 10)) |
| 148 | + |
| 149 | + |
124 | 150 | def test_a2f_min_max():
|
125 | 151 | str_io = BytesIO()
|
126 | 152 | for in_dt in (np.float32, np.int8):
|
@@ -270,6 +296,110 @@ def test_int_scinter():
|
270 | 296 | assert_equal(int_scinter_ftype(np.int8, -1e38, 0.0), np.float64)
|
271 | 297 |
|
272 | 298 |
|
| 299 | +def test_working_type(): |
| 300 | + # Which type do input types with slope and inter cast to in numpy? |
| 301 | + # Wrapper function because we need to use the dtype str for comparison. We |
| 302 | + # need this because of the very confusing np.int32 != np.intp (on 32 bit). |
| 303 | + def wt(*args, **kwargs): |
| 304 | + return np.dtype(working_type(*args, **kwargs)).str |
| 305 | + d1 = np.atleast_1d |
| 306 | + for in_type in NUMERIC_TYPES: |
| 307 | + in_ts = np.dtype(in_type).str |
| 308 | + assert_equal(wt(in_type), in_ts) |
| 309 | + assert_equal(wt(in_type, 1, 0), in_ts) |
| 310 | + assert_equal(wt(in_type, 1.0, 0.0), in_ts) |
| 311 | + in_val = d1(in_type(0)) |
| 312 | + for slope_type in NUMERIC_TYPES: |
| 313 | + sl_val = slope_type(1) # no scaling, regardless of type |
| 314 | + assert_equal(wt(in_type, sl_val, 0.0), in_ts) |
| 315 | + sl_val = slope_type(2) # actual scaling |
| 316 | + out_val = in_val / d1(sl_val) |
| 317 | + assert_equal(wt(in_type, sl_val), out_val.dtype.str) |
| 318 | + for inter_type in NUMERIC_TYPES: |
| 319 | + i_val = inter_type(0) # no scaling, regardless of type |
| 320 | + assert_equal(wt(in_type, 1, i_val), in_ts) |
| 321 | + i_val = inter_type(1) # actual scaling |
| 322 | + out_val = in_val - d1(i_val) |
| 323 | + assert_equal(wt(in_type, 1, i_val), out_val.dtype.str) |
| 324 | + # Combine scaling and intercept |
| 325 | + out_val = (in_val - d1(i_val)) / d1(sl_val) |
| 326 | + assert_equal(wt(in_type, sl_val, i_val), out_val.dtype.str) |
| 327 | + # Confirm that type codes and dtypes work as well |
| 328 | + f32s = np.dtype(np.float32).str |
| 329 | + assert_equal(wt('f4', 1, 0), f32s) |
| 330 | + assert_equal(wt(np.dtype('f4'), 1, 0), f32s) |
| 331 | + |
| 332 | + |
| 333 | +def test_better_float(): |
| 334 | + # Better float function |
| 335 | + def check_against(f1, f2): |
| 336 | + return f1 if FLOAT_TYPES.index(f1) >= FLOAT_TYPES.index(f2) else f2 |
| 337 | + for first in FLOAT_TYPES: |
| 338 | + for other in IUINT_TYPES + np.sctypes['complex']: |
| 339 | + assert_equal(better_float_of(first, other), first) |
| 340 | + assert_equal(better_float_of(other, first), first) |
| 341 | + for other2 in IUINT_TYPES + np.sctypes['complex']: |
| 342 | + assert_equal(better_float_of(other, other2), np.float32) |
| 343 | + assert_equal(better_float_of(other, other2, np.float64), |
| 344 | + np.float64) |
| 345 | + for second in FLOAT_TYPES: |
| 346 | + assert_equal(better_float_of(first, second), |
| 347 | + check_against(first, second)) |
| 348 | + # Check codes and dtypes work |
| 349 | + assert_equal(better_float_of('f4', 'f8', 'f4'), np.float64) |
| 350 | + assert_equal(better_float_of('i4', 'i8', 'f8'), np.float64) |
| 351 | + |
| 352 | + |
| 353 | +def have_longer_double(): |
| 354 | + # True if longdouble has more precision than float64, and longdouble is |
| 355 | + # something we can rely on |
| 356 | + nmant_64 = flt2nmant(np.float64) # should be 52 |
| 357 | + try: |
| 358 | + nmant_ld = flt2nmant(np.longdouble) |
| 359 | + except FloatingError: |
| 360 | + return False |
| 361 | + return nmant_ld > nmant_64 |
| 362 | + |
| 363 | + |
| 364 | +def test_best_write_scale_ftype(): |
| 365 | + # Test best write scaling type |
| 366 | + # Types return better of (default, array type) unless scale overflows. |
| 367 | + # Return float type cannot be less capable than the input array type |
| 368 | + for dtt in IUINT_TYPES + FLOAT_TYPES: |
| 369 | + arr = np.arange(10, dtype=dtt) |
| 370 | + assert_equal(best_write_scale_ftype(arr, 1, 0), |
| 371 | + better_float_of(dtt, np.float32)) |
| 372 | + assert_equal(best_write_scale_ftype(arr, 1, 0, np.float64), |
| 373 | + better_float_of(dtt, np.float64)) |
| 374 | + assert_equal(best_write_scale_ftype(arr, np.float32(2), 0), |
| 375 | + better_float_of(dtt, np.float32)) |
| 376 | + assert_equal(best_write_scale_ftype(arr, 1, np.float32(1)), |
| 377 | + better_float_of(dtt, np.float32)) |
| 378 | + # Overflowing ints with scaling results in upcast |
| 379 | + best_vals = ((np.float32, np.float64),) |
| 380 | + if have_longer_double(): |
| 381 | + best_vals += ((np.float64, np.longdouble),) |
| 382 | + for lower_t, higher_t in best_vals: |
| 383 | + # Information on this float |
| 384 | + t_max = np.finfo(lower_t).max |
| 385 | + nmant = flt2nmant(lower_t) # number of significand digits |
| 386 | + big_delta = lower_t(2**(floor_log2(t_max) - nmant)) # delta below max |
| 387 | + # Even large values that don't overflow don't change output |
| 388 | + arr = np.array([0, t_max], dtype=lower_t) |
| 389 | + assert_equal(best_write_scale_ftype(arr, 1, 0), lower_t) |
| 390 | + # Scaling > 1 reduces output values, so no upcast needed |
| 391 | + assert_equal(best_write_scale_ftype(arr, lower_t(1.01), 0), lower_t) |
| 392 | + # Scaling < 1 increases values, so upcast may be needed (and is here) |
| 393 | + assert_equal(best_write_scale_ftype(arr, lower_t(0.99), 0), higher_t) |
| 394 | + # Large minus offset on large array can cause upcast |
| 395 | + assert_equal(best_write_scale_ftype(arr, 1, -big_delta/2.01), lower_t) |
| 396 | + assert_equal(best_write_scale_ftype(arr, 1, -big_delta/2.0), higher_t) |
| 397 | + # With infs already in input, default type returns |
| 398 | + arr[0] = np.inf |
| 399 | + assert_equal(best_write_scale_ftype(arr, lower_t(0.5), 0), lower_t) |
| 400 | + arr[0] = -np.inf |
| 401 | + assert_equal(best_write_scale_ftype(arr, lower_t(0.5), 0), lower_t) |
| 402 | + |
273 | 403 |
|
274 | 404 | def test_can_cast():
|
275 | 405 | tests = ((np.float32, np.float32, True, True, True),
|
|
0 commit comments