1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
|
;;
This procedure was written by Raymond Filiatreault.
This FloatToAscii function converts an 80-bit REAL number (Src) to its
decimal representation as a zero terminated alphanumeric string which
is returned at the specified memory destination unless an invalid
operation is reported by the FPU. The format of the string can be
specified as regular, or scientific notation. The number of decimal
places returned must also be specified but the total number of digits
must not exceed 18.
The source can be an 80-bit REAL number from the FPU itself or from
memory. If the source is taken from the FPU, its value will be preserved
there if no error is reported.
The source is not checked for validity. This is the programmer's
responsibility.
This procedure is based on using an FPU instruction to convert the
REAL number into a specific packed decimal format. After unpacking,
the decimal point is positioned as required.
Only EAX is used to return error or success. All other registers are
preserved.
____________________________________________________________________________________________
Calling: > call FloatToAscii Source, Destination, Decimal, FLAG
Source: Either a Pointer to a Data [T$Source: ...], or &NULL if you
"fld F$ / R$ / T$ Source" before calling.
Destination: Pointer to a Data Buffer for the Ascii String
(Max Size = 25 Bytes).
Decimal: The Number of wanted decimals (Max = 15).
FLAG (for notation choice): Either SCIENTIFIC or REGULAR.
____________________________________________________________________________________________
;;
; Flags:
[REGULAR 0 SCIENTIFIC 1]
Proc FloatToAscii:
Arguments @Source, @Destination, @Decimal, @Flag
Local @temporary, @eSize, @oldcw, @truncw, @stword
Structure @BCD 12, @bcdstr 0
fclex ;clear exception flags on FPU
; Get the specified number of decimals for result (MAX = 15):
On D@Decimal > 0F, mov D@Decimal 0F
; The FPU will be initialized only if the source parameter is not taken
; from the FPU itself (D@ Source <> &NULL):
.If D@Source = &NULL
fld st0 ;copy it to preserve the original value
.Else
mov eax D@Source
If eax > 0400_000
finit | fld T$eax
; Check first if value on FPU is valid or equal to zero:
ftst ;test value on FPU
fstsw W@stword ;get result
test W@stword 04000 ;check it for zero or NAN
jz L0> ;continue if valid non-zero
test W@stword 0100 ;now check it for NAN
jnz L1> ;Src is NAN or infinity - cannot convert
; Here: Value to be converted = 0
mov eax D@Destination | mov W$eax '0' ; Write '0', 0 szstring
mov eax &TRUE | finit | ExitP
Else
L1: finit | mov eax &FALSE | ExitP
End_If
.End_If
; Get the size of the number:
L0: fld st0 ;copy it
fabs ;insures a positive value
fld1 | fldl2t
fdivp ST1 ST0 ;->1/[log2(10)]
fxch | fyl2x ;->[log2(Src)]/[log2(10)] = log10(Src)
fstcw W@oldcw ;get current control word
mov ax W@oldcw
or ax 0C00 ;code it for truncating
mov W@truncw ax
fldcw W@truncw ;change rounding code of FPU to truncate
fist D@eSize ;store characteristic of logarithm
fldcw W@oldcw ;load back the former control word
ftst ;test logarithm for its sign
fstsw W@stword ;get result
test W@stword 0100 ;check if negative
jz L0>
dec D@eSize
L0: On D@eSize > 15, mov D@Flag SCIENTIFIC
; Multiply the number by a power of 10 to generate a 16-digit integer:
L0: fstp st0 ;get rid of the logarithm
mov eax 15
sub eax D@eSize ;exponent required to get a 16-digit integer
jz L0> ;no need if already a 16-digit integer
mov D@temporary eax
fild D@temporary
fldl2t | fmulp ST1 ST0 ;->log2(10)*exponent
fld st0 | frndint | fxch
fsub st0 st1 ;keeps only the fractional part on the FPU
f2xm1 ;->2^(fractional part)-1
fld1
faddp ST1 ST0 ;add 1 back
fscale ;re-adjust the exponent part of the REAL number
fxch
fstp st0
fmulp ST1 ST0 ;->16-digit integer
L0: fbstp T@bcdstr ;transfer it as a 16-digit packed decimal
fstsw W@stword ;retrieve exception flags from FPU
test W@stword 1 ;test for invalid operation
jnz L1<< ;clean-up and return error
; Unpack bcd, the 10 bytes returned by the FPU being in the little-endian style:
push ecx, esi, edi
lea esi D@bcdstr+9
mov edi D@Destination
mov al B$esi ;sign byte
dec esi | dec esi
If al = 080
mov al '-' ;insert sign if negative number
Else
mov al ' ' ;insert space if positive number
End_If
stosb
...If D@Flag = REGULAR
; Verify number of decimals required vs maximum allowed:
mov eax 15 | sub eax D@eSize
cmp eax D@Decimal | jae L0>
mov D@Decimal eax
; ;check for integer digits:
L0: mov ecx D@eSize
or ecx ecx ;is it negative
jns L3>
; Insert required leading 0 before decimal digits:
mov ax '0.' | stosw
neg ecx
cmp ecx D@Decimal | jbe L0>
jmp L8>>
L0: dec ecx | jz L0>
stosb | jmp L0<
L0:
mov ecx D@Decimal | inc ecx
add ecx D@eSize | jg L4>
jmp L8>>
; Do integer digits:
L3: inc ecx
L0: movzx eax B$esi | dec esi | ror ax 4 | ror ah 4
add ax '00' | stosw | sub ecx 2 | jg L0<
jz L0>
dec edi
L0: cmp D@Decimal 0 | jz L8>>
mov al '.' | stosb
If ecx <> 0
mov al ah | stosb
mov ecx D@Decimal | dec ecx | jz L8>>
Else
mov ecx D@Decimal
End_If
; Do decimal digits:
L4: movzx eax B$esi
dec esi
ror ax 4 | ror ah 4 | add ax 03030 | stosw
sub ecx 2 | jg L4<
jz L1>
dec edi
L1: jmp L8>>
; scientific notation
...Else
mov ecx D@Decimal | inc ecx
movzx eax B$esi | dec esi
ror ax 4 | ror ah 4 | add ax '00' | stosb
mov al '.' | stosb
mov al ah | stosb
sub ecx 2 | jz L7>
jns L0>
dec edi | jmp L7>
L0: movzx eax B$esi
dec esi
ror ax 4 | ror ah 4
add ax '00' | stosw | sub ecx 2 | jg L0<
jz L7>
dec edi
L7: mov al 'E' | stosb
mov al '+', ecx D@eSize | or ecx ecx | jns L0>
mov al '-' | neg ecx
L0: stosb
; Note: the absolute value of the size could not exceed 4931
mov eax ecx
mov cl 100
div cl ;->thousands & hundreds in AL, tens & units in AH
push eax
and eax 0FF ;keep only the thousands & hundreds
mov cl 10
div cl ;->thousands in AL, hundreds in AH
add ax '00' ;convert to characters
stosw ;insert them
pop eax
shr eax 8 ;get the tens & units in AL
div cl ;tens in AL, units in AH
add ax '00' ;convert to characters
stosw ;insert them
...End_If
L8: mov B$edi 0 ;string terminating 0
pop edi, esi, ecx
finit | mov eax &TRUE
EndP |
Partager