math16 on the Spectrum

ZX80, ZX 81, ZX Spectrum, TS2068 and other clones
Post Reply
derekfountain
Member
Posts: 74
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: 1506
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: 74
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: 1506
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: 74
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: 1506
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: 74
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: 74
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: 1506
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: 74
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: 1506
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: 74
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: 1506
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: 74
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: 1506
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: 1506
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...
Post Reply