ZX Floating point to Decimal code in BASIC

Any discussions related to the creation of new hardware or software for the ZX80 or ZX81
David G
Posts: 44
Joined: Thu Jul 17, 2014 7:58 am

ZX Floating point to Decimal code in BASIC

Post by David G » Mon Jul 21, 2014 10:47 am

I wrote this 10 years ago, not sure how accurate it is. But it seems to work. Comments/corrections are welcomed.

David G

Code: Select all

Sub FloatToDec (ZxString$, DecNumber#)
    Dim FirstByte As Double
    Dim ZxDigits As Double
    Dim SecondByte As Double
    Dim t As Double
    Dim Xdigits As Double
    Dim ZxSign As Double
    Dim figure As Double

    zero$ = String$(5, Chr$(0))
    If ZxString$ = zero$ Then DecNumber# = 0: Exit Sub

    'get sign from second byte
    SecondByte = Asc(Mid(ZxString$, 2, 1))
    ZxSign = SecondByte And 128

    'Adjust second byte after getting sign
    Mid(ZxString$, 2, 1) = Chr$(SecondByte Or 128)

    'drop trailing bytes that equal 0
    'result in x$
    For f = 5 To 2 Step -1
        If Asc(Mid(ZxString$, f, 1)) Then
            Exit For
        End If
    Next f
    x$ = Mid(ZxString$, 2, f - 1)

    'get # of binary digits needed from first byte
    FirstByte = Asc(Mid(ZxString$, 1, 1))
    ZxDigits = FirstByte - 128

    'drop trailing bits that equal 0
    DecNumber# = Asc(Right(x$, 1))
    For f = 8 To 1 Step -1
        If DecNumber# And 1 Then Exit For
        Let DecNumber# = DecNumber# / 2
    Next f
    'f = number of significant bits

    'get # of binary digits in base number
    Xdigits = (Len(x$) - 1) * 8 + f

    'convert string to base number
    t = 0   'counter for powers of figure (256)
    figure = 2 ^ f
    For f = (Len(x$) - 1) To 1 Step -1
    DecNumber# = DecNumber# + Asc(Mid(x$, f, 1)) * figure * (256 ^ t)
    t = t + 1
    Next f

    'based on exponent, modify number to correct number
    'of binary digits
    Let DecNumber# = DecNumber# * 2 ^ (ZxDigits - Xdigits)
    If ZxSign Then Let DecNumber# = -DecNumber#
End Sub
Last edited by David G on Sun Jul 27, 2014 5:50 am, edited 1 time in total.

User avatar
XavSnap
Posts: 689
Joined: Sat May 10, 2008 4:23 pm

Re: ZX Floating point to Decimal code in BASIC

Post by XavSnap » Mon Jul 21, 2014 11:01 pm

Hi,
Thanks David.

In VB81 Xur, the revers function (Decimal to ZXfp), seem to be bugged using "1.5", "1,2" values ! :?
Can your have a look to this code : (Vb81 XuR/ZxToken:ModZXUtil.bas)
(I had to type VAL('"1.2") to get the right value!)

Code: Select all


' ZX81 floating point number.
Type Zx81_Long
        Exp As Integer ' - Exponent
        man As String ' - Mantissa
End Type

Function dbl2zx81(Num As Single) As Zx81_Long
 'On Error Resume Next
 
' * Converts a double to an inline-basic-style ZX81 floating point number.
' *
' * IN:      num  - Number to convert (dbl)
' * OUT:     &exp - Exponent (integer)
' * OUT:     &man - Mantissa (char)
' *

Dim IntNum As Double
Dim tmpint1 As Double
Dim tmpint2 As Double

Dim TmpVal As String
Dim Expon As Double

'    /* Special case for zero */
    If Num = 0 Then
    dbl2zx81.Exp = 0#
    dbl2zx81.man = 0#
     Exit Function
    End If
        
'    /* If negative work with the absolute value */
    If Num < 0 Then Num = -Num

dbl2zx81.Exp = Int(Log(Num) / Log(2))

If dbl2zx81.Exp < -129 Or dbl2zx81.Exp > 126 Then Exit Function

IntNum = ((Num / (2# ^ (dbl2zx81.Exp + Expon#))) * 2147483648#)

' 65536=&h10000
' 32767=&h7FFF

tmpint2 = IntNum - (Fix(IntNum / 65536#) * 65536#) - 28
tmpint1 = (IntNum / 65536#) And 32767#

If tmpint2 < 0 Then tmpint2 = 0

TmpVal = Hex16(UnsignedToLong(tmpint1)) & Hex16(UnsignedToLong(tmpint2))
    
    dbl2zx81.man = TmpVal
    dbl2zx81.Exp = dbl2zx81.Exp + 129#
    Exit Function

Dbl_ERR:
    Disp_Err 11, Str(Num)
End Function

olofsen
Posts: 167
Joined: Wed Jan 08, 2014 12:29 pm

Re: ZX Floating point to Decimal code in BASIC

Post by olofsen » Tue Jul 22, 2014 6:53 pm

The "28" should perhaps be "128" to clear the sign bit for positive numbers?

Code: Select all

tmpint2 = IntNum - (Fix(IntNum / 65536#) * 65536#) - 128

User avatar
XavSnap
Posts: 689
Joined: Sat May 10, 2008 4:23 pm

Re: ZX Floating point to Decimal code in BASIC

Post by XavSnap » Tue Jul 22, 2014 7:14 pm

tmpint2 = IntNum - (Fix(IntNum / 65536#) * 65536#) - 28
tmpint2 = IntNum - (Fix(IntNum / 65536#) * 65536#) - 128

True, somethings wrong...
I don't know if i's due to a sign remover, or a "FIX" leek how had to correct the value using the "28" value !
In Zxfp, there isn't any sign, and all values are positive.
May be the VB integer leek, how use his own sign tag !
If the FIX value is translated in Integer, we lost 1 bit in the "FIX" return value...
:?:

olofsen
Posts: 167
Joined: Wed Jan 08, 2014 12:29 pm

Re: ZX Floating point to Decimal code in BASIC

Post by olofsen » Tue Jul 22, 2014 7:29 pm

May be we can compare intermediate results: for num=12345, intnum=3236167680, tmpint1=0 and tmpint2=49380 or 0xc0e4 but it should be 0x40e4?

David G
Posts: 44
Joined: Thu Jul 17, 2014 7:58 am

Re: ZX Floating point to Decimal code in BASIC

Post by David G » Sun Jul 27, 2014 6:07 am

For comparison, here is the same BASIC routine converted to C++

known issues:
* since it doesn't calculate the FP conversion in the exact same way as ZX81 Basic, it rounds differently.
* I haven't tested or implemented E+00 notation for extremely large or extremely small numbers

Code: Select all

#include <math.h> // or <tgmath.h>

double FloatToDec(BYTE* ZxFP)
{   // input is the 5 bytes that comprise the floating point number
    BYTE x[6];
    double DecNumber = 0;
    int f;
    int t;
    BYTE Xdigits;
    int ZxDigits;
    BOOL ZxSign;
    double figure;

    if(memcmp(ZxFP,"\000\000\000\000\000",5) == 0) 
    {
        return 0;
    }

    //'get sign from second byte
    ZxSign = ZxFP[1] & 128;

    //'Adjust second byte after getting sign
    BYTE dd = ZxFP[1];
    ZxFP[1] = dd | 128;

    //drop trailing BYTES that equal 0
    //'result in x$
    for( f = 4; f >= 1; f--)
    {
        if(ZxFP[f] != 0)
            break;
    }
    int len_x = f;
    memcpy(x,ZxFP+1, len_x); //x[len_x]=0;

    //'get # of binary digits needed from first byte
    BYTE FirstByte =ZxFP[0];
    ZxDigits = FirstByte - 128;

    // drop trailing BITS that equal 0
    BYTE bb = x[len_x - 1];
    for(f = 8; f >= 1 ; f--)	
    {
        if(bb & 1)
            break;
        bb = bb >> 1;
    }
    DecNumber = bb;
    //f now contains the number of significant bits
	
    //'get # of binary digits in base number
    Xdigits = ((unsigned char)len_x - 1) * 8 + (unsigned char)f;

    //'convert string to base number
    t = 0; //   'counter for powers of figure (256)
    figure = pow((double)2, f);
    for(f = len_x - 1; f >= 1; f--)
    {
        DecNumber = DecNumber + x[f-1] * figure * pow((double)256,t);
        t = t + 1;
    }
    //'based on exponent, modify number to correct number
    //'of digits
    DecNumber = DecNumber * pow((double)2,ZxDigits - Xdigits);
    if (ZxSign)
        DecNumber = -DecNumber;
    return DecNumber;
}
For display, I use sprintf "%f", and truncate any '0' characters after the decimal point ('.')
Last edited by David G on Sun Jul 27, 2014 6:31 am, edited 1 time in total.

David G
Posts: 44
Joined: Thu Jul 17, 2014 7:58 am

Re: ZX Floating point to Decimal code in BASIC

Post by David G » Sun Jul 27, 2014 6:23 am

For ZX81 floating point, the sign is in the 2nd value byte as shown in the original "ZX81 BASIC Programming" book.

Chapter 27 - Organization of memory
Attachments
fig27_3.jpg
Chapter 27, figure 3 (variable memory diagram)
fig27_3.jpg (11.51 KiB) Viewed 4078 times

David G
Posts: 44
Joined: Thu Jul 17, 2014 7:58 am

Re: ZX Floating point to Decimal code in BASIC

Post by David G » Sun Jul 27, 2014 7:28 am

In VB81 Xur, the revers function (Decimal to ZXfp), seem to be bugged using "1.5", "1,2" values !
Can your have a look to this code : (Vb81 XuR/ZxToken:ModZXUtil.bas)
(I had to type VAL('"1.2") to get the right value!)
It appears to work correctly. I ran the posted code in Visual Basic and a sample number was correctly converted.

I downloaded ModZXUtil.bas from http://zx81.vb81.free.fr/download/VB81_RES.zip, however if does not appear have the function "Decimal to ZXfp" (dbl2zx81) in it. In the code above I don't see "1.5" or "1,2".

User avatar
PokeMon
Posts: 2225
Joined: Sat Sep 17, 2011 6:48 pm

Re: ZX Floating point to Decimal code in BASIC

Post by PokeMon » Sun Jul 27, 2014 1:06 pm

For calculating/converting floating points in programs, this is a good source:

http://www.users.waitrose.com/~thunor/m ... ter17.html

User avatar
XavSnap
Posts: 689
Joined: Sat May 10, 2008 4:23 pm

Re: ZX Floating point to Decimal code in BASIC

Post by XavSnap » Sat Apr 07, 2018 7:26 am

Hi all,

I tried to use the David Gonzales's codes in an update of SnaList (from Chris Cowley) on Spectrum Tap/TZX.

A new C++ routine on the Floating Point conversion from _SAM_ & Hlide: (Fr lang)
http://forum.system-cfg.com/viewtopic.p ... b0e23879dd

Code: Select all

//*******************************************************************
// Spectrum/ZX81 Floating point decoder From _SAM_ et hlide
//*******************************************************************
//IN: 5 Bytes : 1 EXP + 4 Mantissa (sign on the secound byte)
//OUT: DOUBLE
double decode(unsigned char fp[5]) { 
	double mantisse;

if(fp[0]==0) {
	double t = fp[1]*65536 + fp[3]*256 + fp[2];
	if(fp[1]&128) t -= 256*65536;
	return t;
} 

	mantisse = (((fp[4]/256.0 + fp[3])/256.0 + fp[2])/256.0 + fp[1])/128.0;	
	if(mantisse < 1) mantisse += 1;  // nombre positif
	else mantisse = -mantisse; // nombre negatif
	
	return mantisse*pow(2, fp[0]-129);
}
[EDITED 2018-04-24]
Last edited by XavSnap on Tue Apr 24, 2018 5:54 pm, edited 1 time in total.

Post Reply