Solved

Bit shift gives unexpected result

  • 27 March 2021
  • 4 replies
  • 7 views

Badge

Hi,

 

I'm experimenting with bit shifting in FME, and I do not get the expected results. In FME 2020.1.0.1 (see example workbench), (255 << 24) is transformed to a 64 bit integer, and gives me 4278190080.

 

in Postgres database this operation gives the expected result of -16777216. I'm using Postgres as a reference since eventually the results of my FME script will be stored there and used by other applications.

 

I don't understand what I'm doing wrong (or should do to achieve my expected result), and hope somebody can help me out.

 

Thanks,

 

Richard.

 

 

icon

Best answer by david_r 29 March 2021, 09:36

View original

4 replies

Badge +10

Way outside of my experience, as I've only messed about with bitshifts as part of advent of code but the result you get in FME (4278190080)  is as you would expect given what you are doing is the following

@Evaluate(255*@pow(2,24))

This is above the max for a 32 bit signed integer (you can see this in the error log in your workspace when you try and convert it using @int32)

Attribute(string)                 : `fme_expression_warnings{0}.attributeName' has value `shifted_24_as_int32'
Attribute(string)                 : `fme_expression_warnings{0}.message' has value `Failed to evaluate numeric expression '@int32(4278190080)'.  Result is set to null'
Attribute(string)                 : `fme_expression_warnings{0}.transformerName' has value `AttributeManager'

 

It looks like postgres is handling the overflow by wrapping around. I don't know how you can recreate this in FME. I know in python 255<<24 will give the result as per FME as the integers in python are not of a fixed bit width, you can use numpy to get the result you are seeing in postgres

import fme
import fmeobjects
import numpy as np
 
def processFeature(feature):
    feature.setAttribute("shifted_24_as_int32",int(np.left_shift(255,24)))

 

Userlevel 4

The result you're getting from FME is the correct result for a 64-bit integer (or an unsigned 32-bit integer), also called an arithmetic shift. The result you're getting from Postgres is due to a flipped sign, since the bit shift overflows the signed int32 datatype. This is commonly referred to as a logical shift.

For logical bit manipulations in FME I would recommend using Python, where you can force a specific datatype using the numpy libraries. Example:

>>> import numpy as np
>>> np.int32(255) << 24
-16777216

 To help understand the sign bit, consider the above compared to the same bit shift but using an unsigned 32-bit integer:

>>> np.uint32(255) << 24
4278190080

 

 

Badge

The result you're getting from FME is the correct result for a 64-bit integer (or an unsigned 32-bit integer), also called an arithmetic shift. The result you're getting from Postgres is due to a flipped sign, since the bit shift overflows the signed int32 datatype. This is commonly referred to as a logical shift.

For logical bit manipulations in FME I would recommend using Python, where you can force a specific datatype using the numpy libraries. Example:

>>> import numpy as np
>>> np.int32(255) << 24
-16777216

 To help understand the sign bit, consider the above compared to the same bit shift but using an unsigned 32-bit integer:

>>> np.uint32(255) << 24
4278190080

 

 

Hi,

Thanks for the reply. After some experimenting with the PythonCaller, I managed to get the desired result.

 I have uploaded a new version of my workbench, in case somebody might be interested.

 

 

Badge

Way outside of my experience, as I've only messed about with bitshifts as part of advent of code but the result you get in FME (4278190080)  is as you would expect given what you are doing is the following

@Evaluate(255*@pow(2,24))

This is above the max for a 32 bit signed integer (you can see this in the error log in your workspace when you try and convert it using @int32)

Attribute(string)                 : `fme_expression_warnings{0}.attributeName' has value `shifted_24_as_int32'
Attribute(string)                 : `fme_expression_warnings{0}.message' has value `Failed to evaluate numeric expression '@int32(4278190080)'.  Result is set to null'
Attribute(string)                 : `fme_expression_warnings{0}.transformerName' has value `AttributeManager'

 

It looks like postgres is handling the overflow by wrapping around. I don't know how you can recreate this in FME. I know in python 255<<24 will give the result as per FME as the integers in python are not of a fixed bit width, you can use numpy to get the result you are seeing in postgres

import fme
import fmeobjects
import numpy as np
 
def processFeature(feature):
    feature.setAttribute("shifted_24_as_int32",int(np.left_shift(255,24)))

 

Thanks. Credits to you too, off course...

Reply