JS精度丢失

发布时间 2023-04-21 16:40:23作者: ylc0x01

Javascript是非数据安全类型的语言,所以JS尾数精度有丢失的问题

Number类型

通过查阅JS官方文档

在JS定义的浮点数会自动转换为Number类型,Number类型是一个双精度64位浮点数,二进制存储格式执行IEEE 754标准

image

通过查阅维基百科,64位二进制格式IEEE 754的定义

image

Number能处理的最大安全值和最小安全值分别为:

image

对于JS来说超过\(2^{53}-1=9007199254740991\)\(-2^{53}+1=-9007199254740991\)都是不安全的

比如:

image

精度是如何丢失的

const m1 = 9007199254740991;

推导过程,10进制转2进制转IEEE:

9007199254740991

= \(2^{53}-1\)

= \(2^{52}+2^{51}+2^{50}+...+2^{2}+2^{1}+2^{0}\)

= 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

+ 0100 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

+ 0010 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

...

+ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0100

+ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010

+ 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001

= 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111

= 1.111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 * \(2^{52}\)

符号位:正数 = 0

指数:52 = 000 0011 0100

尾数:1.111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111

IEEE:0000 0011 0100 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111

还原:

1.111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 * \(2^{52}\)

= 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 (小数点右移52位)

= 9007199254740991

const m2 = 9007199254740992;

推导过程,10进制转2进制转IEEE:

9007199254740992

= 1 * \(2^{53}\)

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

符号位:正数 = 0

指数:53 = 000 0011 0101

尾数:1 = 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 000(最后一位溢出)

IEEE:0000 0011 0101 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

还原:

1 * \(2^{53}\)

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(小数点右移53位)

= 9007199254740992(虽然与期望值9007199254740992相同,但是实际上丢失了最后一位0)

const m3 = 9007199254740993;

推导过程,10进制转2进制转IEEE:

9007199254740993

= 1 * \(2^{53}\) + 1

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001

符号位:正数 = 0

指数:指数 = 000 0011 0101

尾数:1 = 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 000(最后一位溢出)

IEEE:0000 0011 0101 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000

还原:

1 * \(2^{53}\)

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000(小数点右移53位)

= 9007199254740992(与期望值9007199254740993不同,最后一位1丢失)

const m4 = 9007199254740994;

9007199254740994

= 1 * \(2^{53}\) + 2

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010

符号位:正数 = 0

指数:指数 = 000 0011 0101

尾数:

1.0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 001(最后一位溢出)

IEEE:0000 0011 0101 1000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0001

还原:

1.0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 001 * \(2^{53}\)

= 1 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0010(小数点右移53位)

= 9007199254740994(虽然与期望值9007199254740994相同,但是实际上丢失了最后一位0)