News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Basic-style Str$

Started by jj2007, July 05, 2009, 11:45:44 PM

Previous topic - Next topic

jj2007

As an alternative to Masm32 FloatToStr and crt_sprintf, the attached FloatStr.asm provides an easy way to print numbers using the print Str$(123) syntax.

Reasons to use it (at your own risk, of course):
- faster than FloatToStr (*2) and crt_sprintf (*14)
- flexible (integer, reals, immediate reals, registers can be freely mixed)
- full REAL10 precision
- free :bg

Reasons not to use it:
- not tested for all possible combinations

Testing float$
PI      3.14159265358979323846 (there are more digits...)
Str$    3.141592653589793238
crt     3.14159265358979310

mov eax, 333: eax/3 =   111
Forced real:     123.0000000
Forced integer:  333/4 = 83

Str$ performs simple calculation (up to 5 arguments,
CAUTION: sequential, i.e. 2*5+2*5=(10+2)*5):
See the result of a calculation involving 5 different data types
(imm32, immReal, reg32, real10, reg8):
esi=52, ebx=23:
120/2.5+esi+MyReal10+bl = 123.4567890123456789


Timings on Celeron M:
291     cycles for Str$
605     cycles for FloatToStr
4317    cycles for crt_sprintf
99      cycles for dwtoa (int32 only)


To use it, extract all files to a folder of your choice and run InstallFloatStr.bat

Note this is the replacement for the slightly inexact float$ shown here.

EDIT: Qwords do not print directly, but edx::eax can be used:

.data
MyQword dq 1234567890123456789
.code
mov eax, dword ptr MyQword
mov edx, dword ptr MyQword[4]
print Str$("\nA qword: %i", edx::eax)


QuoteA qword: 1234567890123456789

Edit(2+3+4): New version with increased precision attached.
Edit(5): Chr$(eax) example added
Edit(6): MovVal example added:    MovVal MyReal10, chr$("-123456.789e-33")
Edit(7): Bugfix - used to return 0.0 if ecx was zero on proc entry

[attachment deleted by admin]

dedndave

cool Jochen
looks like you worked hard on this one
i thought you might have something that displayed 64-bit integers up your sleeve - lol
i noticed that your value for Pi has 21 usable (i.e. correct) digits
i only got 20 - i may need to re-visit my conversion method
EDIT
oh - lol - that is a string - ya got me

jj2007

#2
Quote from: dedndave on July 07, 2009, 03:08:26 AM
i noticed that your value for Pi has 21 usable (i.e. correct) digits
i only got 20 - i may need to re-visit my conversion method
EDIT
oh - lol - that is a string - ya got me

Yes, that was a string - but with the attached new version, you do get 19 digits, see below.
For getting the full 19, use Str$("%Jf", MyReal10).
Note that the inherent FPU precision implies that the 19th digit is not guaranteed to be exact. Use uppercase I for 18 digits.

        1x234567890123456789 digits precision
PI      3.14159265358979323846... (there are many more digits...)
Str$    3.141592653589793238      (jj2007)
crt     3.14159265358979310       (CRT printf or sprintf)
FloatTo 3.141593                  (Masm32 lib FloatToStr)

Log2(e) 1.44269504088896340736...
Str$    1.442695040888963407
crt     1.44269504088896340
FloatTo 1.442695

Lg2(10) 3.3219280948873623480...
Str$    3.321928094887362348
crt     3.32192809488736220
FloatTo 3.321928

Lg10(2) 0.30102999566398119521...
Str$    0.3010299956639811952
crt     0.30102999566398120
FloatTo 0.30103

Lge(2)  0.69314718055994530942...
Str$    0.6931471805599453094
crt     0.69314718055994529
FloatTo 0.6931472


mov eax, 333: eax/3 =   111
A qword: 1234567890123456789    (using edx::eax)
Forced real:     123.0000000
Forced integer:  333/4 = 83

Str$ performs simple calculations
(up to 5 arguments, CAUTION: sequential, i.e. 2*5+2*5=(10+2)*5):
See the result of a calculation involving 5 different data types
(imm32, immReal, reg32, real10, reg8, esi=52, ebx=23):

120/2.5+esi+MyReal10+bl = 123.4567890123456789

Syntax: print Str$("\nThe value is %9f", MyVar1*MyVar2/bx+12.34+esi)
Qualifiers: \n newline, \t tab
%i = int32; %f = real, 7 digits (default)
%3f = real, 3 digits; %Af = real, 10 digits; %Jf = real, 19 digits


EDIT: To avoid confusion, latest version from now on always attached to first post.

dedndave

i ran the FPU value for Pi through my eval.exe program:
+3.14159265358979323851280895940618620443274267017841339111328125  FPU representation of Pi
+3.1415926535897932384626..........................                                              actual value of Pi
20 usable decimal digits (just barely - lol)

jj2007

Quote from: dedndave on July 14, 2009, 11:58:24 AM
i ran the FPU value for Pi through my eval.exe program:
+3.14159265358979323851280895940618620443274267017841339111328125  FPU representation of Pi
+3.1415926535897932384626..........................                                              actual value of Pi
20 usable decimal digits (just barely - lol)

Str$ yields 3.1415926535897932384626... and that is what you also get as valid digits. The 5 is already incorrect, so it's 19 digits :bg

Raymond's Simply FPU says REAL10 has 19.5 digits precision. No way to go beyond that... unless we embark for REAL16.

dedndave

it is considered a usable digit, as the "4626" rounds up to "5000"
it is coincidental, however - it applies to this specific value - not all values
if you have to represent Pi with 20 decimal digits, what would the last digit be? (answer: 5)
the value shown is the precise evaluation of the FPU's FLDPI binary representation of Pi
(3.14159265358979323851280895940618620443274267017841339111328125)
if Ray returns a different value, he is cheating somehow - lol
i would think the FLDPI has the geometrically closest value to Pi that may be represented in extended float
QuoteStr$ yields 3.1415926535897932384626
that is 23 decimal digits - i think you are looking at a value that was defined as an ascii string
QuoteRaymond's Simply FPU says REAL10 has 19.5 digits precision. No way to go beyond that
that contradicts the 23 digit value

jj2007

Quote from: dedndave on July 14, 2009, 01:24:27 PM
it is considered a usable digit, as the "4626" rounds up to "5000"

Sure?

12.34690 rounded to the nearest tenth is 12.3

Quote
QuoteStr$ yields 3.1415926535897932384626
that is 23 decimal digits

I meant the ones up to 3238 - that's why I put them in blue.

dedndave

yes - i am sure
1.4626 rounded to the nearest tenth is 1.5

dedndave

3.141592653589793238295968524909085317631252110004425048828125     next lower extended real value

3.14159265358979323851280895940618620443274267017841339111328125  FPU Pi

3.1415926535897932387296493939032870912342332303524017333984375    next higher extended real value

notice how they all end with 5 - that is because ALL binary fractions end with 5 when represented in decimal
binary    decimal

.1        .5
.01       .25
.001      .125

and so on

jj2007

Quote from: dedndave on July 14, 2009, 01:55:54 PM
3.141592653589793238295968524909085317631252110004425048828125     next lower extended real value
3.14159265358979323851280895940618620443274267017841339111328125  FPU Pi
3.1415926535897932387296493939032870912342332303524017333984375    next higher extended real value

I see what you mean, Dave. The problem is that we are demanding something from the FPU that it cannot perform: Getting the 46. The nearest to the 46 that is representable with 64 digits is the 51 - but one tick of the LSB is -22/+21, so even the 5 is no longer a valid digit, if not by accident. Olly cuts off as 32380, which is slightly incorrect in that the 0 should not be shown. Crt sprintf behaves even worse...

dedndave

that's right
here is the problem:
if you round 3.14159265358979323851 to 19 digits, it is 3.141592653589793239 - you induce an error by rounding

dedndave

#11
well - that is half the problem - lol
before we ever got our hands on the binary value from the FPU,
the guys at intel had to round the correct value of Pi to the nearest value representable by their numbering system
THEN, we come along and round it again to convert it to a decimal string

3.1415926535897932384626... actual value of Pi
3.1415926535897932385128... closest extended real binary value
3.141592653589793239        our best interpretation of that binary value
3.141592653589793238        actual value of Pi rounded to 19 digits

i suppose "our best interpretation" might actually be the 20 digit value 3.1415926535897932385
but, that is stretching the 191/2 digit resolution of the 64-bit binary
i don't think it is practical to always render 20 digits
you might do something like this:
if the leading digit is 1-4, render 20 digits
otherwise, render 19 digits
(the 1-4 is a guess - we would have to find the correct value to use in place of the 4)
to complicate issues a bit, we could examine a few leading digits to determine the switch-over point
typically, when they refer to "1/2 digit", they mean a leading 1
many digital volt-meters display 31/2 or 41/2 digits
most of them mean a leading 1
a few use a leading 3 - they display 5 digits up to 3.9999, then switch to 4 digits at 4.000

EDIT
examining the 2 leading digits would be a practical way to determine the switch point

jj2007

Quote from: dedndave on July 14, 2009, 03:05:44 PM

a few use a leading 3 - they display 5 digits up to 3.9999, then switch to 4 digits at 4.000


Like this?

include \masm32\include\masm32rt.inc
include \masm32\macros\FloatStr.asm

.code
start:
print Str$("%8f\n", 3.99999999)
print Str$("%9f\n", 3.99999999)
exit
end start


4.0000000
3.99999999

dedndave

you have the right idea, Jochen
now all you need is the right cross-over point
you should examine with care whether or not digits are "usable"

3.141592653589793238 295968524909085317631252110004425048828125     next lower extended real value
3.141592653589793238 51280895940618620443274267017841339111328125  FPU Pi
3.141592653589793238 7296493939032870912342332303524017333984375    next higher extended real value

at this point, we no longer take into consideration what the original intended value was meant to be (Pi in this case)
the value you are converting into a string has a precise evaluation
in this case, that would be 3.14159265358979323851, as you have no intention of displaying more than 20 digits
rounding it to 19 digits would be 3.141592653589793239
anything we display from 3.1415926535897932381 to 3.1415926535897932389 is closer to the precise value than rounding it to 19 digits
that one is coincidental, because the 20th digit is a 5
if the 20th digit were a 3, rounding the value to 19 digits would round downward (that 3 would be truncated)
notice that the 20th digit could be a 2, and it would still be closer to the precise value than if it were rounded to 19 digits
in fact, we could display 20 digits with anything from 2 to 5 at the end and it would be closer to the precise value than if it were rounded off
the point is, "usable digits" are not neccessarily the correct value
they are usable, so long as they get us closer to the precise value than if they were rounded off



dedndave

i am guessing that if the first 2 signifigant digits of a value are 10 through 36, 20 usable digits are available
if they are 37 through 99, only 19 digits are usable
that guess is based on 2 x (2^64) = 36,893,488,147,419,103,232
at that value, each change of 1 to the lsb changes the the result by 2

403F_FFFFFFFF_FFFFFFFF: +36893488147419103230
4040_80000000_00000000: +36893488147419103232
4040_80000000_00000001: +36893488147419103236
4043_9FFFFFFF_FFFFFFFF: +368934881474191032288
4043_A0000000_00000000: +368934881474191032320
4043_A0000000_00000001: +368934881474191032352
4046_C7FFFFFF_FFFFFFFF: +3689348814741910322944
4046_C8000000_00000000: +3689348814741910323200
4046_C8000000_00000001: +3689348814741910323456

i added hex output to the prog - lol
maybe i shoulld do another decade or two
you can see what happens as i go above that value - the lsd changes by 4 instead of 2 in the first example