64 位 NS_OPTIONS 位掩码
64-bit NS_OPTIONS bitmask
上下文
我正在使用 NS_OPTIONS
宏来创建位掩码。我给它分配了一种 NSInteger
类型,因为我是在 64 位平台上构建的,这应该给我总共 63 "slots" 的工作(64 位少一位签名-ness).
这里是枚举:
typedef NS_OPTIONS(NSInteger, LPSVGOPlugin) {
LPSVGOPluginNone = 0,
LPSVGOPluginCleanupAttrs = 1 << 0,
LPSVGOPluginRemoveDoctype = 1 << 1,
LPSVGOPluginRemoveXMLProcInst = 1 << 2,
LPSVGOPluginRemoveComments = 1 << 3,
LPSVGOPluginRemoveMetadata = 1 << 4,
LPSVGOPluginRemoveTitle = 1 << 5,
LPSVGOPluginRemoveDesc = 1 << 6,
LPSVGOPluginRemoveUselessDefs = 1 << 7,
LPSVGOPluginRemoveEditorsNSData = 1 << 8,
LPSVGOPluginRemoveEmptyAttrs = 1 << 9,
LPSVGOPluginRemoveHiddenElems = 1 << 10,
LPSVGOPluginRemoveEmptyText = 1 << 11,
LPSVGOPluginRemoveEmptyContainers = 1 << 12,
LPSVGOPluginRemoveViewBox = 1 << 13,
LPSVGOPluginCleanupEnableBackground = 1 << 14,
LPSVGOPluginMinifyStyles = 1 << 15,
LPSVGOPluginConvertStyleToAttrs = 1 << 16,
LPSVGOPluginConvertColors = 1 << 17,
LPSVGOPluginConvertPathData = 1 << 18,
LPSVGOPluginConvertTransform = 1 << 19,
LPSVGOPluginRemoveUnknownsAndDefaults = 1 << 20,
LPSVGOPluginRemoveNonInheritableGroupAttrs = 1 << 21,
LPSVGOPluginRemoveUselessStrokeAndFill = 1 << 22,
LPSVGOPluginRemoveUnusedNS = 1 << 23,
LPSVGOPluginCleanupIDs = 1 << 24,
LPSVGOPluginCleanupNumericValues = 1 << 25,
LPSVGOPluginMoveElemsAttrsToGroup = 1 << 26,
LPSVGOPluginMoveGroupAttrsToElems = 1 << 27,
LPSVGOPluginCollapseGroups = 1 << 28,
LPSVGOPluginRemoveRasterImages = 1 << 29,
LPSVGOPluginMergePaths = 1 << 30,
LPSVGOPluginConvertShapeToPath = 1 << 31,
LPSVGOPluginSortAttrs = 1 << 32,
LPSVGOPluginTransformsWithOnePath = 1 << 33,
LPSVGOPluginRemoveDimensions = 1 << 34,
LPSVGOPluginRemoveAttrs = 1 << 35,
LPSVGOPluginAddClassesToSVGElement = 1 << 36,
LPSVGOPluginRemoveStyleElement = 1 << 37
};
最高值左移 37 位,远低于我应该可用的 63 位。
问题
我根据该枚举创建了一个掩码,如下所示:
LPSVGOPlugin defaultPluginMask = LPSVGOPluginNone;
defaultPluginMask = (LPSVGOPluginCleanupAttrs
|LPSVGOPluginRemoveDoctype
|LPSVGOPluginRemoveXMLProcInst
|LPSVGOPluginRemoveComments
|LPSVGOPluginRemoveMetadata
|LPSVGOPluginRemoveDesc
|LPSVGOPluginRemoveUselessDefs
|LPSVGOPluginRemoveEditorsNSData
|LPSVGOPluginRemoveEmptyAttrs
|LPSVGOPluginRemoveHiddenElems
|LPSVGOPluginRemoveEmptyText
|LPSVGOPluginRemoveEmptyContainers
|LPSVGOPluginCleanupEnableBackground
|LPSVGOPluginMinifyStyles
|LPSVGOPluginConvertStyleToAttrs
|LPSVGOPluginConvertColors
|LPSVGOPluginConvertPathData
|LPSVGOPluginConvertTransform
|LPSVGOPluginRemoveUnknownsAndDefaults
|LPSVGOPluginRemoveNonInheritableGroupAttrs
|LPSVGOPluginRemoveUselessStrokeAndFill
|LPSVGOPluginRemoveUnusedNS
|LPSVGOPluginCleanupIDs
|LPSVGOPluginCleanupNumericValues
|LPSVGOPluginMoveElemsAttrsToGroup
|LPSVGOPluginMoveGroupAttrsToElems
|LPSVGOPluginCollapseGroups
|LPSVGOPluginMergePaths
|LPSVGOPluginConvertShapeToPath
);
当我使用这个记录 defaultPluginMask
的值时:
NSLog(@"maskValue: %li", (long)defaultPluginMask);
结果是-536879137。一旦我从掩码中删除最后一个掩码添加 (LPSVGOPluginConvertShapeToPath
),我就会得到一个正值:1610604511
重要的是,LPSVGOPluginConvertShapeToPath
左移了 31 位,这将是 32 位 NSInteger 中的最大位位置。但是,如果我使用 sizeof()
来记录 defaultPluginMask
的大小,它会将其报告为 8 个字节,这意味着它应该是 64 位。
我有点(哈!)困惑。我犯了一个我没有看到的基本错误吗?
对于long
数据类型,C标准规定要求最小范围为-2147483648
到2147483647
,即使用32 bits
,包括符号位.
即使为 64 bits
平台编译,也不能保证 long
变量会在 64 bits
上表示。为确保它具有最小 64 bits
的存储空间,应使用 long long
。正如@Olaf 所建议的那样,要精确指定存储大小,可以使用 stdint.h
类型(例如 int64_t
)。
但上述观察结果似乎并不是您的问题的根源(如果 NSInteger
和 long
大小 >= 8 字节)。
问题是要移动的值 1
是一个适合 32 位的整数文字,将按原样存储(如果它不适合 32 位,例如 50 亿,它将存储在 64 位上)。 1 << 31
将是一个有符号的 32 位整数,1 恰好在符号位置,因此是一个负数。如果稍后将其分配给 64 位有符号整数变量,则符号位将被扩展(二进制补码表示)。
这可以从您的案例中观察到的值看出,这些值在 64 位上以十六进制表示为:
-536879137 => ffffffffdfffdfdf - when 1 << 31 is present
1610604511 => 5fffdfdf - when 1 << 31 is removed
下面我做了一个小实验,看看在这个"border"附近会发生什么(应该注意的是从1 << 32
和1 << 33
获得的值是未定义的行为 - 转移存储大小,在其他平台上可能恰好给出与 1 << 0
和 1 << 1
相同的结果。
printf("%d %d %d %d\n", sizeof(int), sizeof(long), sizeof(long long), sizeof(void*));
// Prints: 4 4 8 8, running on an Windows 64-bit, compiled with gcc from MinGW
long long a[9] = {
1 << 30, // 1073741824 40000000
1LL << 30, // 1073741824 40000000
1 << 31, // -2147483648 ffffffff80000000
1LL << 31, // 2147483648 80000000
1 << 32, // 0 0
1LL << 32, // 4294967296 100000000
1 << 33, // 0 0
1LL << 33, // 8589934592 200000000
5000000000,// 5000000000 12a05f200
};
// Prints of this loop are in the comments above
for (int i = 0; i < 9; i++)
printf("%12lli %16llx\n", a[i], a[i]);
解决方法:如果一个整型字面量打算直接用于其结果不适合该字面量存储大小的操作,则它必须加上后缀:
u
/U
用于 unsigned
(如果需要)
l
/L
对于 long
ll
/LL
对于 long long
问题中的问题可以通过对这些元素应用 LL
后缀来解决:
LPSVGOPluginConvertShapeToPath = 1LL << 31,
LPSVGOPluginSortAttrs = 1LL << 32,
LPSVGOPluginTransformsWithOnePath = 1LL << 33,
LPSVGOPluginRemoveDimensions = 1LL << 34,
LPSVGOPluginRemoveAttrs = 1LL << 35,
LPSVGOPluginAddClassesToSVGElement = 1LL << 36,
LPSVGOPluginRemoveStyleElement = 1LL << 37
上下文
我正在使用 NS_OPTIONS
宏来创建位掩码。我给它分配了一种 NSInteger
类型,因为我是在 64 位平台上构建的,这应该给我总共 63 "slots" 的工作(64 位少一位签名-ness).
这里是枚举:
typedef NS_OPTIONS(NSInteger, LPSVGOPlugin) {
LPSVGOPluginNone = 0,
LPSVGOPluginCleanupAttrs = 1 << 0,
LPSVGOPluginRemoveDoctype = 1 << 1,
LPSVGOPluginRemoveXMLProcInst = 1 << 2,
LPSVGOPluginRemoveComments = 1 << 3,
LPSVGOPluginRemoveMetadata = 1 << 4,
LPSVGOPluginRemoveTitle = 1 << 5,
LPSVGOPluginRemoveDesc = 1 << 6,
LPSVGOPluginRemoveUselessDefs = 1 << 7,
LPSVGOPluginRemoveEditorsNSData = 1 << 8,
LPSVGOPluginRemoveEmptyAttrs = 1 << 9,
LPSVGOPluginRemoveHiddenElems = 1 << 10,
LPSVGOPluginRemoveEmptyText = 1 << 11,
LPSVGOPluginRemoveEmptyContainers = 1 << 12,
LPSVGOPluginRemoveViewBox = 1 << 13,
LPSVGOPluginCleanupEnableBackground = 1 << 14,
LPSVGOPluginMinifyStyles = 1 << 15,
LPSVGOPluginConvertStyleToAttrs = 1 << 16,
LPSVGOPluginConvertColors = 1 << 17,
LPSVGOPluginConvertPathData = 1 << 18,
LPSVGOPluginConvertTransform = 1 << 19,
LPSVGOPluginRemoveUnknownsAndDefaults = 1 << 20,
LPSVGOPluginRemoveNonInheritableGroupAttrs = 1 << 21,
LPSVGOPluginRemoveUselessStrokeAndFill = 1 << 22,
LPSVGOPluginRemoveUnusedNS = 1 << 23,
LPSVGOPluginCleanupIDs = 1 << 24,
LPSVGOPluginCleanupNumericValues = 1 << 25,
LPSVGOPluginMoveElemsAttrsToGroup = 1 << 26,
LPSVGOPluginMoveGroupAttrsToElems = 1 << 27,
LPSVGOPluginCollapseGroups = 1 << 28,
LPSVGOPluginRemoveRasterImages = 1 << 29,
LPSVGOPluginMergePaths = 1 << 30,
LPSVGOPluginConvertShapeToPath = 1 << 31,
LPSVGOPluginSortAttrs = 1 << 32,
LPSVGOPluginTransformsWithOnePath = 1 << 33,
LPSVGOPluginRemoveDimensions = 1 << 34,
LPSVGOPluginRemoveAttrs = 1 << 35,
LPSVGOPluginAddClassesToSVGElement = 1 << 36,
LPSVGOPluginRemoveStyleElement = 1 << 37
};
最高值左移 37 位,远低于我应该可用的 63 位。
问题
我根据该枚举创建了一个掩码,如下所示:
LPSVGOPlugin defaultPluginMask = LPSVGOPluginNone;
defaultPluginMask = (LPSVGOPluginCleanupAttrs
|LPSVGOPluginRemoveDoctype
|LPSVGOPluginRemoveXMLProcInst
|LPSVGOPluginRemoveComments
|LPSVGOPluginRemoveMetadata
|LPSVGOPluginRemoveDesc
|LPSVGOPluginRemoveUselessDefs
|LPSVGOPluginRemoveEditorsNSData
|LPSVGOPluginRemoveEmptyAttrs
|LPSVGOPluginRemoveHiddenElems
|LPSVGOPluginRemoveEmptyText
|LPSVGOPluginRemoveEmptyContainers
|LPSVGOPluginCleanupEnableBackground
|LPSVGOPluginMinifyStyles
|LPSVGOPluginConvertStyleToAttrs
|LPSVGOPluginConvertColors
|LPSVGOPluginConvertPathData
|LPSVGOPluginConvertTransform
|LPSVGOPluginRemoveUnknownsAndDefaults
|LPSVGOPluginRemoveNonInheritableGroupAttrs
|LPSVGOPluginRemoveUselessStrokeAndFill
|LPSVGOPluginRemoveUnusedNS
|LPSVGOPluginCleanupIDs
|LPSVGOPluginCleanupNumericValues
|LPSVGOPluginMoveElemsAttrsToGroup
|LPSVGOPluginMoveGroupAttrsToElems
|LPSVGOPluginCollapseGroups
|LPSVGOPluginMergePaths
|LPSVGOPluginConvertShapeToPath
);
当我使用这个记录 defaultPluginMask
的值时:
NSLog(@"maskValue: %li", (long)defaultPluginMask);
结果是-536879137。一旦我从掩码中删除最后一个掩码添加 (LPSVGOPluginConvertShapeToPath
),我就会得到一个正值:1610604511
重要的是,LPSVGOPluginConvertShapeToPath
左移了 31 位,这将是 32 位 NSInteger 中的最大位位置。但是,如果我使用 sizeof()
来记录 defaultPluginMask
的大小,它会将其报告为 8 个字节,这意味着它应该是 64 位。
我有点(哈!)困惑。我犯了一个我没有看到的基本错误吗?
对于long
数据类型,C标准规定要求最小范围为-2147483648
到2147483647
,即使用32 bits
,包括符号位.
即使为 64 bits
平台编译,也不能保证 long
变量会在 64 bits
上表示。为确保它具有最小 64 bits
的存储空间,应使用 long long
。正如@Olaf 所建议的那样,要精确指定存储大小,可以使用 stdint.h
类型(例如 int64_t
)。
但上述观察结果似乎并不是您的问题的根源(如果 NSInteger
和 long
大小 >= 8 字节)。
问题是要移动的值 1
是一个适合 32 位的整数文字,将按原样存储(如果它不适合 32 位,例如 50 亿,它将存储在 64 位上)。 1 << 31
将是一个有符号的 32 位整数,1 恰好在符号位置,因此是一个负数。如果稍后将其分配给 64 位有符号整数变量,则符号位将被扩展(二进制补码表示)。
这可以从您的案例中观察到的值看出,这些值在 64 位上以十六进制表示为:
-536879137 => ffffffffdfffdfdf - when 1 << 31 is present
1610604511 => 5fffdfdf - when 1 << 31 is removed
下面我做了一个小实验,看看在这个"border"附近会发生什么(应该注意的是从1 << 32
和1 << 33
获得的值是未定义的行为 - 转移存储大小,在其他平台上可能恰好给出与 1 << 0
和 1 << 1
相同的结果。
printf("%d %d %d %d\n", sizeof(int), sizeof(long), sizeof(long long), sizeof(void*));
// Prints: 4 4 8 8, running on an Windows 64-bit, compiled with gcc from MinGW
long long a[9] = {
1 << 30, // 1073741824 40000000
1LL << 30, // 1073741824 40000000
1 << 31, // -2147483648 ffffffff80000000
1LL << 31, // 2147483648 80000000
1 << 32, // 0 0
1LL << 32, // 4294967296 100000000
1 << 33, // 0 0
1LL << 33, // 8589934592 200000000
5000000000,// 5000000000 12a05f200
};
// Prints of this loop are in the comments above
for (int i = 0; i < 9; i++)
printf("%12lli %16llx\n", a[i], a[i]);
解决方法:如果一个整型字面量打算直接用于其结果不适合该字面量存储大小的操作,则它必须加上后缀:
u
/U
用于unsigned
(如果需要)l
/L
对于long
ll
/LL
对于long long
问题中的问题可以通过对这些元素应用 LL
后缀来解决:
LPSVGOPluginConvertShapeToPath = 1LL << 31,
LPSVGOPluginSortAttrs = 1LL << 32,
LPSVGOPluginTransformsWithOnePath = 1LL << 33,
LPSVGOPluginRemoveDimensions = 1LL << 34,
LPSVGOPluginRemoveAttrs = 1LL << 35,
LPSVGOPluginAddClassesToSVGElement = 1LL << 36,
LPSVGOPluginRemoveStyleElement = 1LL << 37