ToolConvoyToolConvoyv2.6

int vs float:数字是怎么存的

int 整数精确但范围有限。float 范围大但有舍入误差。大多数 bug 都来自把它们混着用。

广告

int vs float:数字是怎么存的

int(整数)和 float(浮点数)是编程语言表示一个数字的两种方式。这个选择在你的代码碰到真实数据时立刻会感觉到后果。

int 是精确可数的:42 就是 42,没有舍入、没有意外。float 是近似的、连续的:IEEE 754 双精度下 0.1 + 0.2 等于 0.30000000000000004,谁第一次遇到都会惊讶。

精确范围

int 是有限的。大多数现代系统中,int32 能存 2^32 个不同的值(−2,147,483,648 到 2,147,483,647)。int64 能存 2^64。超过这些,类型本身就无法表示这个数了。

float 几乎是连续的。IEEE 754 float64(C 系语言里的 “double”)能表示大约 10^−308 到 10^308 的数,约 15-17 位有效十进制数字。代价:该范围内的很多值无法精确表示,很多值(比如 0.1)会被舍入。

为什么 0.1 + 0.2 ≠ 0.3

在二进制浮点中,0.1 不是干净的分数。正如十进制的 1/3 是 0.3333…,二进制的 1/10 是 0.000110011001100… 循环。float 必须舍入到有限的位数,实际存进去的值略偏。

两个舍入后的数相加,舍入误差会累积。0.1 + 0.2 比 0.3 略大一点,float 就打印出 0.30000000000000004。

这不是 bug,是 IEEE 754 在正常工作。bug 是把 float 用在了本该精确的事情上。

怎么选

int 用于:

  • 计数(行数、索引、长度)
  • 以最小单位存钱(分、聪、wei——永远用整数最小单位)
  • 处理 ID、按秒/毫秒存的时间戳、字节数
  • 需要精确相等语义(a == b 就是你想的那个意思)

float 用于:

  • 物理量计算(距离、重量、角度)
  • 聚合求和(精确性比范围重要度低)
  • 受益于大范围数值的数学(科学计算、图形)
  • 接受带容差的比较(abs(a - b) < epsilon)

混用的陷阱

最隐蔽的 bug 都来自 int 和 float 混用。在动态类型语言中:

  • 5 / 2 — 很多语言里是整数除法得 2;JavaScript 里得 2.5
  • 5.0 / 2 — 任何语言都得 2.5
  • int(2.5) 与 Math.floor(2.5) — 大多数语言都得 2;JavaScript 因为没有 int 类型会让你意外

超过 int64 的整数(超大的 ID、高精度时间戳、区块链金额)需要特殊类型:Python 的 int 是任意精度;C++ 有 __int128 和 boost::multiprecision;JavaScript 数字始终是 float64(超过 2^53 的整数用 BigInt)。

货币与经典教训

五十年计算史中的绝大多数金融 bug 都来自用 float 存钱。修法是通用的:以最小整数单位存钱(分、便士、聪)。1999 分就是 19.99 元,永远不会近似。转成人类可读的数字只在显示时发生,使用做十进制算术的库(或配合显式舍入规则的整数除法)。

工具

处理嵌在 JSON 或 CSV 数据里的数字——你可能遇到看起来像 float 的 int 或反过来——JSON to CSV 和 JSON to YAML 在转换中忠实地保留值的类型不改变。

SQL 中,int vs float 列的选择会决定下游所有查询的行为,SQL Formatter 帮你看清局面。

另见

Related tools

广告