Daddy needs a new pair of RAM!

edit: the fps are way better in smaller terminal windows with lower character count but then it’s hard to make out the dice. D:

edit2: code here (expires in 2 weeks)

      • RheumatoidArthritis@mander.xyz
        link
        fedilink
        arrow-up
        17
        ·
        4 days ago

        I didn’t have the patience to do it myself bit wanted to see just how complex it would get:

        fp32_mul() {
            local a=$1 b=$2
            local sa=$(( (a >> 31) & 1 ))
            local sb=$(( (b >> 31) & 1 ))
            local sign=$((sa ^ sb))
        
            local ea=$(( (a >> 23) & 0xff ))
            local eb=$(( (b >> 23) & 0xff ))
            local fa=$(( a & 0x7fffff ))
            local fb=$(( b & 0x7fffff ))
        
            # NaN / infinity / zero handling
            if (( ea == 255 )); then
                if (( fa != 0 )); then
                    printf '%08x\n' $((0x7fc00000))
                    return
                fi
                if (( eb == 0 && fb == 0 )); then
                    printf '%08x\n' $((0x7fc00000))   # inf * 0 = NaN
                    return
                fi
                printf '%08x\n' $(((sign << 31) | 0x7f800000))
                return
            fi
        
            if (( eb == 255 )); then
                if (( fb != 0 )); then
                    printf '%08x\n' $((0x7fc00000))
                    return
                fi
                if (( ea == 0 && fa == 0 )); then
                    printf '%08x\n' $((0x7fc00000))
                    return
                fi
                printf '%08x\n' $(((sign << 31) | 0x7f800000))
                return
            fi
        
            if (( ea == 0 && fa == 0 || eb == 0 && fb == 0 )); then
                printf '%08x\n' $((sign << 31))
                return
            fi
        
            # Convert subnormals to a normalized significand/exponent.
            # m is a 24-bit significand for normals.
            local ma mb
            if (( ea == 0 )); then
                ma=$fa
                ea=1
                while (( (ma & 0x800000) == 0 )); do
                    ma=$((ma << 1))
                    ((ea--))
                done
            else
                ma=$((fa | 0x800000))
            fi
        
            if (( eb == 0 )); then
                mb=$fb
                eb=1
                while (( (mb & 0x800000) == 0 )); do
                    mb=$((mb << 1))
                    ((eb--))
                done
            else
                mb=$((fb | 0x800000))
            fi
        
            # Multiply the two 24-bit significands.
            # Product is up to 48 bits.
            local p=$((ma * mb))
            local e=$((ea + eb - 127))
        
            # Normalize product.
            #
            # ma*mb has binary point after bit 46.  If bit 47 is set,
            # product is [2,4), otherwise [1,2).
            local shift
            if (( p & 0x800000000000 )); then
                shift=24
                ((e++))
            else
                shift=23
            fi
        
            # Extract 23 fraction bits plus guard/round/sticky information.
            local frac=$(( (p >> shift) & 0x7fffff ))
            local guard=$(( (p >> (shift - 1)) & 1 ))
            local round=$(( (p >> (shift - 2)) & 1 ))
            local sticky=0
        
            if (( shift >= 3 )); then
                local mask=$(( (1 << (shift - 2)) - 1 ))
                (( (p & mask) != 0 )) && sticky=1
            fi
        
            # Round-to-nearest, ties-to-even.
            if (( guard && (round || sticky || (frac & 1)) )); then
                ((frac++))
                if (( frac == 0x800000 )); then
                    frac=0
                    ((e++))
                fi
            fi
        
            # Overflow -> infinity.
            if (( e >= 255 )); then
                printf '%08x\n' $(((sign << 31) | 0x7f800000))
                return
            fi
        
            # Normal result.
            if (( e > 0 )); then
                printf '%08x\n' $(((sign << 31) | (e << 23) | frac))
                return
            fi
        
            # Underflow into the subnormal range.
            #
            # At this point the normalized significand represented by
            # (1.frac) must be shifted right by 1-e positions.
            local mant=$((0x800000 | frac))
            local rshift=$((1 - e))
            local lost=0
            local halfway=0
            local low=0
        
            if (( rshift >= 25 )); then
                # Everything rounds to zero (unless the exact value is
                # sufficiently close, which it cannot be here).
                mant=0
            else
                low=$((mant & ((1 << rshift) - 1)))
                mant=$((mant >> rshift))
        
                halfway=$((1 << (rshift - 1)))
        
                if (( low > halfway || (low == halfway && (mant & 1)) )); then
                    ((mant++))
                fi
            fi
        
            # Rounding a subnormal can produce the smallest normal.
            if (( mant >= 0x800000 )); then
                printf '%08x\n' $(((sign << 31) | (1 << 23)))
            else
                printf '%08x\n' $(((sign << 31) | mant))
            fi
        }