-
Notifications
You must be signed in to change notification settings - Fork 7
/
float.d
396 lines (323 loc) · 14 KB
/
float.d
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
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
//#################################################################
//
// 32 bit IEEE 754 floats (kurz: Gleitkommazahlen) für Daedalus:
// Ursprüngliches Skript vom 26.11.2008:
// http://forum.worldofplayers.de/forum/threads/500080-Instrumentarium-Floats
//
// Diese Version behält das ursprüngliche Interface bei,
// nutzt aber tatsächliche Floating-Point Instruktionen.
// Diese Version ist daher schneller und genauer.
//
// Benötigt wird Ikarus.
//
// Fertigstellung am 6.4.2012:
// - Umstellung auf Ikarus
// - Ergänzung von castFromIntf und castToIntf
//
//#################################################################
/*####################################
// Kurzdokumentation
//####################################
//*********************
// Grundsätzliches
//*********************
Die Funktionen in diesem Skript sind in der Lage Floating Point Operationen auszuführen. Als Behältnis für die Gleitkommadaten dient hierbei ein gewöhnlicher 32-bit Integer.
Hintergrund ein syntaktische Unterscheidung zwischen Integern und Floats nicht mehr vorzunehmen ist, dass syntaktische Floats wesentlichen Einschränkung seitens des Gothic Parsers unterworfen sind.
Es ist daher von Nutzerseite darauf zu achten, Floats und integer trotz des formal gleichen Datentyps penibel zu unterscheiden. Wenn eine Zahl in Floatdarstellung mit einer Zahl in Integerdarstellung verrechnet wird, oder Funktionen auf integer angewendet werden, die eigentlich für floats gedacht sind oder umgekehrt kommt Blödsinn heraus.
//************************************
// Das Instrumentarium
//************************************
Um mit floats rechnen zu können, müssen diese zunächst erzeugt werden. Dazu gibt es die Funktionen mkf und fracf, die eine Integer Ganzzahl bzw. einen Bruch mit ganzzahligem Zähler und Nenner in einen float umwandeln.
Floats können untereinander addiert, subtrahiert, multipliziert und dividiert werden. Eine Wurzelfunktion ist ebenfalls definiert.
Um den Wert der floats intepretieren zu können, sind die Ordnungsrelationen >=, >, <, <= mit den Funktionen ge, g, l, le gegeben.
Ferner können Floats mit der truncf und roundf Funktion zurück in einen Integer konvertiert werden.
//************************************
// Die Funktionen im Überblick:
//************************************
"func float" und "var float" wie hier angegeben gibt es nicht wirklich (für Gothic sind alles integer). Diese Notation soll hier nur zur Veranschaulichung dienen.
Um sich die Funktionsnamen merken zu können hilft vielleicht folgende Liste, die die Abkürzungen erklärt:
mkf = make float
truncf = truncate float
roundf = round float
addf = add floats
subf = subtract floats
negf = negate float
mulf = multiply floats
divf = divide floats
invf = inverse float
gf = greater
gef = greater or equal
lf = lower
lef = lower or equal
sqrf = squre float
sqrtf = square root of float
printf = print float
**************** Umwandlung *******************
func float mkf (var int x) //Erzeugt die Floatdarstellung aus einer Ganzzahl.
func int truncf (var float x) //reduziert einen float auf seinen Ganzzahlanteil, wobei alle Nachkommastellen verworfen werden. Der Ergebnistyp ist Integer. (-1.5 würde zu -1.0, nicht zu -2)
func int roundf (var float x) //reduziert einen float auf die nächstgelegende Ganzzahl. Der Ergebnistyp ist Integer.
**************** Addition *********************
func float addf (var float x, var float y) //addiert x und y und gibt das Ergebnis zurück.
func float subf (var float x, var float y) //subtrahiert y von x und gibt das Ergebnis zurück.
func float negf (var float x) //Gibt das additive Inverse von x (also -x) zurück.
**************** Multiplikation ***************
func float mulf (var float x, var float y) //multipliziert x und y miteinander und gibt das Ergebnis zurück.
func float divf (var float x, var float y) //dividiert x durch y und gibt das Ergebnis zurück.
func float invf (var float x) //Gibt das multiplikative Inverse des floats x, also 1/x zurück.
func float fracf (var int p, var int p) //Gibt den Bruch p/q als float zurück. Äquivalent zu divf(mkf(p), mkf(q))
**************** Ordnungsrelationen ***********
func int gf (var float x, var float y) //gibt für x > y TRUE zurück, sonst FALSE
func int gef (var float x, var float y) //gibt für x >= y TRUE zurück, sonst FALSE
func int lef (var float x, var float y) //gibt für x <= y TRUE zurück, sonst FALSE
func int lf (var float x, var float y) //gibt für x < y TRUE zurück, sonst FALSE
**************** Verschiedene *****************
func float sqrf (var float x) //gibt das Quadrat des floats x zurück.
func float sqrtf (var float x) //gibt die Wurzel des floats x zurück.
func string toStringf(var float x) //Erzeugt eine Stringdarstellung von x, z.B. "12.345"
**************** Casts ************************
Daedalus kennt seine eigenen, sehr eingeschränkten "var float".
Um floats im Sinne dieses Skripts in Floats im Sinne von Daedalus umzuwandeln und umgekehrt gibt es folgende zwei Funktionen.
func float castFromIntf(var int f) //Wandle Float im Sinne dieses Pakets in Daedalus Float um.
func int castToIntf (var float f) //Wandle Daedalus Float in einen Float im Sinne dieses Pakets um.
Beide Funktionen machen letzendlich überhaupt nichts. Sie "schummeln" lediglich eine Änderung des Datentyps am Parser vorbei.
Parameter und Rückgabewert von beiden Funktionen sind in Wirklichkeit in beiden Fällen vom Typ float.
//************************************
// Sonstiges
//************************************
Es sind fünf float Konstanten definiert, die genutzt werden können, ohne dass sie erst berechnet/erzeugt werden müssen:
FLOATNULL = 0.0
FLOATEINS = 1.0
FLOATHALB = 0.5
PI = 3.1415...
E = 2.7182...
//************************************
// Beispiele
//************************************
Folgende Funktion soll das Volumen eines Zylinders in cm³ berechnen und gerundet zurückgeben.
Hierbei sind die Parameter als Ganzzahlen gegeben (Einheit cm).
func int Zylindervolumen (var int radius, var int hoehe) {
var int radiusf, var int hoehef;
radiusf = mkf (radius);
hoehef = mkf (hoehe);
//Volumen = r² * PI * h
var int ergebnisf;
ergebnisf = mulf (radiusf, radiusf);
ergebnisf = mulf (ergebnisf, PI);
ergebnisf = mulf (ergebnisf, h);
return roundf (ergebnisf);
};
Folgende Funktion berechnet eine Zahl und gibt sie auf dem Bildschirm aus. Sie schachtelt dabei manche Funktionen
func void antwort () {
var int foo;
foo = mulf (mkf (1337), PI);
printf (divf (foo, mkf (100)));
//(1337*PI)/100 ist verblüffend genau 42. ;-)
};
Folgende Funktion macht ein paar Vergleiche. Es wird in keinem Fall "FEHLER" ausgegeben.
func void floattest()
{
var int halb; var int eins; var int zwei;
var int null;
var int minuseins; var int minuszwei;
halb = invf (mkf (2));
eins = mkf (1);
zwei = mkf (2);
null = mkf (0);
minuseins = mkf (-1);
minuszwei = mkf (-2);
if (gf (zwei,eins)) {} else { print ("FEHLER!"); };
if (gf (eins,null)) {} else { print ("FEHLER!"); };
if (lf (minuseins,null)) {} else { print ("FEHLER!"); };
if (lf (minuszwei,minuseins)) {} else { print ("FEHLER!"); };
if (gf (halb,minuseins)) {} else { print ("FEHLER!"); };
if (lf (halb,zwei)) {} else { print ("FEHLER!"); };
if (lef (null,null)) {} else { print ("FEHLER!"); };
if (gef (null,null)) {} else { print ("FEHLER!"); };
};
//************************************
// Beschränkungen und Fallen
//************************************
*********** Nutzerfehler ************
Es sollten unter keinen Umständen die Operatoren +, -, * oder / auf floats angewendet werden. Sie haben dort keine sinnvolle Anwendung. Bestensfalls kommt einfach nur Blödsinn heraus. Wie oben beschrieben sind addf, subf, mulf und divf anzuwenden.
Wer versteht wie ein float intern aufgebaut ist, kann zum Beispiel innerhalb der positiven Zahlen die Ordnungsrealtionen (>, <, <= >=) benutzen. Wer sich nicht sicher ist, sollte auf die gegebenen Funktionen zurückgreifen.
Natürlich sind umgekehrt die Floatfunktionen für Integer unbrauchbar. Der Ausdruck sqrtf(4) ist nicht 2 sondern einfach nur falsch, da 4 ein Integer ist! Wer die Wurzel von 4 ziehen will und als Integer haben möchte kann das so tun: roundf(sqrtf(mkf(4))). Hier kommt 2 heraus.
*********** Float-Fehler *************
Die Genauigkeit der floats ist begrenzt, ab etwa 7 Dezimalstellen ist mit Rundungsfehlern zu rechnen. Dafür können sehr große und sehr kleine Zahlen dargestellt werden.
Es gelten die gewöhnlichen Einschränkungen und Empfehlungen für floats. Zum Beispiel ist es selten sinnvoll floats auf Gleichheit zu überprüfen, da es durch Rundungsfehler wahrscheinlich ist, dass auch zwei floats, die eigentlich gleich sein müssten kleine Abweichungen zueinander aufweisen. Es sollte in einem solchen Fall besser geprüft werden, ob die Differenz der beiden floats einen (im Verhältnis zur Problemstellung) kleinen Wert unterschreitet.
*/
//#################################################################
//
// DIE FUNKTIONEN
//
//#################################################################
const int FLOAT_SIGN_MASK = 1 << 31;
const int FLOATNULL = 0;
const int FLOATEINS = 1065353216; //vz 0, exp 0 (also char 127), mt 1.0
const int FLOATONE = FLOATEINS; //EN
const int FLOATHALB = 1056964608; //vz 0, exp -1 (also char 126), mt 1.0
const int FLOATHALF = FLOATHALB; //EN
const int PI = 1078530011;
const int E = 1076754516;
/* Die folgenden Arrays enthalten Assemblercode. */
const int FLTINT_trunc_code[9] = { -2082109099, 2111375596, 1166763774, 1712108798, -637778551, 1842939973, -128066564, -1946260007, -1010173883 };
const int FLTINT_mul_code [3] = { -639268523, 1306004549, 12830984 };
const int FLTINT_div_code [3] = { -639268523, 2111310917, 12830984 };
const int FLTINT_add_code [3] = { -639268523, 1171786821, 12830984 };
const int FLTINT_mk_code [2] = { -605714091, -1010235323 };
/* Helper Function: */
func int _FLT_Build_Func(var int codePtr, var int param1Ptr, var int param2Ptr, var int retValIsFloat, var int resultPtr) {
CALL_Open();
if (param2Ptr) {
CALL_IntParam(param2Ptr);
};
if (param1Ptr) {
CALL_IntParam(param1Ptr);
};
if (retValIsFloat) {
CALL_RetValIsFloat();
};
CALL_PutRetValTo(resultPtr);
CALL__cdecl(codePtr);
return CALL_Close();
};
//************************************
// Wrapper:
//************************************
func int mkf (var int x) {
const int code = 0;
if (!code) {
code = _FLT_Build_Func(_@(FLTINT_mk_code), _@(x), 0, 1, _@(result));
};
ASM_Run(code);
var int result;
return +result;
};
func int truncf (var int x) {
const int code = 0;
if (!code) {
code = _FLT_Build_Func(_@(FLTINT_trunc_code), _@(x), 0, 0, _@(result));
};
ASM_Run(code);
var int result;
return +result;
};
func int addf (var int x, var int y) {
const int code = 0;
if (!code) {
code = _FLT_Build_Func(_@(FLTINT_add_code), _@(x), _@(y), 1, _@(result));
};
ASM_Run(code);
var int result;
return +result;
};
func int mulf (var int x, var int y) {
const int code = 0;
if (!code) {
code = _FLT_Build_Func(_@(FLTINT_mul_code), _@(x), _@(y), 1, _@(result));
};
ASM_Run(code);
var int result;
return +result;
};
func int divf (var int x, var int y) {
const int code = 0;
if (!code) {
code = _FLT_Build_Func(_@(FLTINT_div_code), _@(x), _@(y), 1, _@(result));
};
ASM_Run(code);
var int result;
return +result;
};
//************************************
// Quadrieren und Wurzelziehen
//************************************
func int sqrf(var int f) {
return mulf(f,f);
};
func int sqrtf(var int f) {
const int sqrtf_G1 = 7757593; //0x765F19
const int sqrtf_G2 = 8123917; //0x7BF60D
const int code = 0;
if (!code) {
code = _FLT_Build_Func(MEMINT_SwitchG1G2(sqrtf_G1, sqrtf_G2),
_@(f), 0, 1, _@(result));
};
ASM_Run(code);
var int result;
return +result;
};
//************************************
// Es lassen sich vier kleine
// nützliche Hilfsfunktionen
// definieren:
//************************************
func int negf (var int x) {
if (x < 0) { return x & (~FLOAT_SIGN_MASK); }
else { return x | FLOAT_SIGN_MASK ; };
};
func int absf (var int x) {
return x & (~FLOAT_SIGN_MASK);
};
func int subf (var int x, var int y) {
return addf(x, negf(y));
};
func int roundf (var int x) {
if (x < 0) {
return truncf(subf(x, FLOATHALB));
} else {
return truncf(addf(x, FLOATHALB));
};
};
func int invf (var int x) {
return divf(FLOATEINS, x);
};
/* thanks to orcwarriorPL for the idea! */
func int fracf (var int p, var int q) {
if (!q) {
MEM_Error("fracf: Denominator is 0.");
};
return divf(mkf(p), mkf(q));
};
/* deprecated */
func int sqrtf_approx(var int f) { return sqrtf(f); };
//************************************
// Ordnungsrelationen
//************************************
func int gf (var int x, var int y) {
var int isnegX; isnegX = x & FLOAT_SIGN_MASK;
var int isnegY; isnegY = y & FLOAT_SIGN_MASK;
if (isNegX && isNegY) { //beide negativ
if (x < y) {
return TRUE;
};
}
else {
if (x > y) {
return TRUE;
};
};
return FALSE;
};
func int gef (var int x, var int y) {
if (x == y) { return true; };
return gf (x,y);
};
func int lef (var int x, var int y) {
return !gf(x,y);
};
func int lf (var int x, var int y) {
return !gef (x,y);
};
//*****************************************
// Ausgabefunktionen, danke an Gottfried!
//*****************************************
func float castFromIntf(var int f) { f; };
func int castToIntf (var float f) { return MEM_ReadInt(_@f(f)); };
func string toStringf(var int x) {
var float f; f = castFromIntf(x);
return FloatToString(f);
};
/* schlechter Name, aus Kompatibilitätsgründen trotzdem drin. */
func void printf(var int x) {
Print(toStringf(x));
};