首页 > 解决方案 > 如何安全地从 float64 圆形和钳位到 int64?

问题描述

这个问题是关于 python/numpy 的,但它也可能适用于其他语言。

如何改进以下代码以在转换期间将大浮点值安全地钳制为最大 int64 值?(理想情况下,它应该仍然有效。)

import numpy as np

def int64_from_clipped_float64(x, dtype=np.int64):
  x = np.round(x)
  x = np.clip(x, np.iinfo(dtype).min, np.iinfo(dtype).max)
  # The problem is that np.iinfo(dtype).max is imprecisely approximated as a
  # float64, and the approximation leads to overflow in the conversion.
  return x.astype(dtype)

for x in [-3.6, 0.4, 1.7, 1e18, 1e25]:
  x = np.array(x, dtype=np.float64)
  print(f'x = {x:<10}  result = {int64_from_clipped_float64(x)}')

# x = -3.6        result = -4
# x = 0.4         result = 0
# x = 1.7         result = 2
# x = 1e+18       result = 1000000000000000000
# x = 1e+25       result = -9223372036854775808

标签: python-3.xnumpyfloating-pointinteger-overflow

解决方案


问题是最大np.int64的是 2 63 - 1,不能用浮点数表示。同样的问题不会发生在另一端,因为 -2 63是完全可表示的。

所以在浮点空间(用于检测)和整数空间(用于校正)中进行剪裁一半:

def int64_from_clipped_float64(x, dtype=np.int64):
    assert x.dtype == np.float64

    limits = np.iinfo(dtype)
    too_small = x <= np.float64(limits.min)
    too_large = x >= np.float64(limits.max)
    ix = x.astype(dtype)
    ix[too_small] = limits.min
    ix[too_large] = limits.max
    return ix

推荐阅读