by Jon Ripley, August 2006

BBC BASIC for Windows stores floating point numbers in 40-bit and 64-bit resolutions and has no built-in support for 32-bit (single precision) floating point numbers. When using the Windows API and functions in third party DLLs it is sometimes necessary to use 32-bit floating point numbers. To ensure that single precision numbers are correctly passed to SYS calls they must be stored in integer variables which are also 32-bits wide.

The following two functions are required to perform the necessary conversions:

  • FN_ConvertToSingle converts a number to a single precision floating point value
  • FN_ConvertFromSingle converts a number from a single precision floating point value

      DEF FN_ConvertToSingle(A#)
      LOCAL A%,P%
      PRIVATE F%
      IF F%=0 THEN
        DIM P% 10
        [OPT 2
        .F%
        mov esi,[ebp+2]   ; esi = pointer to A#
        mov edi,[ebp+7]   ; edi = pointer to A%
        fld qword [esi]   ; load indirect 64 bit real from esi
        fstp dword [edi]  ; store indirect 32 bit real to edi
        ret
        ]
      ENDIF
      A#*=1.0#
      CALL F%,A#,A%
      =A%
 
      DEF FN_ConvertFromSingle(A%)
      LOCAL A#,P%
      PRIVATE F%
      IF F%=0 THEN
        DIM P% 10
        [OPT 2
        .F%
        mov esi,[ebp+2]   ; esi = pointer to A%
        mov edi,[ebp+7]   ; edi = pointer to A#
        fld dword [esi]   ; load indirect 32 bit real from esi
        fstp qword [edi]  ; store indirect 64 bit real to edi
        ret
        ]
      ENDIF
      CALL F%,A%,A#
      =A#

Note: FN_ConvertToSingle is the same routine as FN_f4 from the supplied D3DLIB library, but the assembly language has been commented and the name has been changed for clarity.


by Richard Russell, November 2006

The above routines use assembly language code to perform the conversion. An alternative is to use Windows API functions as follows:

      DEF FN_ConvertToSingle(A#)
      LOCAL A%
      PRIVATE F%
      IF F% = 0 THEN
        SYS "LoadLibrary", "OLEAUT32.DLL" TO F%
        SYS "GetProcAddress", F%, "VarR4FromR8" TO F%
      ENDIF
      A#*=1.0#
      SYS F%,!^A#,!(^A#+4),^A%
      =A%
 
      DEF FN_ConvertFromSingle(A%)
      LOCAL A#
      PRIVATE F%
      IF F% = 0 THEN
        SYS "LoadLibrary", "OLEAUT32.DLL" TO F%
        SYS "GetProcAddress", F%, "VarR8FromR4" TO F%
      ENDIF
      SYS F%,A%,^A#
      =A#
If speed is critical, use this conversion function:
      DEF FN_ConvertToSingle(A#)=USR(f4)
where the routine f4 has been assembled previously as follows:
        .f4
        mov   ecx,[^A#+4]
        test  ecx,&FFFFFF00
        jnz   f4d
        movzx ecx,cl
        jecxz f4i
        mov   edx,[^A#]
        add   ecx,895
        rol   edx,1
        shld  ecx,edx,21
        shl   edx,20
        btr   edx,20
        rcr   ecx,1
        mov   [^A#],edx
        mov   [^A#+4],ecx
        .f4d
        push  eax
        fld   qword [^A#]
        fstp  dword [esp]
        pop   eax
        ret
        ;
        .f4i
        push  eax
        fild  dword [^A#]
        fstp  dword [esp]
        pop   eax
        ret
Ensure there is at least a 2048-byte gap between the code and any writable data, to prevent thrashing of the instruction cache.