Skip to content

Commit 9ef0301

Browse files
farkmarnumschlafly
andauthored
Fix floating point issues (#194)
* Fix issue with floating point imprecision & integer overflow * Add test * Fix issue with old version of numpy --------- Co-authored-by: Eddie Schlafly <eschlafly@stsci.edu>
1 parent 3022876 commit 9ef0301

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

romanisim/image.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,19 @@ def simulate_counts_generic(image, exptime, objlist=None, psf=None,
469469
image.quantize()
470470
rng_numpy_seed = rng.raw()
471471
rng_numpy = np.random.default_rng(rng_numpy_seed)
472+
473+
# NOTE: Here, we convert the array of float32 values to int32 (i4), so
474+
# we need to "clip" them to be in the valid range first. However, just
475+
# clipping with the maximum int32 value (2^31 - 1) isn't sufficient.
476+
# This is because `np.float32(2**31 - 1)` is the same value as
477+
# `np.float32(2**31)` due to floating point imprecision, and on some
478+
# architectures, converting that value to int32 rolls over to a negative
479+
# number. To resolve, we use `np.nextafter` to get the previous floating
480+
# point number, which is roughly 2^31 - 128.
481+
MAX_SAFE_VALUE = np.nextafter(2**31 - 1, 0, dtype=np.float32)
472482
image.array[:, :] = rng_numpy.binomial(
473-
np.clip(image.array, 0, 2**31 - 1).astype('i4'), flat / maxflat)
483+
np.clip(image.array, 0, MAX_SAFE_VALUE).astype("i4"), flat / maxflat
484+
)
474485

475486
if dark is not None:
476487
workim = image * 0

romanisim/tests/test_image.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,19 @@ def test_simulate_counts_generic():
411411
assert np.sum(objinfo['counts'] > 0) == 0
412412
# these sources should be out of bounds
413413

414+
### Assert that out-of-range values do not cause integer overflow:
415+
try:
416+
# Have numpy raise on warnings:
417+
np.seterr(all='raise')
418+
# Test simulating with a value that is out of range for an int32
419+
im9 = im.copy()
420+
im9.array[0][0] = np.float32(2**40)
421+
image.simulate_counts_generic(im9, exptime, sky=sky, flat=0.5, zpflux=zpflux)
422+
finally:
423+
# revert numpy warning settings to default
424+
np.seterr(all='print')
425+
426+
414427

415428
def test_simulate_counts():
416429
imdict = set_up_image_rendering_things()

0 commit comments

Comments
 (0)