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
      16
      ·
      2 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
      }