整数与精确数值与浮点与舍入

共 31 题
📑 题目列表 31 题
#
★★★

1. MySQL 中 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT 的存储空间与方言特性?

请说明 MySQL 中 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT 这五种整数类型各自的存储字节数、取值范围,以及它们在 MySQL 官方语法中的方言特性(如显示宽度)?

  • 各整数类型的存储字节数与取值范围
  • MySQL 的显示宽度(display width)特性
  • 有符号与无符号的区别

MySQL 的整数类型按存储字节数划分:TINYINT 占 1 字节(-128 到 127),SMALLINT 占 2 字节(-32768 到 32767),MEDIUMINT 占 3 字节(约 ±8388607),INT 占 4 字节(约 ±21 亿),BIGINT 占 8 字节(约 ±922 亿亿)。可使用 UNSIGNED 修饰符使范围变为非负;例如 TINYINT(1) 常用于模拟布尔值。MySQL 8.0 之前的 INT(11) 中的 11 是显示宽度(zerofill 时影响填充),不影响存储范围,MySQL 8.0 已废弃该显示宽度特性。

选择整数类型本质是"范围 vs 存储空间"的权衡:能容纳业务数据的最小类型最节省空间,但也要预估未来增长。INT 是默认推荐,BIGINT 用于自增主键或超大数值。

CREATE TABLE int_test (
  a TINYINT,        -- 1 字节
  b SMALLINT,       -- 2 字节
  c MEDIUMINT,      -- 3 字节
  d INT,            -- 4 字节
  e BIGINT          -- 8 字节
);
#
★★★

2. NUMERIC/DECIMAL 精确数值的存储与精度损失?

请说明 NUMERIC/DECIMAL 精确数值类型在数据库中的存储方式,以及为什么它不会产生浮点数的精度损失?

  • NUMERIC/DECIMAL 以十进制整数存储
  • 精度(precision)与标度(scale)
  • 与浮点类型的本质区别

NUMERIC 和 DECIMAL 是精确数值类型,底层以十进制整数字节序列存储,而非二进制浮点表示,因此不会出现 0.1+0.2≠0.3 这类二进制舍入误差。DECIMAL(p,s) 表示共 p 位有效数字、其中 s 位在小数点后。PostgreSQL 中 NUMERIC 与 DECIMAL 完全等价;MySQL 中两者也等价。存储时按每 4 位十进制一组(base-10000)打包,因此存储空间与精度相关。

金额、税率等需要精确十进制的场景必须用 NUMERIC/DECIMAL,避免浮点二进制表示导致的对账不平。代价是运算速度慢于原生浮点。

CREATE TABLE price (amount DECIMAL(10,2)); -- 最多 10 位,其中 2 位小数
INSERT INTO price VALUES (99999999.99);    -- 精确存储
#
★★★

3. PostgreSQL 中 smallint(int2)、int(int4)、bigint(int8)的存储空间(2、4、8 字节)与取值范围?

请说明 PostgreSQL 中 smallint、int、bigint 三种整数类型的存储空间(int2/int4/int8)与取值范围?

  • int2/int4/int8 的存储字节数
  • 各类型的取值范围
  • 别名与原名的关系

PostgreSQL 中 smallint 即 int2,占 2 字节,范围 -32768 到 32767;integer 即 int、int4,占 4 字节,范围约 -21 亿到 +21 亿;bigint 即 int8,占 8 字节,范围约 -9223372036854775808 到 9223372036854775807。int2、int4、int8 是类型别名,两者等价。另有 serial/bigserial 自动生成序列。

明确各类型存储与范围有利于选择合适的主键类型与表结构设计,避免数据溢出或浪费空间。

SELECT 32767::smallint AS ok;      -- 成功
SELECT 32768::smallint;            -- 报错:smallint out of range
#
★★★

4. 整数溢出的检测,PostgreSQL 整数溢出报错 vs MySQL 的 wrap-around?

请说明 PostgreSQL 与 MySQL 在整数溢出时的行为差异,以及各自的处理方式?

  • PostgreSQL 溢出报错
  • MySQL 默认 wrap-around(回绕)与严格模式
  • 严格模式 SQL_MODE 的影响

PostgreSQL 在整数运算溢出时直接抛出类似 "integer out of range" 的错误,强制开发者处理。MySQL 在非严格模式下对整数溢出采取"回绕"(wrap-around),即按模 2^n 截断取低 n 位,产生错误但看似合理的结果;在严格模式(sql_mode 包含 STRICT_TRANS_TABLES)下则报错。因此 MySQL 的默认行为更隐蔽,容易掩盖 bug。

PostgreSQL 的报错语义更安全,能尽早暴露问题;MySQL 依赖严格模式来获得类似保护,生产环境应开启严格模式。

-- PostgreSQL
SELECT 2147483647 + 1; -- ERROR: integer out of range
-- MySQL 严格模式
SET sql_mode='STRICT_TRANS_TABLES';
SELECT 2147483647 + 1; -- ERROR 1264: Out of range value
#
★★★

5. 货币类型 MONEY 的局限(精度、地区)与 NUMERIC 的取舍?

请说明 PostgreSQL 中 MONEY 类型的局限(精度、地区化)以及与 NUMERIC 的取舍?

  • MONEY 的存储与精度固定
  • 地区化(locale)影响显示
  • 与 NUMERIC 的取舍

PostgreSQL 的 MONEY 类型存储一个固定精度的有符号定长值,其小数位数由 lc_monetary 区域设置决定,因此不同地区显示不同(如 $1,000.00 与 1.000,00)。MONEY 的精度受地区约束且运算可能产生非 MONEY 值,数据库迁移时兼容性差。因此普遍推荐使用 NUMERIC(p,s) 存储金额,以获得固定精度、可移植性与运算灵活性。

NUMERIC 是更通用的精确数值方案,MONEY 的区域化显示在跨语言/跨时区应用中反而成为负担,故多数团队弃用 MONEY。

SELECT cast('$1,000.00' AS money); -- 区域化的输入输出
#
★★★

6. INT 与 BIGINT 的选择,未来数据增长估算?

在选择 INT 与 BIGINT 作为主键或 ID 时,应如何估算未来数据增长?

  • INT 约 21 亿的上限
  • 增长估算与留余量
  • 大表单列主键的选择

INT 最大值约 21 亿,对多数组装足够,但若表示用户 ID、订单号等可能长期高频增长的业务,或未来可能合并分库、批量导入,则应当预留 BIGINT。判断标准:估算每年增量 × 生命周期年限,若接近或超过 21 亿则选 BIGINT。BIGINT 占 8 字节,索引略大,但避免未来 ALTER 迁移的昂贵成本。

主键类型迁移(INT→BIGINT)代价极高(需重建表与索引),宁可初始选择 BIGINT 也不愿后期迁移。这是典型的"前瞻性成本"考量。

CREATE TABLE users (id BIGSERIAL PRIMARY KEY); -- 预估量大,用 8 字节主键
#
★★★

7. DECIMAL 与 NUMERIC 的关系?

请说明 DECIMAL 与 NUMERIC 在 PostgreSQL 与 MySQL 中的关系?

  • 两者等价性
  • 语法别名
  • 各数据库实现的一致性

在 PostgreSQL 与 MySQL 中,DECIMAL 与 NUMERIC 是同一精确数值类型的两种别名,完全等价,可互换使用,均支持 DECIMAL(p,s)/NUMERIC(p,s) 声明精度与标度。两者均以精确十进制存储,无二进制舍入误差。ANSI SQL 标准允许两类型有细微实现差异,但主流数据库实现为等价。

面试常考"是不是同一个类型"这一等价性陷阱,回答其为完全等价的别名即可,并说明精度与标度参数的语义一样。

CREATE TABLE t (a DECIMAL(10,2), b NUMERIC(10,2)); -- 等价
#
★★★

8. MySQL 中 INT(11) 的 11 含义?

请说明 MySQL 中 INT(11) 里的 11 是什么意思?

  • 显示宽度
  • 与存储关系
  • MySQL 8.0 的废弃

MySQL 中 INT(11) 的 11 是"显示宽度"(display width),仅在启用 ZEROFILL 时用于补零显示,例如 INT(5) ZEROFILL 会把 42 显示为 00042。它不影响存储空间与取值范围,INT 始终是 4 字节。MySQL 8.0.17 起已废弃显示宽度并将其从列定义中移除,因此新代码不应依赖 INT(11)。

这是常见陷阱,很多人误以为 11 表示最大位数或影响精度,实际它只是显示层面的填充。

CREATE TABLE t (id INT(5) ZEROFILL); -- 显示为 00042
#
★★★

9. MySQL 的 UNSIGNED INT 的应用?

请说明 MySQL 中 UNSIGNED INT 的使用场景与注意事项?

  • 非负语义与范围翻倍
  • 主键/自增的使用
  • 溢出与运算陷阱

UNSIGNED 使整数类型不允许负数,同时把取值范围向非负方向扩展(如 INT 从 -21 亿~21 亿变为 0~42 亿)。常用于自增主键、非负的计数/金额字段。但注意 UNSIGNED 与有符号列运算、以及减法结果为负时可能报错或回绕,且 UNSIGNED 类型在生成/迁移工具中兼容性略差。MySQL 8.0 仍支持,但 PostgreSQL 无 UNSIGNED 概念。

UNSIGNED 主要价值是扩大非负范围,但引入 cross-type 运算警告等坑,需谨慎。现代实践在 Java 等语言侧用 BIGINT 更稳妥。

CREATE TABLE t (id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY);
#
★★★

10. NUMERIC 与浮点的取舍(精确 vs 性能)?

请说明 NUMERIC 精确类型与浮点类型在精确性、性能、存储上的取舍?

  • 精确性差异
  • 性能差异
  • 适用场景

NUMERIC 以十进制精确存储,无二进制舍入误差,适合金额、税率等需要精确对账的数据,但运算是软件实现的十进制运算,速度明显慢于原生浮点(CPU 硬件浮点指令),且存储空间更大。浮点(FLOAT/DOUBLE)利用硬件指令,速度快、空间小,但存在二进制表示误差,适合科学计算、比率、坐标等可接受近似值的场景。取舍原则:需要精确与可预测舍入用 NUMERIC,追求性能与可接受近似用浮点。

关键是理解"精确性"与"性能"的权衡,以及业务对精度要求决定类型选择。

SELECT 0.1::numeric + 0.2::numeric; -- 0.3 精确
SELECT 0.1::float8 + 0.2::float8;   -- 0.30000000000000004
#
★★★

11. NUMERIC(10,2) 的精度含义?

请解释 NUMERIC(10,2) 中 10 和 2 分别表示什么?

  • precision(精度)
  • scale(标度)
  • 取值范围与存储

NUMERIC(10,2) 中 10 是精度(precision),表示总共能容纳 10 位有效数字;2 是标度(scale),表示其中小数点后有 2 位。因此整数部分最多 8 位,取值范围为 -99999999.99 到 99999999.99。超出精度会报错或(在 MySQL 非严格模式)截断。PostgreSQL 中 DECIMAL(10,2) 与 NUMERIC(10,2) 语法一致。

精度与标度限定了允许的最大值,是金额字段设计的核心参数,需按业务最大金额预留。

SELECT 99999999.99::numeric(10,2); -- 正常
SELECT 100000000.00::numeric(10,2); -- ERROR: numeric field overflow
#
★★★

12. PostgreSQL 中 INT 类型的存储大小?

请说明 PostgreSQL 中 INTEGER(INT)类型的存储大小?

  • int 占 4 字节
  • 别名 int4
  • 范围

PostgreSQL 中 INTEGER 类型(别名 INT、int4)固定占用 4 字节,取值范围约 -2147483648 到 2147483647。它是 PostgreSQL 中最常用的整数类型,许多聚合函数与序列默认配合使用。相比 smallint(2 字节)与 bigint(8 字节),INT 是通用折中。

这是基础但高频的考点,需牢记 4 字节与别名 int4。

#
★★★

13. IEEE 754 双精度浮点数(DOUBLE PRECISION、FLOAT8)的精度损失与 0.1 + 0.2 ≠ 0.3 陷阱?

请说明 IEEE 754 双精度浮点数(DOUBLE PRECISION/FLOAT8)的精度损失原理,以及 0.1+0.2≠0.3 的原因?

  • IEEE 754 二进制表示
  • 十进制小数无法精确表示
  • 精度陷阱与规避

DOUBLE PRECISION(FLOAT8)按 IEEE 754 双精度表示,用 53 位有效二进制尾数。十进制小数 0.1、0.2 在二进制中都是无限循环小数,无法精确表示,因此 0.1+0.2 的结果是 0.30000000000000004 而非 0.3。这是浮点表示固有的精度损失,比较浮点数应用容差(如 abs(a-b)<1e-9)而非直接相等。需要精确十进制时应改用 NUMERIC。

理解浮点二进制表示与十进制间转换的不可逆性,是避免"比较浮点相等"bug 的关键。

SELECT 0.1::float8 + 0.2::float8;          -- 0.30000000000000004
SELECT 0.1::float8 + 0.2::float8 = 0.3;    -- false
#
★★

14. 浮点聚合(AVG、SUM)的精度损失与替代方案(NUMERIC)?

请说明浮点列上的 AVG、SUM 聚合为何会产生精度损失,以及如何替代?

  • 浮点累积误差
  • 聚合精度问题
  • NUMERIC 替代

对 FLOAT/DOUBLE 列做 SUM、AVG 时,逐项累加浮点误差会累积放大,导致大数和小数相加时小数被吞掉(catastrophic cancellation)。替代方案:对金额等精确需求,将列转为 NUMERIC 再聚合,或直接用 NUMERIC 类型存储;也可用 SUM(col::numeric) 在聚合时转换。PostgreSQL 中对 float 聚合误差在数值巨大或差值悬殊时尤为明显。

聚合精度是"累积性"问题,个别值看似无误差,求和后误差显现,需在源列类型上解决。

SELECT SUM(amount::numeric) FROM orders; -- 精确求和
#
★★

15. PostgreSQL 中 NUMERIC 与 DOUBLE PRECISION 的性能差异?

请说明 PostgreSQL 中 NUMERIC 与 DOUBLE PRECISION 在性能上的差异?

  • 软件十进制 vs 硬件浮点
  • 运算与索引开销
  • 使用场景

DOUBLE PRECISION 使用 CPU 硬件浮点单元,运算接近原生速度,性能高;NUMERIC 是软件实现的任意精度十进制运算,运算开销大、速度慢,尤其在大量聚合与排序时差距明显。但 NUMERIC 精度高、无舍入误差。因此对性能敏感且可接受近似值的场景用 double precision,对精度要求高的金额用 NUMERIC。

性能差异源于"硬件加速 vs 软件模拟",是选择数值类型时的重要权衡。

#
★★

16. 货币存储为何推荐 NUMERIC(19,4)?

为什么推荐使用 NUMERIC(19,4) 存储货币金额?

  • 4 位小数避免舍入误差累积
  • 19 位整数上限
  • 与 ISO 货币标准匹配

NUMERIC(19,4) 表示最多 19 位有效数字、4 位小数,整数部分 15 位。4 位小数比 2 位多出两个小数位,可在计算(如汇率、税率、折扣)时保留中间精度,避免四舍五入误差累积后最终对账不平;19 位总量足以覆盖大型企业金额(最大约 1 千万亿)。这也是常见金融系统的推荐配置。

多位小数作为"内部精度缓冲",最终展示时再舍入到 2 位,是金额计算的稳健做法。

CREATE TABLE account (balance NUMERIC(19,4));
#
★★

17. MySQL FLOAT 与 DOUBLE 的方言差异?

请说明 MySQL 中 FLOAT 与 DOUBLE 的方言差异与使用注意?

  • FLOAT 4 字节、DOUBLE 8 字节
  • 精度差异
  • 近似值存储

MySQL 中 FLOAT 是单精度(4 字节,约 7 位有效数字),DOUBLE 是双精度(8 字节,约 15-16 位有效数字),两者都是近似浮点类型。FLOAT(p) 指定精度位数,若 p<=24 用 FLOAT(4 字节),否则自动用 DOUBLE(8 字节)。DOUBLE 与 REAL 默认等价(取决于 sql_mode 中 REAL_AS_FLOAT)。两者均存在二进制近似误差,不用于精确金额。

面试重点在于 FLOAT 与 DOUBLE 的字节数与精度,以及 FLOAT(p) 的动态类型选择。

CREATE TABLE t (a FLOAT, b DOUBLE); -- a 4字节, b 8字节
#
★★

18. MySQL 中 FLOAT(p) 的精度 p 含义?

请说明 MySQL 中 FLOAT(p) 的 p 表示什么?

  • p 表示有效二进制位数
  • p<=24 与 p>24 的类型选择
  • 存储字节差异

MySQL 中 FLOAT(p) 的 p 表示以位为单位的"精度"(有效二进制位数)。当 p 在 0-24 之间时使用单精度 FLOAT(4 字节),当 p 在 25-53 之间时自动使用 DOUBLE(8 字节)。因此 p 不是十进制位数的直接指示,而是决定底层类型是单精度还是双精度。

这是 MySQL 特有的方言,p 决定底层单/双精度,与 PostgreSQL 的 NUMERIC(p,s) 含义完全不同。

CREATE TABLE t (a FLOAT(10), b FLOAT(30)); -- a 单精度, b 双精度
#
★★

19. MySQL 的 DOUBLE 与 FLOAT 索引?

请说明 MySQL 中 DOUBLE 与 FLOAT 列上建立索引的注意事项?

  • 浮点列索引可用性
  • 等值查询的近似问题
  • 范围查询

MySQL 可以在 FLOAT/DOUBLE 列上建立普通 B-Tree 索引,索引本身可用。但由于浮点存储是近似值,等值查询(WHERE a = 0.3)可能匹配不到实际存储的值,导致索引失效或查询结果异常。因此对浮点列更推荐范围查询(BETWEEN、大于/小于),或者在精确比较时先转 NUMERIC 或使用舍入后的值。实践中浮点列很少作为索引键。

浮点索引的问题不在索引本身,而在近似值等值比较的语义,应优先精确匹配方案。

#
★★

20. PostgreSQL 中 numeric 与 float8 的隐式转换?

请说明 PostgreSQL 中 numeric 与 float8 之间的隐式转换规则?

  • 隐式转换的有向性
  • 精度损失风险
  • 显式转换推荐

PostgreSQL 的类型转换遵循"安全"原则:float8 可以隐式转换为 numeric(因为 numeric 精度更高,无损),但 numeric 转 float8 是"有损"转换,通常需要显式 CAST,不能依赖隐式转换。数值运算中,numeric 与 float8 混合时 PostgreSQL 会按类型分类规则选择结果类型,通常浮点优先。为明确意图,跨类型转换应显式使用 CAST。

隐式转换规则是为了避免静默精度损失,数值类型间转换的"合理默认"是向高精度方向自动、向低精度方向显式。

SELECT 1.5::numeric::float8; -- 显式转换
#
★★

21. 浮点聚合的替代品(sum 转 NUMERIC)?

在不改变列类型的前提下,如何避免浮点聚合的精度损失?

  • 聚合时显式转 NUMERIC
  • 结果精度
  • 性能权衡

可通过在聚合时把浮点列显式转换为 NUMERIC 来获得精确结果,如 SUM(amount::numeric) 或 SUM(CAST(amount AS numeric))。这样聚合过程使用十进制精确运算,避免浮点累积误差。代价是聚合性能略降(NUMERIC 为软件运算)。若列本身是浮点且业务可接受近似,也可直接对浮点聚合。

聚合期转换是"不改表结构、只改查询"的精准解决方案,适合临时精度要求的场景。

SELECT SUM(amount::numeric) AS total FROM orders;
#
★★

22. 序列类型 SMALLSERIAL、SERIAL、BIGSERIAL 与显式 CREATE SEQUENCE 的取舍?

请说明 PostgreSQL 中 SMALLSERIAL、SERIAL、BIGSERIAL 序列类型与显式 CREATE SEQUENCE 的取舍?

  • 三种序列类型的底层类型
  • 与显式序列的差异
  • 自增主键的常见做法

PostgreSQL 的 SERIAL 是伪类型:它创建一个底层聚类序列(sequence)并默认绑定到列的 nextval 默认值。SMALLSERIAL 对应 smallint,SERIAL 对应 integer,BIGSERIAL 对应 bigint。它们与显式 CREATE SEQUENCE 的区别在于:SERIAL 是"简化糖",自动创建并关联序列;显式 CREATE SEQUENCE 则允许自定义增量、步长、循环、缓存、归属等高级选项。现代实践更推荐用 GENERATED BY DEFAULT AS IDENTITY(与 MySQL AUTO_INCREMENT 语义更一致)。

SERIAL 快而方便,但隐藏序列对象;显式序列可控性更强。IDENTITY 是 PostgreSQL 10+ 推荐的现代替代。

CREATE TABLE t (id SERIAL PRIMARY KEY);          -- 自动建序列
CREATE SEQUENCE seq START 100 INCREMENT 5;
CREATE TABLE t2 (id bigint DEFAULT nextval('seq'));
#
★★

23. INTEGER 类型与 32 位/64 位系统关系?

请说明 INTEGER 类型的取值与 32 位/64 位系统有什么关系?

  • 数据库类型与平台位数无关
  • INT 固定 4 字节
  • 可移植性

数据库的 INTEGER 类型是平台无关的,无论运行在 32 位还是 64 位操作系统上,INTEGER 始终固定占 4 字节、范围约 ±21 亿,BIGINT 始终 8 字节。这与 C 语言中 int 可能随平台变化不同,数据库通过类型系统保证跨平台一致性。因此数据库整数类型不受硬件位宽影响,保证了数据文件的可移植性。

关键区别是数据库类型长度固定、不随平台位数变化,这与 C/Java 原生类型不同。

#
★★

24. Java/SQL 的整型映射(Integer、Long、BigInteger)?

请说明 Java 的 Integer、Long、BigInteger 与 SQL 整数类型的映射关系?

  • 各 SQL 整数对应的 Java 类型
  • 缺省与溢出
  • 大数据量字段映射

Java 中 int 对应 SQL 的 INTEGER(4 字节),long 对应 BIGINT(8 字节),BigInteger 对应 NUMERIC/DECIMAL 或任意精度整数。short 对应 SMALLINT,byte 对应 TINYINT。使用 JDBC 时,getInt 读取 INTEGER 列、getLong 读取 BIGINT 列,若 BIGINT 值超出 long 范围(极少)则需用 BigInteger。映射时注意 SQL 类型长度与 Java 类型范围的匹配,避免溢出。

类型映射表是面试常见题,核心是字节数与范围对应,以及大整数用 BigInteger 的兜底。

// PreparedStatement.setLong(1, id) 对应 BIGINT 列
#
★★

25. bigint 的最大值 9223372036854775807?

请说明 bigint 的最大值 9223372036854775807 的来源与含义?

  • 2^63-1
  • 有符号 64 位
  • 溢出场景

bigint 是 64 位有符号整数,最大值 9223372036854775807 即 2^63-1,最小值 -9223372036854775808 即 -2^63。这是 64 位二进制能表示的最大有符号数,约 92 亿亿。对于自增主键、时间戳等绝大多数业务足够,但当差值或运算超过该值会溢出报错。SERIAL 不能直接给 bigint 用,需用 BIGSERIAL。

认识到 2^63-1 是有符号 64 位上限,有助于判断 ID 是否可能在未来耗尽。

#
★★

26. smallint 的范围 -32768 到 32767?

请说明 smallint 的取值范围 -32768 到 32767 的依据?

  • 2 字节有符号
  • 2^15 范围
  • 适用场景

smallint 是 16 位有符号整数,占 2 字节,最小值 -32768 即 -2^15,最大值 32767 即 2^15-1。因为 2 字节共 16 位,符号位占 1 位,剩余 15 位表示数值。适合表示状态码、年龄、评分等取值有限的字段,可节省空间。超出范围会报错。

2^15 与 2^15-1 的边界是 16 位有符号数的标准范围,理解位宽即可推导。

#
★★

27. REAL(FLOAT4)与 DOUBLE PRECISION(FLOAT8)的存储与精度对比?

请对比 PostgreSQL 中 REAL(FLOAT4)与 DOUBLE PRECISION(FLOAT8)的存储与精度?

  • FLOAT4 4 字节、FLOAT8 8 字节
  • 有效数字位数
  • 精度取舍

PostgreSQL 中 REAL(别名 FLOAT4)是单精度浮点,占 4 字节,约 6-7 位有效十进制数字;DOUBLE PRECISION(别名 FLOAT8)是双精度浮点,占 8 字节,约 15-16 位有效十进制数字。两者都是 IEEE 754 二进制浮点,存在精度损失。REAL 空间小但精度低,DOUBLE 精度更高占用更大。坐标、测量等场景常选用 DOUBLE。

FLOAT4/FLOAT8 以字节数命名,直接体现存储;精度随字节数增长。

#
★★

28. 浮点数的特殊值,Infinity、-Infinity、NaN 在数据库中的存储与比较?

请说明浮点数的特殊值 Infinity、-Infinity、NaN 在数据库中的存储与比较行为?

  • IEEE 754 特殊值
  • NaN 的存储
  • 比较与排序语义

PostgreSQL 支持 IEEE 754 特殊值:'Infinity'、'-Infinity'、'NaN' 可作为 float4/float8 值存储。比较排序中 -Infinity 最小,Infinity 最大,而 NaN 被定义为大于所有非 NaN 数值(包括 Infinity)。但 NaN 与任何值(包括自身)的相等比较都是 false,需用 IS NAN 或 = ANY 判断。MySQL 则更多地依赖字符串或特殊处理。这些特殊值在聚合、排序时需特别小心。

NaN 在比较中"大于一切"且"不等于自身"是反直觉的,需使用专门的谓词判断。

SELECT 'NaN'::float8 = 'NaN'::float8;        -- false
SELECT 'NaN'::float8 IS NAN;                  -- true
SELECT 'Infinity'::float8 > 1e308;            -- true
#
★★

29. 舍入模式,ROUND 四舍五入 vs 银行家舍入(ROUND HALF EVEN)的差异?

请说明 ROUND 四舍五入与银行家舍入(ROUND HALF EVEN)的差异?

  • 四舍五入的偏差
  • 银行家舍入规则
  • 各数据库实现

传统四舍五入(ROUND HALF UP)对 .5 一律向上进位,在统计大量数据时会产生系统性向上偏差。银行家舍入(ROUND HALF EVEN)对恰好 .5 时舍入到偶数,使正负误差大致抵消,被金融统计广泛采用。PostgreSQL 的 ROUND 对 numeric 使用四舍五入(不减半),但若要银行家舍入需自行实现;某些数据库(如 SQL Server ROUND 默认)行为不同。金额计算选择舍入模式需明确业务规则。

核心是"恰好 .5 时向哪边舍入"的规则差异,以及偏置消除的统计意义。

SELECT round(2.5); -- PostgreSQL 返回 3(四舍五入)
SELECT round(3.5); -- 4
#

30. 1 + 0.2 在 DOUBLE PRECISION 的结果?

在 DOUBLE PRECISION 中计算 1 + 0.2 的结果是什么?

  • 浮点近似
  • 结果精度
  • 与 1.2 比较

在 DOUBLE PRECISION 中,1 + 0.2 的结果是 1.2 的二进制近似值,实际为 1.2000000000000002(或 1.2 的最近浮点表示),与字面量 1.2 可能不相等。因为 0.2 无法用二进制精确表示。因此 1 + 0.2 = 1.2 的比较结果为 false,这是浮点精度损失的典型表现。

直观的整数加法看似无误差,但一旦涉及 0.2 这类二进制不可表示小数就出现近似,需辨明。

SELECT 1::float8 + 0.2::float8;      -- 1.2000000000000002
SELECT 1::float8 + 0.2::float8 = 1.2; -- false
#

31. ROUND(2.5) 与 ROUND(3.5) 的银行家舍入?

银行家舍入下 ROUND(2.5) 与 ROUND(3.5) 的结果分别是什么?

  • 银行家舍入规则
  • 偶数偏好
  • 与四舍五入对比

在银行家舍入(ROUND HALF EVEN)下,ROUND(2.5) 结果为 2(因为 2 是偶数),ROUND(3.5) 结果为 4(因为 4 是偶数)。这与传统四舍五入(2.5→3,3.5→4)不同。注意 PostgreSQL 默认的 numeric round 采用四舍五入,因此 2.5 会舍入为 3;银行家舍入需在应用层实现或使用特定函数。

银行家舍入的关键是"舍入到偶数",恰好 .5 时看哪边是偶数,从而交替进位避免系统偏差。