math16 on the Spectrum

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

math16 on the Spectrum

Post by derekfountain »

I'm trying to get the basics of math16 working on the Spectrum. Cobbling together information from round the 'net, my code is:

Code: Select all

#pragma printf %f

#include <stdio.h>
#include <math.h>

#ifdef __MATH_MATH16
    #define DOUBLE          half_t
#else
    #define DOUBLE          float
#endif


int main(void)
{
  DOUBLE t1 = (DOUBLE)10000.0;
  DOUBLE t2 = (DOUBLE)100000.0;

  printf("t1 = %f, t2 = %f\n",(float)t1,(float)t2);
  return 0;
}
which I compile with:

Code: Select all

zcc +zx -vn -SO3 --max-allocs-per-node200000 -clib=sdcc_iy -startup=1 --math16 --math32 math16_simple.c -o m16s -create-app
This prints

Code: Select all

t1 = 9999.999000 t2 = -31071.990000
Why isn't it handling the larger value correctly? I'm guessing it's my use of printf(), but I'm not really sure.

If I remove the --math16 it works correctly, so the 32 bit float library seems OK.

P.S. My z88dk is a few months old. Do I need to update?
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

As noted here: https://github.com/z88dk/z88dk/wiki/Cla ... ----math16 sdcc doesn't understand natively what a 16 bit float is, a half_t is actually typedef int half_t. It's only sccz80 that natively supports the type and implicit conversions.

With that knowledge, it's fairly obvious where (rounding errors aside) your printf values are coming from.

In order to use a half_t and convert between types with sdcc you have to use the explicit conversion functions defined here: https://github.com/z88dk/z88dk/blob/mas ... ath.h#L593
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

dom wrote: Wed Nov 24, 2021 9:41 pm With that knowledge, it's fairly obvious where (rounding errors aside) your printf values are coming from.
I assure you it isn't! I really don't understand what's going on inside printf or the math16 library.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

derekfountain wrote: Wed Nov 24, 2021 10:17 pm
dom wrote: Wed Nov 24, 2021 9:41 pm With that knowledge, it's fairly obvious where (rounding errors aside) your printf values are coming from.
I assure you it isn't! I really don't understand what's going on inside printf or the math16 library.
So t2 (which is an int16_t for sdcc) would hold the value of 34464 (100000 - 65536), but since a half_t is signed it's interpreted as -31072
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

I'm still floundering about, and I've got as far as this:

Code: Select all

#pragma printf %f

#include <stdint.h>
#include <stdio.h>
#include <math.h>

int main(void)
{
  half_t t1 = f16_i32(10000);
  half_t t2 = f16_u32(10000);

  printf("t1 = %f\nt2 = %f\n",f32_f16(t1),f32_f16(t2));
  return 0;
}
which results in this compile time error:

Code: Select all

>zcc +zx -vn -SO3 --max-allocs-per-node200000 -clib=sdcc_iy -startup=1 --math16 --math32  math16_simple.c -o m16s -create-app
Error at file 'math/float/math16/lm16/c/sdcc/___ulong2h_callee.asm' line 10: symbol 'cm16_sdcc___ulong2h_callee' not defined
Error at file 'math/float/math16/lm16/c/sdcc/___ulong2h_callee.asm' line 11: symbol 'cm16_sdcc___ulong2h_fastcall' not defined
Errors in source file /home/derek/BEETLE/Derek/dev/Spectrum/DevelopmentTools/Z88DK/z88dk/lib/config/../..//libsrc/_DEVELOPMENT/target/zx/zx_crt.asm.m4:
Error at file 'math/float/math16/lm16/c/sdcc/___ulong2h_callee.asm' line 10: symbol 'cm16_sdcc___ulong2h_callee' not defined
Error at file 'math/float/math16/lm16/c/sdcc/___ulong2h_callee.asm' line 11: symbol 'cm16_sdcc___ulong2h_fastcall' not defined
It's the f16_u32() call which produces that. Looks like a symbol missing in the library? I've not much clue to be honest. :)
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

It looks like there was a rename of those functions at one point in time (from ulong2fs to ulong2h) and the file got renamed but not the functions inside.

I've pushed a fix for that, and tweaked a few things in classic so that your example is now working for all classic/newlib and sdcc/sccz80:

Code: Select all

% zcc +cpm -clib=sdcc_ix m16.c --math16 --math32
% ln -s a_CODE.bin a.com
% z88dk-ticks a.com
t1 = 9999.999000
t2 = 9999.999000

% zcc +cpm -clib=new m16.c --math16 --math32
% z88dk-ticks a.com
t1 = 9999.999000
t2 = 9999.999000

% zcc +test --math16 --math32 m16.c
% z88dk-ticks a.bin
t1 = 10000.000000
t2 = 10000.000000

% zcc +test --math16 --math32 m16.c -compiler=sdcc
% z88dk-ticks a.bin
t1 = 10000.000000
t2 = 10000.000000

derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

Thanks dom. I'll be returning to this math16 stuff over the weekend. I might be bothering you some more. :)
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

OK, I picked up nightly and confirmed the fix is working. :)

But just to confirm I still don't know what I'm doing, I added a zero to my testcase:

Code: Select all

#pragma printf %f

#include <stdint.h>
#include <stdio.h>
#include <math.h>

int main(void)
{
  half_t t1 = f16_i32(10000);
  half_t t2 = f16_u32(100000);

  printf("t1 = %f\nt2 = %f\n",f32_f16(t1),f32_f16(t2));
  return 0;
}
It still prints t2 as 65535.990000.

I thought was was the printf() interface I'm not understanding, but code like this:

Code: Select all

  half_t t4 = addf16( f16_i32(35000), f16_i32(35000) );  // 70000
  t4 = subf16( t4, f16_i32(10000) );                     // 60000
prints 55519.99000, so I'm clearly still missing something rather fundamental. =(
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

The range of a half-precision float is limited, so I think you're going out of range. It looks like the converters don't seem to have bounds checking for performance reasons.

There's some background on this datatype here: https://en.wikipedia.org/wiki/Half-prec ... int_format
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

dom wrote: Fri Nov 26, 2021 11:45 am The range of a half-precision float is limited, so I think you're going out of range. It looks like the converters don't seem to have bounds checking for performance reasons.

There's some background on this datatype here: https://en.wikipedia.org/wiki/Half-prec ... int_format
Ah right. I had already been to that Wikipedia page, but I missed this rather important point:

This can express values in the range ±65,504, with the minimum value above 1 being 1 + 1/1024.

I think what I have in mind will still work, so I'll press on with the limitations in mind.

Thanks!
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

I'm quite interested in what the use case is, the best I can come up with is something graphically related.

It's significantly faster than a 40/48 bit library (see the z88 cube demo clips), but still not fast enough for real time calculations. So I can see a use case in quickly precalculating lookup tables.

Regardless it's an interesting piece of code, leveraging hardware multiplication (if available) and using /that/ trick for faster square roots. There's now the source code for 7 different FP implementations in the tree which would make for a great benchmark/comparison/hybridise experiment: each library makes different trade-offs and compromises.
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

OK, how about this one:

Code: Select all

#pragma printf %f
#pragma scanf  %f

#include <stdio.h>
#include <math.h>

#define COUNT 500
half_t results[COUNT];

int i;

int main(void)
{
  half_t s1;
  half_t s2;
  half_t hy;

  s1=f16_u32(4);
  s2=f16_u32(5);
  hy=hypotf16( s1, s2 );

  printf("s1 = %f\ns2 = %f\nhy = %f\n\n",f32_f16(s1),f32_f16(s2),f32_f16(hy));



  for(i=0;i<COUNT;i++)
  {
    results[i]=hypotf16( s1, s2 );
    printf("hy=%f\n",f32_f16(results[i]));
  }

  return 0;
}
Compiling with:

Code: Select all

zcc +zx -vn -SO3 --max-allocs-per-node200000 -clib=sdcc_iy -startup=4 --math16 --math32  math16.c -o m16hyp -create-app
The top bit works correctly, the hypotenuse being correctly calculated.

But the loop, which I was playing with to get some idea of speed, doesn't work. As shown it reports the result lines as 0.000000. If I move the hypotf16() call into the printf() (i.e. bypass the results array) it seems to work. But I think it's mangling the stack: if I move the int i; declaration inside main() the loop doesn't iterate. If I put the results array inside main() it loops but produces some very odd numbers.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

I get the feeling there's an issue with the callee implementation of hypotf16() if the suspicion is stack wonkiness.

I'm not near a suitable environment to try it out, but maybe if you add a #undefine hypotf16 after the includes does it work?
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

sdcc doesn't appear to have #undefine.

I hacked the header file:

Code: Select all

extern half_t hypotf16(half_t x,half_t y); 
extern half_t hypotf16_callee(half_t x,half_t y) __z88dk_callee; 
#define hypotf16DISABLED(a,b) hypotf16_callee(a,b)
which I think will do what you mean?

The test case now finds other, more interesting ways to crash the Spectrum. =(
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

Well, that's sorted out my plans for this evening...

It looks like most of the testing was with sccz80 since testing with a native type is much easier. From a glance at the code, it looks like the callee and non-callee implementations for sdcc have been swapped over for the sdcc implementations which isn't great.
User avatar
dom
Well known member
Posts: 2072
Joined: Sun Jul 15, 2007 10:01 pm

Re: math16 on the Spectrum

Post by dom »

I scanned through the other functions and it looks like hypotf16() was, somewhat predictably, the only swapped one.

I'd like to hope that this was the last issue you'll find, but I won't hold my breath...
User avatar
feilipu
Member
Posts: 45
Joined: Tue Nov 15, 2016 5:02 am

Re: math16 on the Spectrum

Post by feilipu »

Sorry for not chiming in earlier. Thanks for sorting this out, Dom.
I didn't really test it with sdcc/classic, so breakage everywhere, as you've fixed.
User avatar
feilipu
Member
Posts: 45
Joined: Tue Nov 15, 2016 5:02 am

Re: math16 on the Spectrum

Post by feilipu »

With regard to performance, since I've incorporated even faster multiply routines into math32, by abandoning the 16_8x8 core metaphor for z80 the advantage for math16 is has somewhat faded. It is still worth using for specific applications, but it is worth testing whether math32 is actually fast enough.

I'm considering rewriting math16 to use a 16h_16x16 multiply routine, but given it is using the integer math 32_16x16 as a basis across all platforms (z80, z180, z80n) I'm not sure adding yet more code is a good idea.
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

dom wrote: Fri Nov 26, 2021 2:20 pm I'm quite interested in what the use case is, the best I can come up with is something graphically related.
It was 6 months ago, but since I finally finished the project I'll fill in this blank. ;)

I started with a port of the Boids algorithm, as demonstrated here. I implemented it on Linux, then started wondering how a Spectrum might cope with it. Over it went to z88dk, but since it uses floating point numbers and calculates the position of each boid relative to all the other boids for each frame, all using Pythagoras, the poor Z80 wasn't very good at it. I updated the algorithm to use the 16 bit float library, which is where this thread came from.

But it was still a matter of seconds-per-frame rather than frames-per-second, so I abandoned that and rewrote the algorithm using integers and methods which don't loop over the entire swarm each time one of them is updated.

I based a simple game around the concept, details of which are on Spectrum Computing here.
User avatar
feilipu
Member
Posts: 45
Joined: Tue Nov 15, 2016 5:02 am

Re: math16 on the Spectrum

Post by feilipu »

The demo looks very good. AFK so can’t run it on my Next.

The bugs you mentioned on the Spectrum Computing blog were fixed by Dom late last year, right?
If there are further issues I like to fix them, so please raise an issue.

Cheers, P
derekfountain
Member
Posts: 121
Joined: Mon Mar 26, 2018 1:49 pm

Re: math16 on the Spectrum

Post by derekfountain »

Yes, everything I found got fixed. :)
Post Reply