Site logo

Triceraprog
La programmation depuis le Crétacé

VG5000µ, jouer avec les nombres ()

Suite à l'article précédent, j'ai mis sur le dépôt GitHub un petit utilitaire Python qui reproduit les conversions entre la valeur du nombre et son codage en 4 octets.

Parfois, voir du code est plus simple qu'un long discours.

Et parce qu'on ne sait jamais trop quel sera la vie future du dépôt, voici le code des deux principales fonctions de l'outil.

import math


def get_byte(number):
    """Takes the current number, and returns the next byte encoding it with the reminder of the number to encode. """

    number *= 256
    result = int(number)
    return result, (number - result)


def encode(number):
    """Gets a number, returns it's encoded four bytes (memory layout, so exponent at the end)."""

    # If the number is zero, the encoding is immediate.
    # In fact, only the exponent has to be 0.
    if number == 0:
        return [0, 0, 0, 0]

    # Gets the sign from the number for later encoding
    sign = 0x80 if number < 0 else 0

    # We encode only positive numbers
    number = abs(number)

    # Shift the number so that the first fractional part bit
    # of the mantissa is 1 (0.1 binary is 0.5 decimal)
    exp = 0
    while number >= 0.5:
        number /= 2
        exp += 1
    while number < 0.5:
        number *= 2
        exp -= 1

    # Gets the three bytes encoding the mantissa
    o1, number = get_byte(number)
    o2, number = get_byte(number)
    o3, number = get_byte(number)

    # Clears the most significant bit
    # and replace it by the sign bit
    o1 &= 0x7F
    o1 |= sign

    # Encode exponent
    exp += 128

    # Returns an array (Z80 memory layout)
    return [o3, o2, o1, exp]


def decode(encoded):
    """ Takes four encoded bytes in the memory layout, and returns the decoded value. """

    # Gets the exponent
    exp = encoded[3]

    # If it's 0, we're done. The value is 0.
    if exp == 0:
        return 0

    # Extract value from the exponent
    exp -= 128

    # Extract the sign bit from MSB
    sign = encoded[2] & 0x80

    # Sets the most significant bit implied 1 in the mantissa
    encoded[2] = encoded[2] | 0x80

    # Reconstruct the mantissa
    mantissa = encoded[2]
    mantissa *= 256
    mantissa += encoded[1]
    mantissa *= 256
    mantissa += encoded[0]

    # Divide the number by the mantissa, corrected
    # by the 24 bits we just shifted while reconstructing it
    mantissa /= math.pow(2, 24 - exp)

    # Apply the sign to the whole value
    if sign:
        mantissa = -mantissa

    return mantissa