问题
Context
I'm using the NS_OPTIONS
macro to create a bitmask. I've assigned it a type of NSInteger
and since I'm building on a 64-bit platform, that should give me a total of 63 "slots" to work with (64 bits less one bit for signed-ness).
Here is the enumeration:
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
};
The top value is left-shifted 37 bits, which is well below the 63 bits I should have available.
The Problem
I create a mask from that enumeration, like this:
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
);
When I log the value of defaultPluginMask
using this:
NSLog(@"maskValue: %li", (long)defaultPluginMask);
The result is -536879137. As soon as I remove that last mask addition (LPSVGOPluginConvertShapeToPath
) from the mask, I get a positive value: 1610604511
Critically, LPSVGOPluginConvertShapeToPath
is left-shifted 31 bits, which would be the maximum bit position in a 32-bit NSInteger. But if I use sizeof()
to log the size of defaultPluginMask
, it reports it as 8 bytes, which means it should be 64 bits.
I'm a bit (ha!) confused. Have I made a basic error I'm not seeing?
回答1:
For long
data type, C standard specifies that the minimum range is required to be from -2147483648
to 2147483647
, i.e. using 32 bits
, including the sign bit.
Even if compiling for a 64 bits
platform, it is not guaranteed that a long
variable will be represented on 64 bits
. To ensure that it will have minimum 64 bits
of storage, long long
should be used. As @Olaf recommended, to precisely specify the storage size, stdint.h
types could be used (e.g. int64_t
).
But the observations above doesn't seem to be the source of the issue in your case (if NSInteger
and long
sizes are >= 8 bytes).
The issue is that the value to be shifted 1
is an integer literal that fits in 32 bits and will be stored as such (if it wouldn't fit in 32 bits, e.g. 5 billions, it would be stored on 64 bits). And 1 << 31
will be a signed 32 bit integer, with 1 exactly on the sign position, so a negative number. And if this is assigned later to a 64 bit signed integer variable, the sign bit will be extended (two's complement representation).
This can be seen from the values observed in your case, which are represented in hexadecimal on 64 bits as:
-536879137 => ffffffffdfffdfdf - when 1 << 31 is present
1610604511 => 5fffdfdf - when 1 << 31 is removed
Below I've done a small experiment to see what happens near this "border" (it should be noted that values obtained from 1 << 32
and 1 << 33
are undefined behaviour - shifting over the storage size, which on other platforms may happen to give the same results as 1 << 0
and 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]);
Solution: If an integer literal is intended to be directly used in operations whose results won't fit in the storage size of that literal, it have to be suffixed with:
u
/U
forunsigned
(if needed)l
/L
forlong
ll
/LL
forlong long
The issue in the question may be solved by applying the LL
suffix to these elements:
LPSVGOPluginConvertShapeToPath = 1LL << 31,
LPSVGOPluginSortAttrs = 1LL << 32,
LPSVGOPluginTransformsWithOnePath = 1LL << 33,
LPSVGOPluginRemoveDimensions = 1LL << 34,
LPSVGOPluginRemoveAttrs = 1LL << 35,
LPSVGOPluginAddClassesToSVGElement = 1LL << 36,
LPSVGOPluginRemoveStyleElement = 1LL << 37
来源:https://stackoverflow.com/questions/33904321/64-bit-ns-options-bitmask