scalbnl.s   [plain text]


//  long double scalbnl( long double x, int n );
//  long double ldexpl( long double x, int n );
//  long double scalblnl( long double x, long int n );
//
//  Returns x * 2^n computed efficiently, without undue over/underflow.
//  
//  -- Stephen Canon, January 2010


.text
.globl _scalblnl
.globl _scalbnl
.globl _ldexpl

#if defined __x86_64__

    #define     floatval    -24(%rsp)
    #define     exponent    -16(%rsp)
    #define     n           %edi
    #define     ln          %rdi
    #define     nw          %di

.align 4
_scalblnl:
    fldt        8(%rsp)                 // load x
    fld1
    fstpt       floatval                // conjure 1.0L
    
    cmp         $16382,     ln          // if n is so large that 2^n is not
    jg          L_bigPositiveN          // representable as a long double,
    cmp         $-16382,    ln          // branch to handle the scaling
    jl          L_bigNegativeN          // carefully.
    jmp         L_scaleX
    
.align 4
_scalbnl:
_ldexpl:  
    fldt        8(%rsp)                 // load x
    
#elif defined __i386__

    #define     floatval    4(%esp)
    #define     exponent    12(%esp)
    #define     n           %eax
    #define     nw          %ax
    
.align 4
_scalblnl:
_scalbnl:
_ldexpl:
    mov       20(%esp),     n           // load n
    fldt        floatval                // load x
    
#else
    #error "This implementation supports 32- and 64-bit Intel only"
#endif

    fld1
    fstpt       floatval                // conjure 1.0L
    
    cmp         $16382,     n           // if n is so large that 2^n is not
    jg          L_bigPositiveN          // representable as a long double,
    cmp         $-16382,    n           // branch to handle the scaling
    jl          L_bigNegativeN          // carefully.

L_scaleX:    
    add         nw,         exponent    // 2^n
    fldt        floatval
    fmulp                               // x * 2^n
    ret
    
L_bigPositiveN:
    addw        $16382,     exponent
    fldt        floatval                // 2^16382
    mov         $(3*16382), %edx
    cmp         %edx,       n
    cmovg       %edx,       n           // clamp n to 3 * 16382
0:  fmul        %st(0),     %st(1)      // do {
    sub         $16382,     n           //  x *= 2^16382
    cmp         $16382,     n           //  n -= 16382
    jg          0b                      // } (while n > 16382)
    fstp        %st(0)
    movw        $0x3fff,    exponent
    jmp         L_scaleX
    
L_bigNegativeN:
    addw        $-16382,    exponent
    fldt        floatval                // 2^-16382
    mov         $(-3*16382),%edx
    cmp         %edx,       n
    cmovl       %edx,       n           // clamp n to -3 * 16382
0:  fmul        %st(0),     %st(1)      // do {
    add         $16382,     n           //  x *= 2^-16382
    cmp         $-16382,    n           //  n += 16382
    jl          0b                      // } (while n < -16382)
    fstp        %st(0)
    movw        $0x3fff,    exponent
    jmp         L_scaleX