Search code examples
cassemblyfloating-pointx86ieee-754

Convert a float literal to int representation in x86 assembly?


The following C code:

int main()
{
    float f;
    f = 3.0;
}

Is converted to the following assembly instructions:

main:
  pushl %ebp
  movl %esp, %ebp
  subl $16, %esp
  flds .LC0
  fstps -4(%ebp)
  movl $0, %eax
  leave
  ret
.LC0:
  .long 1077936128

What is the correct way to calculate the .long/int representation of the float literal?

e.g. 1077936128 generated from 3.0 for the example shown above


For this example gcc is used with the -m32 -S -O0 -fno-stack-protector -fno-asynchronous-unwind-tables flags using intel settings to generate the assembly output.

References:

Compiler Explorer Link with compilation flags and other settings


Solution

  • x86 FPU hardware uses IEEE754 binary32 / binary64 representations for float / double.

    Determining the IEEE 754 representation of a floating point number is not trivial for humans. In handwritten assembly code, it's usually a good idea to use the .float or .double directives instead:

    .float 3.0    # generates 3.0 as a 32 bit float
    .double 3.0   # generates 3.0 as a 64 bit float
    

    If you really want to compute this manually, refer to the explanations on Wikipedia. It might be interesting to do so as an exercise, but for actual programming it's tedious and mostly useless.

    Compilers do the conversion (with rounding to the nearest representable FP value) internally, because FP values often don't come directly from a literal in the source; they can come from constant folding. e.g. 1.23 * 4.56 is evaluated at compile time, so the compiler already ends up with FP values in float or double binary representation. Printing them back to decimal for the assembler to parse and re-convert to binary would be slower and might require a lot of decimal places.


    To compute the representation of a 32 bit float as a 32 bit integer, you can use an online IEEE754 converter, or a program like this:

    #include <stdlib.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <inttypes.h>
    
    int main(int argc, char *argv[])
    {
        union { uint32_t u32; float f32; } intfloat;
    
        if (argc != 2) {
            fprintf(stderr, "Usage: %s some-number\n", argv[0]);
            return EXIT_FAILURE;
        }
    
        intfloat.f32 = atof(argv[1]);
    
        printf("0x%08" PRIx32 "\n", intfloat.u32);
    
        return EXIT_SUCCESS;
    }