Skip to main content
Solved

Bit shift gives unexpected result

  • March 27, 2021
  • 4 replies
  • 72 views

r_huesken
Contributor
Forum|alt.badge.img+5

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.

 

 

Best answer by david_r

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

 

 

This post is closed to further activity.
It may be an old question, an answered question, an implemented idea, or a notification-only post.
Please check post dates before relying on any information in a question or answer.
For follow-up or related questions, please post a new question or idea.
If there is a genuine update to be made, please contact us and request that the post is reopened.

4 replies

ebygomm
Influencer
Forum|alt.badge.img+44
  • Influencer
  • 3427 replies
  • March 28, 2021

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)))

 


david_r
Celebrity
  • 8392 replies
  • Best Answer
  • March 29, 2021

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

 

 


r_huesken
Contributor
Forum|alt.badge.img+5
  • Author
  • Contributor
  • 33 replies
  • March 29, 2021

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.

 

 


r_huesken
Contributor
Forum|alt.badge.img+5
  • Author
  • Contributor
  • 33 replies
  • March 29, 2021

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...