JavaScript(JS)、JSON和int64

大家在前后端联合开发时经常会遇到一个问题,js不能处理服务端返回的JSON中的大整数。处理方法往往是将这个大整数字段设置为string。

为什么js不能处理大整数了?
因为之前js只有number类型来表示数字(整数+浮点数),其遵循IEEE 754规范,是一个双精度 64 位二进制格式值,其整数的最大支持2的53次方-1(2^53-1):9007199254740992。内置常量Number.MAX_SAFE_INTEGER值为9007199254740991(2^53-1),因为当赋值给Number类型变量大于此值时(比如2^53,2^53+1,……),都会被存储为2^53。顺带提一下,还有一个常量:Number.MAX_VALUE。

后来新增了BitInt这种内置类型,它提供了一种能力来表示大于 2^53 – 1 的整数。而且BigInt 可以表示任意大的整数。
可以用在一个整数字面量后面加 n 的方式定义一个 BigInt ,如:10n,或者调用函数 BigInt()(但不包含 new 运算符)并传递一个整数值或字符串值。

const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991);
// 9007199254740991n
const hugeString = BigInt("9007199254740991");
// 9007199254740991n
const hugeHex = BigInt("0x1fffffffffffff");
// 9007199254740991n
const hugeBin = BigInt("0b11111111111111111111111111111111111111111111111111111");
// 9007199254740991n

 

那么又有新问题了,既然js支持大整数,那么为什么服务端不能返回大整数了?
因为我们服务端返回数据设定Content-Type: application/json时,我们告诉客户端这是一段json,而根据json规范的定义,其只支持js的number类型,而不支持BitInt类型,所以当服务端返回的json包含了一个大于Number.MAX_SAFE_INTEGER的整数时(其实这个时候严格来说服务端没有遵循json规范),客户端不能识别是正常的现象(按照上文提到的,会被转换成2^53),是符合json的规范的行为。那所以web客户端这边没有把服务端返回的大整数转换成BitInt类型是源于遵循JSON规范。既然web客户端不支持大于2^53-1的整数,那么服务端就应该避免返回这些数据,所以如果某个字段是int64类型,那么为了安全,建议将int64转换成string类型返回,服务端接收web客户端参数时也一样。

当然我们期待json规范也能更新(到时出现一个Content-Type: application/json2,哈哈),能够原生支持BitInt类型,然后内置的JSON对象的parse()和stringify()两个方法也升级支持BitInt类型。这样我们开发者就可以安全方便的在前后端之间传输int64类型数据了。

References:

https://zh.wikipedia.org/wiki/雙精度浮點數

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number

https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/BigInt

Go中struct的内存对齐

直接看代码

var i int32
var ii int64
var b bool
var s string
fmt.Printf("width:%d\n", unsafe.Sizeof(i))  // width:4
fmt.Printf("width:%d\n", unsafe.Sizeof(ii)) // width:8
fmt.Printf("width:%d\n", unsafe.Sizeof(b))  // width:1
fmt.Printf("width:%d\n", unsafe.Sizeof(s))  // width:16

{
   //空结构体的宽度为 0
   type S struct{}
   var ss S
   fmt.Printf("width:%d\n", unsafe.Sizeof(ss)) // width:0
}

{
   type stringStruct struct {
      str unsafe.Pointer // 8
      len int            // 8
   }
   var ss stringStruct
   fmt.Printf("width:%d\n", unsafe.Sizeof(ss)) // width:16
}

//以下涉及内存对齐
// https://dave.cheney.net/2014/03/25/the-empty-struct#comment-2815
{
   type S struct {
      a uint64 // 8
      b uint32 // 4
   }
   var s S
   fmt.Printf("width:%d\n", unsafe.Sizeof(s)) // width:16, not 12
}

{
   type S struct {
      a uint64    // 8
      b uint32    // 4
      c [3]uint32 // 12
   }
   var s S
   fmt.Printf("width:%d\n", unsafe.Sizeof(s)) // width:24
}

{
   type S struct {
      a uint64 // 8
      b bool   //1
   }
   var s S
   fmt.Printf("width:%d\n", unsafe.Sizeof(s)) // width:16, not 9
}
{

   type S struct {
      a uint64    // 8
      b uint32    // 4
      c bool      //1
      d [3]uint32 // 12
   }
   var s S
   fmt.Printf("width:%d\n", unsafe.Sizeof(s)) // width:32, not 25(8+4+1+12), not 28(8+4+4+12)
}

{
   type S struct {
      a uint64 // 8
      b uint32 // 4
      c bool   //1
      d uint32 // 4
   }
   var s S
   fmt.Printf("width:%d\n", unsafe.Sizeof(s)) // width:24
}

 

关键要读懂Russ Cox的这段话:

It’s not true that “a value must be aligned in memory to a multiple of its width.” Each type has another property, its alignment. Alignments are always powers of two. The alignment of a basic type is usually equal to its width, but the alignment of a struct is the maximum alignment of any field, and the alignment of an array is the alignment of the array element. The maximum alignment of any value is therefore the maximum alignment of any basic type. Even on 32-bit systems this is often 8 bytes, because atomic operations on 64-bit values typically require 64-bit alignment.

To be concrete, a struct containing 3 int32 fields has alignment 4 but width 12.

It is true that a value’s width is always a multiple of its alignment. One implication is that there is no padding between array elements.

 

References:

https://dave.cheney.net/2014/03/25/the-empty-struct#comment-2815

https://ijayer.github.io/post/tech/code/golang/20200419_emtpy_struct_in_go