Fixed Point (s31.32)

Fixed Point (s31.32) allows Mecrisp-Stellaris to compute results including decimal places using microprocessors lacking a “Floating Point Unit”.

Fixpoint numbers are stored ( n-comma n-whole ) and can be handled like signed double numbers

Examples

d-

( df1 df2 - - df3 )

Addition of two fixpoint numbers

9,12 7,13 d+ f. 16,24999999976716935634613037109375 ok.

d+

( df1 df2 - - df3 )

Subtraction of two fixpoint numbers

9,12 7,13 d- f. 1,98999999999068677425384521484375 ok.

f/

( df1 df2 - - df3 )

Division of two fixpoint numbers

9,12 7,13 f/ f. 1,27910238411277532577514648437500 ok.

f*

( df1 df2 - - df3 )

Multiplication

9,12 7,13 f* f. 65,02559999795630574226379394531250 ok.

hold<

( char - - )

Adds character to pictured number output buffer from behind

f#S

( n-comma1 - - n-comma2 )

Adds 32 comma-digits to number output

f#

( n-comma1 - - n-comma2 )

Adds one comma-digit to number output

9,12 f# f. 90,11999999987892806529998779296875 ok.

f .

( df - - )

Prints a fixpoint number with 32 fractional digits

9,12 7,13 f/ f. 1,27910238411277532577514648437500 ok.

f.n

( df n - - )

Prints a fixpoint number with n fractional digits

9,12 7,13 f/ 3 f.n 1,279 ok.

number

( Counted-String-Address - - 0 )

Tries to convert a string to a number.

( cstr-addr - - n 1 )

( cstr-addr - - n-low n-high 2 )

Fixpoint numbers are stored ( n-comma n-whole ) and can be handled like signed double numbers.

For fixpoint-numbers you can use d+ and d- as usual, and do division and multiplication with f/ and f*.

Examples

Note

Results starting with * are using Unix bc (arbitrary-precision arithmetic language and calculator) on a 64 bit FreeBSD system.

Display a Number

9,12 f. 9,11999999987892806529998779296875  ok.

Adds One Comma-digit To Number Output

9,12 f# f.
  90,11999999987892806529998779296875  ok.

Removes Anything Before Comma

1111,1234 f#S f. 0,12339999992400407791137695312500  ok.

Addition

9,12 7,13 d+ f.
  16,24999999976716935634613037109375  ok.
  * 16.25

Subtraction

9,12 7,13 d- f.
  1,98999999999068677425384521484375  ok.
  * 1.99

Division

9,12 7,13 f/ f.
  1,27910238411277532577514648437500  ok.
  * 1.27910238429172510518

Multiplication

9,12 7,13 f* f.
  65,02559999795630574226379394531250  ok.
  * 65.0256

Display Only Three Decimal Places

9,12 7,13 f/ 3 f.n
  1,279  ok.

s31.32 Insights

Andrew is a Mathematician and was very kind to email me some insight into s31.32 which I have reproduced here. Thanks Andrew!

from:  Andrew Palm
date:  Fri, May 4, 2018
subject: Re: s31.32

Essentially, a s31.32 number is just a 64-bit signed integer (twos-complement), which is a double in M-S Forth. If the stack has two singles ( s1 s2 ) that represent a double, the s1 single is interpreted as the lower 32 bits, s2 the higher 32 bits. If this double is interpreted as an s31.32, then s1 is the fractional part and s2 is the whole number part. Note that Matthias calls the fractional part the “comma part” because those crazy Europeans use a comma instead of a decimal point. ;-> In other words, we imagine a decimal point (or a comma) between the fractional and whole parts.

Now converting from a single to a double or an s31.32 and back is confusing. If you want to convert an ordinary single s to an ordinary double, you do this: If s is 0 or positive, put a 0 on top of it on the stack. If s is negative, put a -1 on top of it on the stack (because of twos-complement). This is because we put s in the lower 32 bits. On the other hand, if you want to convert a single to an s31.32, you do a 0 swap, putting s on top of a zero on the stack, no matter what the sign of s is. This is because in this case you are putting s in the high 32 bits as the whole part and there is a zero fractional part. It took me a while to sort this out!

To add or subtract s32.32 values you just treat them as doubles and use d+ or d-. To multiiply or divide them you do not use double multiplication or division. Instead you use f* and f/. In the case of multiplication, what goes on behind the scenes is as follows: If you want to multiply the two s31.32 values x and y, first multiply them as doubles to get a 128 bit result x*y, then discard the bottom 32 bits (by right shifting 32 times, of course) and finally keep the bottom 64 bits from that. Here is why: An s31.32 value x is represented by the double value a where x = a/(2^32). If we multiply x by another s31.32 value y represented by the double b, so y = b/(2^32), then x*y = [a*b/(2^32)]/(2^32). That is, a*b/(2^32) is the double value representing x*y. If you do division, we get x/y = [(a/b)*(2^32)]/(2^32) so we take the double division result a/b and multiply it by 2^32 (left shift 32 times) to get the s31.32 representation of x/y.

If you search for “fixed point math” or “fixed point muliplication” or something similar you will get many hits. Including “s31.32” results in almost no hits because it is a special case of fixed point. When I did some DSP on a dsPIC33F I used s0.15, usually called Q15 or Q0.15. This is 16 bit signed integers with the lowest 15 bits all fractional, and no whole part. It is nice for signal work, as it represents numbers between -1 (represented by -32768) and 1 - (2^16) (represented by 32767). If the values get too small or too large, you divide them or multiply them by a gain value to avoid overflow, etc. I think I read somewhere that in the early days of cell phones, they used Q15 for the DSP math. There are often processor instructions to facilitate doing fixed point.

As an example, see: https://sestevenson.wordpress.com/fixed-point-multiplication/

See also: http://www.digitalsignallabs.com/downloads/fp.pdf It is kind of “mathy” in its tone, but seems otherwise OK. The s31.32 representation is called A(31, 32) in the document. The (fairly) standard notation is Q31.32.

Well, I hope this helps a bit.

Cheers, Andy

Change the comma to a period ?

What is the best way to print out a fixpoint number with a ‘.’ for the decimal point rather than a comma (specifically in the Pico Forth)? f.n prints with the comma radix point - can this be changed?

Answer

: f..3 ( f -- ) tuck dabs 0 <# #s 2drop [char] . hold< f# f# f# drop sign 0 0 #> type space ;

: f.. ( f -- ) tuck dabs 0 <# #s 2drop [char] . hold< f#s drop sign 0 0 #> type space ;

Output

3,14159 f..3
3,14159 f..3 3.141  ok.

-2,718281828 f..3
-2,718281828 f..3 -2.718  ok.


3,14159 f..
3,14159 f.. 3.14158999989740550518035888671875  ok.

-2,718281828 f..
-2,718281828 f.. -2.71828182786703109741210937500000  ok.