Aptarimas:Matematika/Teiloro eilutė
Pridėti temąIšvaizda
Teiloro eilutės Benchmark'as
[keisti]- Toks Free Pascal (Compiler Version 2.6.0) kodas:
var a:longint; c:real; begin for a:=0 to 1000000000 do c:=c+a*(1+ sqr(a*1.0)*(-1/6+ sqr(a*1.0)*(1/120+ sqr(a*1.0)*(-1/5040+ sqr(a*1.0)*(1/362880+ sqr(a*1.0)*(-1/39916800+ sqr(a*1.0)*(1/6227020800+ sqr(a*1.0)*(-1/1307674368000+ sqr(a*1.0)*(1/355687428096000+ sqr(a*1.0)*(-1/121645100408832000+ sqr(a*1.0)*(1/51090942171709440000+ sqr(a*1.0)*(-1/25852016738884976640000+ sqr(a*1.0)*(1/15511210043330985984000000+ sqr(a*1.0)*(-1/10888869450418352160768000000+ sqr(a*1.0)/8841761993739701954543616000000)))))))))))))); writeln(c); Readln; End.
- duoda rezultatą po 52 sekundžių ant 2.6 GHz CPU.
Sinuso Teiloro eilutės teisingumo skaičiavimo lygis
[keisti]- Toks Free Pascal kodas:
var a:longint; c:real; begin //for a:=0 to 1000000000 do a:=6; c:=c+a*(1+ sqr(a*1.0)*(-1/6+ sqr(a*1.0)*(1/120+ sqr(a*1.0)*(-1/5040+ sqr(a*1.0)*(1/362880+ sqr(a*1.0)*(-1/39916800+ sqr(a*1.0)*(1/6227020800+ sqr(a*1.0)*(-1/1307674368000+ sqr(a*1.0)*(1/355687428096000+ sqr(a*1.0)*(-1/121645100408832000+ sqr(a*1.0)*(1/51090942171709440000+ sqr(a*1.0)*(-1/25852016738884976640000+ sqr(a*1.0)*(1/15511210043330985984000000+ sqr(a*1.0)*(-1/10888869450418352160768000000+ sqr(a*1.0)/8841761993739701954543616000000)))))))))))))); writeln(c); writeln(sin(6)); Readln; End.
- parodo 2 rezultatus (antras rezultatas absoliučiai teisingas):
- -0.279415498042951;
- -0.27941549819892587.
- Dousiu truputi paaiškinimo kodėl antras rezultatas užrašytas didesniu tikslumu. Intel (na ir AMD) procesoriaus x87 FPU (Floating point unit - x87 koprocecosius) skaičiuoja ne double precision (64 bits), o double extended precision (80 bits, 64 bits without mantissa). Tačiau į atminti lengviau krauti 64 bitus, nei 80 bitų, be to rezultatas po daug algoritmo žingsnių ar iteracijų vis tiek prarandamas (dėl divergencijos ar konvergencijos reiškinių), todėl yra numatytas standartas, kad visos programavimo kalbos palaikytų double precision (64 bits) ir single preicision (32 bits), bet nebūtinai double extended precision (80 bits). Greičiau, net reikalaujama, kad galutinis rezultatas būtų pateiktas arba double precision (64 bits) ir single preicision. Tačiau patys skaičiavimai vykstą su double extended precision (80 bits) ir paskui paverčiamas rezultatas sinuso į double precision (64 bits). Originaliai rezultatai pateikti taip:
- -2.79415498042951E-001;
- -2.7941549819892587E-0001.
- Double precision formatas turi bitus nuo 0 bito iki 51 bito, taigi, iš viso 52 bitus užrašyti skaičiui (nuo 52 iki 62 tenka mantisai ir 63 bitas minuso ženklui). Todėl gauname, kad Double precision yra Mes negalime naudoti 4 be 5, 6, 7, 8, 9. Todėl iš 16 gautų skaitmenų tinka tik 15, t. y. skaičius 999999999999999. Todėl pirmas rezultatas yra double precision ir turi 15 skaitmenų. Kadangi viskas skaičiuojama iš tikro su double extended precision (80 bits), tai suapvalinimui surasti 16 skaitmenį nėra problemų.
- Jeigu antras rezultatas (-2.7941549819892587E-0001) pateiktas su double extended precision, tada kyla klausimas kodėl yra tik 17 skaitmenų, jeigu žinoma, kad su double extended precision bitai nuo 0 iki 62 yra skaitmenims, 63 bitas yra sveikajam skaičiui ir bitai nuo 64 iki 78 yra mantisai bei 79 bitas skirtas minuso ženklui. Galimi 2 paaiškinimai tokie:
- 1) yra 19 skaitmenų, mums tinka tik 18 skaitmenų, vienas skaitmuo nerodomas, nes naudojamas apvalinimui, taigi, gaunasi 17 rodomų skaitmenų (apvalinimui reikia daugiau nei vieno bito, nes apvalinama iš dvejetainės sistemos į dešimtainę);
- 2) yra 19 skaitmenų, mums tinka tik 18 skaitmenų (nes reikia, kad būtų 19 devynetų, o ne 9223372036854775808), vienas skaitmuo nerodomas, nes naudojamas apvalinimui, taigi, gaunasi 17 rodomų skaitmenų.
Neteisingo apvalinimo po šaknies traukimo pavyzdis
[keisti]- Va štai toks Free Pascal (Version 1.0.12 2011/12/25; Comiler Version 2.6.0; Debugger GDB 7.2) kodas:
var a:longint; c:real; begin for a:=1 to 1000000000 do c:=c+6/(sqr(a*1.0)); writeln(c); writeln(sqrt(c)); readln; end.
- duoda 2 rezultatus:
- 9.86960433860995+000;
- 3.1415926436458864+0000.
- Šie rezultatai gaunami po 11 sekundžių su 2.6 GHz CPU ir DDR-800 (400 MHz) RAM (procesorius yra dual-core, bet vis tiek Free Pascal naudoja tik vieną core; procesorius turi 512 KB L2 per core, bet x87 FPU turi 8 80 bitų (double-extended precision) registrus, todėl galimas daiktas, kad L2 cashe, kaip ir L1 cashe net neegzistuoja; procesorius taip pat turi 8 16/32/64 bitų General Purpose registrus ir 16 128 bitų SSE registrų, taigi, smulkioms programoms ir net GPU (kurie turi iš tikro šiuo metu apie 8-32 core'ius, o ne 640-2048 shader-core'ius) blur'o effektams užtenka 8 arba 16 registrų (iš kvadrato 4*4 pikselių judama į dešinį šona trinant 4 pikseliu iš kairės ir vietoje jų įdėjus iš RAM 4 naujus pikselius iš dešinės ir kiekvieną kart sudedant 16 pixelių), net jei RAM veikia iki 8 ar 16 kartų lėčiau nei CPU).
- Tikroji reikšmė yra 3,1415926535897932384626433832795. O įrašius į Microsoft Windows kalkuliatorių reikšmę 9,86960433860995 ir ištraukius su Microsoft Windows kalkuliatoriumi šaknį gaunamas rezultas 3,1415926436458864995793203208207. Palyginus matome, kad suapvalinta blogai:
- 3.1415926436458864+0000;
- 3,14159264364588649957932.
- Gal čia Free Pascal (kuris greičiausiai naudojasi CPU koprocesoriaus x87 FPU šaknies traukimo instrukcija FSQRT, 151 puslapis arba 5.2.2; http://www.intel.com/content/www/us/en/processors/architectures-software-developer-manuals.html) apvalina nevisada teisingai tik traukiant šaknį, o ne, pavyzdžiui, dauginant.
- Toks Free Pascal (Version 1.0.12 2011/12/25; Comiler Version 2.6.0; Debugger GDB 7.2) kodas:
var a:longint; c:real; begin for a:=3 to 1000000000 do c:=c+6/(sqr(a*1.0-2)); writeln(c); writeln(sqrt(c)); readln; end.
- duoda rezultatus
- 9.86960433860995+000 ir
- 3.1415926436458864+0000 po 11 sekundžių su 2.6 GHz CPU ir DDR-800 (400 MHz) RAM. Atimties instrukcija neprailgina skaičiavimo laiko ir tai greičiausiai ne dėl savaiminio Free Pascal kodo optimizavimosi, o dėl procesoriaus architektūros ypatumų. Profesionaliai kalbant toks ypatumas būtų pavadintas CISC architekturos pranašumu palyginus su RISC architektūra, taučiau tiesa vis tiek turbūt yra tokia, kad visi procesoriai yra RISC tik, kadangi Intel ir AMD procesoriai yra populiariausi, tai daugiausiai visokių palengvinančių instrukcijų prikūrta, kaip MMX, SSE, AVX, kurios greičiausiai tik sutrumpina programos rašymą, bet nepagreitina niekada patį programos veikimo procesą. Teoriškai MMX, SSE arba AVX instrukcijos turėtų pagreitintį, pavyzdžiui, grafikoje vektorių t. y. geometrijos ir pikselių skaičiavimą 3-4 kartus, bet kadangi spalvų yra 3, o ne keturios ir trimatė erdvė yra iš 3 koordinačių, tai kai kurios užduotys iš tiesų galbūt gali būti padarytos truputi greičiau be visokių SSE, nes reikia 3 operacijų vietoje 4. Cisc architektūra teoriškai sukuria daug ilgų pipline'ų, kada per viena operaciją ir atimama ir sudauginama. Tačiau daug didesnė tikimybė, kad loop'inant kažkaip savaime atėmimo operacija gaunasi nemokamai ir nėra ten jokių piplainų; visi CPU veikia tuo pačiu principu ir RISC ir CISC ir GPU. Taip pat labai galimas variantas, kad konstantos, kuri yra CPU registre, atėmimui reikia nuo 1 iki 4 ciklų todėl atėmimo operacija užima nuo 1/2.6=0.38 sekundės iki 4/2.6=1.54 sekundės. Apytiksliai vienai daugybos arba sudeties operacijai reikia 3,5 ciklo, tačiau sekunės skirtumas nejaučiamas, gal dėl to, kad sudėtis (ir atimtis) daroma truputi greičiau nei daugyba (be abejonės yra sudeties, daugybos ir dalybos pipline'ai 80 bitų (arba 64 bitų sveikiems skaičiams), o jų kombinacijų greičiasiai nėra).
Kaip [in software] pagreitinti sinuso Teiloro eilutę
[keisti]- Kad apskaičiuoti sinusą yra skitos Intel CPU x87 FPU sinuso instrukcijos (galima iš viso skaičiuoti sinusą arba kosinusą arba tangenta arba dar su specifinę instrukcija ir sinusą ir kosinusą vienu metu, kas greičiau nei atskirai, bet lėčiau nei kiekvieną atskirai). Šios x87 FPU (x87 FPU yra nuo Intel 8087, 287, 387; koprocesorius 487 nededamas kartu su CPU tik SX versijai, o toliau nuo intel 486 CPU visi procesoriai turi integruotus x87 FPU į tą patį čipą) koprocesoriaus instrukcijos cos, sin, tan, sincos yra in hardware, t. y., sinuso skaičiavimą Free Pascal kalba naudoja iš x87 FPU intrukcijų per asamblerį, o ne rašo asambleryje Teiloro eilutes. Bet galbūt sinusą in software galima apskaičiuoti greičiau nei in hardware, ypač jei skaičiai yra nuo 0 iki nes tada nereikia skaičiuoti frac() (arba ceil()) funkcijos, kuriai reikia apie 65 ciklų, tuo tarpu, kai sinusui in hardware reikia 122 ciklų.
- Kad pagreitinti sinuso skaičiavimą reikia skaičiuoti ne nuo 0 iki o nuo iki (šitas triukas netinka kosinusui, nes kosinusas kitaip simetriškas koordinačių ašims nei sinusas). Taigi, normalai bet kokį didelį skaičių a nuo iki mes paverstume į skaičių 0 iki taip:
- Skaičių b mes jau galime dėti į Teiloro eilutę ir gausime tą patį rezultatą, kokį gautume įdėjus į begalo ilgą Teiloro eilutę skaičių a. Skaičiuojant su skaičiu b vietoje a Teiloro eilutė labai sutrumpėja, kad gauti tą patį tikslumą.
- Greitesnis būdas apskaičiuoti yra toks:
- Įdėjus į sinuso Teiloro eilutę atsakymo tikslumas turėtų padidėti dvigubai palyginus su tuo, ką gautume įdėjus b (tarkim vietoje 5 teisingų skaimenų po kablelio, gautume 10 teisingų skaimenų po kablelio).
- Toks Free Pascal kodas:
var a:longint; b,c:real; begin //for a:=0 to 1000000000 do a:=6; b:=3.141592653589793238-6.283185307179586477*frac(a/6.283185307179586477); c:=c+b*(1+ sqr(b*1.0)*(-1/6+ sqr(b*1.0)*(1/120+ sqr(b*1.0)*(-1/5040+ sqr(b*1.0)*(1/362880+ sqr(b*1.0)*(-1/39916800+ sqr(b*1.0)*(1/6227020800+ sqr(b*1.0)*(-1/1307674368000+ sqr(b*1.0)*(1/355687428096000+ sqr(b*1.0)*(-1/121645100408832000+ sqr(b*1.0)*(1/51090942171709440000+ sqr(b*1.0)*(-1/25852016738884976640000+ sqr(b*1.0)*(1/15511210043330985984000000+ sqr(b*1.0)*(-1/10888869450418352160768000000+ sqr(b*1.0)/8841761993739701954543616000000)))))))))))))); writeln(c); writeln(sin(6)); Readln; End.
- duoda rezultatus:
- -2.79415498198926E-001;
- -2.7941549819892587E-0001.
- Tikslumas išaugo dvigubai (o gal ir dar daugiau?), kaip ir buvo tikėtasi.
- Update 1 [2024 04 05]. Su naujasne Free Pascal versija (Free Pascal IDE Version 1.0.1 [2020/06/04]; Compiler Version 3.2.0) paskutinis kodas duoda tokius rezultatus:
- -2.7941549819892575E-001
- -2.79415498198925872810E-0001
- Tiksli sin(6) reikšmė iš kalkuliatoriaus yra tokia (čia yra 6 radianai):
- -0.27941549819892587281155544661189.
- Kiek suprantu, kodo pirmas 17 skaitmenų rezultatas yra Double precision (64 bit), o antras 21 skaitmens rezultatas yra Double Extended precision (80 bit). Double precision (64 bit) garantuoja 15 pirmų teisingų skaitmenų iš 17 (likę du skaitmenys kartais gali būti teisingi dėl konvertavimo iš dvejetainės į dešimtainę sistemą). O Double Extended precision (80 bit) berods garantuoja 18 pirmų teisingų skaitmenų. Matyt, kad kai kuriais atvejais gali būti teisingas ir 21 skaitmuo Double Extended precision (80 bit) formate.
- Toks Free Pascal kodas (kuris skiriasi tik tuo, kad vietoje 6 yra 38):
var a:longint; b,c:real; begin a:=38; b:=3.141592653589793238-6.283185307179586477*frac(a/6.283185307179586477); c:=c+b*(1+ sqr(b*1.0)*(-1/6+ sqr(b*1.0)*(1/120+ sqr(b*1.0)*(-1/5040+ sqr(b*1.0)*(1/362880+ sqr(b*1.0)*(-1/39916800+ sqr(b*1.0)*(1/6227020800+ sqr(b*1.0)*(-1/1307674368000+ sqr(b*1.0)*(1/355687428096000+ sqr(b*1.0)*(-1/121645100408832000+ sqr(b*1.0)*(1/51090942171709440000+ sqr(b*1.0)*(-1/25852016738884976640000+ sqr(b*1.0)*(1/15511210043330985984000000+ sqr(b*1.0)*(-1/10888869450418352160768000000+ sqr(b*1.0)/8841761993739701954543616000000)))))))))))))); writeln(c); writeln(sin(38)); Readln; End.
- duoda rezultatus:
- 2.9636857870938516E-001
- 2.96368578709385317445E-0001
- Vėl gavome 17 skaitemnų ir 21 skaitmens rezultatus. Kalkuliatoriaus sin(38) reikšmė yra tokia (38 radianai):
- 0.29636857870938531739229664984902.
- Pirmas rezultatas duoda pirmus 15 teisingų skaitmenų, o antras rezultatas duoda 18 pirmų teisingų skaitmenų.
- Kode įrašius 136 vietoje 38, gaunami tokie rezultatai:
- -7.9043320672288875E-001
- -7.90433206722888758095E-0001
- Kalkuliatoriaus sin(136) reikšmė yra tokia (136 radianai):
- -0.79043320672288875799022474878835.
- Pirmas rezultatas duoda visus 17 teisingus skaitmenis. O antras rezultatas duoda 17 pirmų teisingų skaitmenų, bet galima sakyti 18 pirmų teisingų skaitmenų, nes ...22888758 beveik tas pats kas ...2288875799.
- Taigi, konvertuojant iš dvejetainės į dešimtainę sistemą gaunami dešimtainėje sistemoje paskutiniai pora/keli skaitmenys kartais teisingi, o kartais neteisingi. Double precision (64 bit) garantuoja 15 teisingų pirmų skaitmenų, o Double Extended precision (80 bit) garantuoja, ko gero, 18 pirmų teisingų skaitmenų.
- Kode funkcija frac() duoda skaičių be sveikos dalies, pavyzdžiui, frac(56423.465684681)=0.465684681.
- Kode pirmą eilutę galima pakeisti tokia:
var a,b,c:real;
- tada skaičius a galės būti ne tik sveikas skaičius (galės būti bet koks realusis skaičius).
- Čia pasinaudota redukcijos formule (Redukcijos formulės)
- Va čia ( https://en.wikipedia.org/wiki/Extended_precision#Working_range ) iš angliškos Vikipedijos apie Double Extended precision (80 bit):
- "The 80-bit floating-point format has a range (including subnormals) from approximately 3.65×10−4951 to 1.18×104932. Although log10(264) ≅ 19.266, this format is usually described as giving approximately eighteen significant digits of precision (the floor of log10(263), the minimum guaranteed precision)."
- Be to,
- =log10(9,223,372,036,854,775,808) = 18.964889726830815298465550367643,
- t. y. beveik 19 skaitmenų dauguma atveju, bet kartais garantuoti (teisingi) buna tik 18 pirmų skaitmenų. Matyt, taip.
- Kodėl yra 21 skaitmuo Double Extended precision (80 bit) formate, galima paaiškinti taip (analogiškai, kaip tai aiškinama Double precision (64 bit) Vikipedijos straipsnyje: https://en.wikipedia.org/wiki/Double-precision_floating-point_format#IEEE_754_double-precision_binary_floating-point_format:_binary64 ):
- 5.4210108624275221700372640043497e-20.
- Šis skaitmuo užims 21 skaitmenį ir atrodys maždaug taip 0.00000000000000000005, nes pavyzdžiui t. y. užima 4 skaitmenis. Todėl ir yra 21 skaitmuo ir kai kuriais atvejais visi 21 skaitmenys gali būti teisingi, konvertuojant iš dvejetainės į dešimtainę sistemą.
- Beje, 1.0842021724855044340074528008699e-19. Tada reikia 20 skaitmenų tokiam skaičiui. Tiksliai negaliu pasakyti kodėl aukščiau skaičiuojama su 63 bitais, o ne 64 bitais (), nes Vikipedijos Extended precision (80 bit) straipsnyje ( https://en.wikipedia.org/wiki/Extended_precision#x86_extended_precision_format ) paveikslėlyje rodoma, kad yra 63 bitai ir vienas bitas integer (sveikasis skaičius), kuris, kaip manau, stovi prieš kablelį pats pirmas (tai gaunas kaip ir 1+63=64 bitai).
- Bet panašu (iš to paties straipsnio, lentelės ir paaiškinimo po lentele), kad bitai 0-62 veikia su visais koprocesoriais įskaitant 8087 ir 80287 ir vėlesnius kaip 80387, o 63-čias bitas veikia tik su 8087 ir 80287, jei jo reikšmė yra 0. Kai 63-čio bito reikšmė yra 1, tai koprocesoriaus skaičiavimai veikia normaliai su visais koprocesoriais nuo 8087 iki bet kokio velesnio, kaip 80387. Iš 63-čio bito naudos gali išgauti tik 8087 ir 80287 koprocesoriai. If bit 63 is zero then "Unnormal. Only generated on the 8087 and 80287. The 80387 and later treat this as an invalid operand.". If bit 63 is one then "Normalized value.".
- Iš čia ( https://en.wikipedia.org/wiki/Extended_precision#Working_range ):
- "Bounds on conversion between decimal and binary for the 80-bit format can be given as follows: if a decimal string with at most 18 significant digits is correctly rounded to an 80-bit IEEE 754 binary floating-point value (as on input) then converted back to the same number of significant decimal digits (as for output), then the final string will exactly match the original; while, conversely, if an 80-bit IEEE 754 binary floating-point value is correctly converted and (nearest) rounded to a decimal string with at least 21 significant decimal digits then converted back to binary format it will exactly match the original."
- Ten rašo, kad jeigu iš 18 dešimtainių skaitmenų paversti į 80 bitų (IEEE 754) dvejetainės sistemos skaičių, o paskui tą dvejetainį skaičių paversti vėl į dešimtainį skaičių, tai gausime tą patį dešimtainį skaičių su 18 tokių pačių skaitmenų. O Jeigu 80 bitų (IEEE 754 - 80-bit floating-point format) [Double Extended precision] formato dvejetainės sistemos skaičių paversti į 21 skaitmens dešimtainį skaičių ir paskui šitą 21 skaitmens dešimtainės sistemos skaičių paversti vėl į dvejetainį skaičių (80-bit floating-point formate), tai tas dvejetainis skaičius bus lygiai toks pats koks buvo pradžioje. Tai gal dėl to ir yra 21 [dešimtainio] skaitmens atsakymas, kai FPU skaičiuoja sin(x) iš FPU instrukcijų.
Skaičiaus e skaičiavimas ir ne tik
[keisti]- Toks Free Pascal kodas (skaičiuojantis e):
var a:longint; c:real; begin c:=1; for a:=1 to 1000000000 do c:=c*1.000000001; writeln(c); Readln; End.
- duoda atsakymą "2.7182818271394744E+000" po 6 sekundžių su 4.16 GHz veikiančių procesorium (pirmą kartą duoda šitą atsakymą po maždaug 85 sekundžių). Čia yra milijardas iteracijų ir milijardas daugybos operacijų. Todėl gaunasi, kad vienai daugybos operacijai reikia 6*4.16=24.96=~25 ciklų. Bet negali būti tiek daug ciklų vienai daugybos operacijai, todėl tikriausiai daug ciklų suvalgo pati iteracijos operacija.
- Kalkuliatoriaus reikšmė yra tokia:
- 2.7182818270999043223766440238603. Gavome su FP kodu 10 (beveik 11) pirmų teisingų skaitmenų. Vadinasi, po daug dauginimų skaičiavimo tikslumas mažėja.
- Tiksli skaičiaus e reikšmė yra tokia: e=2.7182818284590452353602874713527.
- [Update 1 (2025 02 05). Patikrinau šitą kodą su paleistu youtube interneto naršyklėje ir kai youtube paspaustas ant pauzės. Iš antro karto paleidus, duoda rezultatą "2.7182818271394744E+000" po 5.5-5.8 sekundžių (laikrodį laikiau prie monitoriaus, kad iškart matytus laikas), kai youtube yra ant pauzės. O kai youtube groja 1440p60 video (1080p monitoriuje), tai Free Pascal skaičiavimo laikas iš antro karto yra apie 7 sekundės (gal net truputi daugiau, kaip 7.3 s). Todėl gali būti, kad procesorius daro iteracijas sunaudodamas tam nemažai ciklų (apie 20 ciklų), o x87 FPU daugina skaičius pagal tai, kaip tai jam pasako daryt CPU. Kol FPU daro vieną iteraciją, FPU spėja sudauginti du skaičius ir pasiruošt kitai iteracijai. Manau, CPU procesorius nusiunčia FPU koprocesoriui tik du 64 bitų skaičius su kuriais reikia atlikti tam tikrus veiksmus. Arba nusiunčia tik vieną skaičių, jei kitas skaičius jau yra FPU koprocesoriaus registre. Kol FPU atlieka veiksmus su tais dviais skaičiais, CPU procesorius atlieka iteracijos skaičiavimus (palygina ar a<1000000000 ir gal ką nors dar). Todėl jei daugyba ir sudėtis su FPU coprocesorium užima apie 1-3 ciklus, tai kol CPU procesorius daro iteraciją, FPU koprocesorius gali atlikti dalybos operaciją. Dėl to ir galėtų būti labai panašus laikas (~6 sekundės) dauginimo iteracijų, ir kaip pateiktas kodas žemiau, dalybos iteracijų (nes dalyba pagal Niutono metodą yra kombinacija sudeties ir daugybos operacijų, ką FPU jau moka daryt be CPU, naudodamas savo ~3 iteracijas). Be to, žiūrėjau kaip skaičiuoja Free Pascal iš pirmo karto, priklausomai nuo to ar youtube rodo ką nors ar ne (kai paleistas youtube, Free Pascal kampe veikia/skaičiuoja, todėl matosi ir youtube vaizdas ir Free Pascal atsakymas, kai jis yra gaunamas). Tai prisimenu, kad kai youtube groja, atsakymas gaunamas po ilgesnio laiko (maždaug nuo 1.2 karto iki 2-3 kartų, priklausomai nuo kodo, gal kaip sinuso skaičiavimas su milijardu iteracijų (gal iki 3 kartų ilgiau, kai paleistas videožaidimas (Crysis (demo)) lange, o ne youtube video, neatsimenu...); vidutiniškai apie 1.5 karto ilgiau skaičiuoja ivairius tokius kodus su milijardu iteracijų (gerai neatsimenu)). Tai išeina arba CPU procesorius perpompuoja iš intereneto youtube videoduomenis kažkokiam Video-varikliukui/kodekui, arba CPU procesorius pats ir dekoduoja tą video, taip užimdamas 7.3/5.5 = 1.3272 =~1.3 karto daugiau laiko, kas reiškia, kad ketvirtadalis skaičiavimu nueina Video rodymui ir 3/4 CPU procesoriaus skaičiavimų skirta iteracijų darymui FPU koprocesoriaus skaičiavimams. 1080p video turi apie 2 milijonus pikselių, o 1440p video turi 3,686,400 pikselių (apie 3.7 milijonus). Su 60 fps, 3.7*60 = 222 MHz, tai su 4.16 GHz procesorium ketvirtadalis yra apie 1 GHz, tai išeitų, kad su 4 ciklais (222*4 =~1000 MHz) užtenka CPU procesoriui visą video parodyt lange apie 800p rezoliucijoje su 60 kadrų per sekundę. Tas youtube video iš kai kurių nejudančių kvadratų sudarytas (kaip ir visi sukoduoti video), nes yra suspaustas (dar rodomas lange, todėl kai kurie pikseliai praleidziami, bet tai galėtų būti dar sunkiau, ypač jei dar tie pixeliai interpoliuojami, kad gautusi mažiau laiptelių (kaip iš 4k rezoliucijos verčiant į 1080p rezoliuciją galima keturis pixelius sudėti į vieną pixelį, taip pagerinant video kokybę, nei vien tik praleidziant kas antrą pikselį ir kas antrą eilutę pixelių)). Tie nejudantys kvadratai - pixelių matricos nereikalauja naujesnių skaičiavimų naujam kadrui, todėl, procesorius galėtų susitvarkyt su šia užduotim, o jei vaizdas visas stipriai judantis, tai judantys objektai, vėlgi, pavirsta į didesnius kvadratus (kai video buvo koduojamas į youtube...) ir nereikia ten nieko stipriai dekoduot, kad rodyt video, tik didesnius kvadratus užpildyt ta pačia spalva ar nusiusti GPU, kad tas parodytų tuos kvadratus, kurie nejuda su likusiais judančiais pixeliais mažuose ploteliuose.
- Čia https://en.wikipedia.org/wiki/Framebuffer#Page_flipping aprašytas mechanizmas GPU kadro išvedimo į monitorių. Šio mechanizmo principas vadinasi Double buffering. Iš pradžių CPU procesorius nusiunčia GPU procesoriui visą kadrą, kaip, pavyzdžiui, nurenderintą Desktop'ą (arba pats GPU nusiunčia kitai GPU daliai, kuri atsakinga už išvedimą kadro į monitorių). Tiksliau tas kadras nusiunčiamas į GPU procesoriaus RAM1. Tada kol GPU išvedinėja tą kadrą iš GPU RAM1, centrinis procesorius (arba GPU kuris renderina grafiką) renderina kitą kadrą ir nurenderinęs nusiunčia į GPU's RAM2. Tada GPU procesoriaus dalis atsakinga už kadro išvedimą į monitorių, išvedus į monitorių kadrą iš RAM1, persijungia į RAM2 adresavimą ir siunčia į monitorių kadrą iš RAM2. O CPU procesorius (ar GPU kažkokia grafikos renderinimo dalis) tuo metu vėl deda pikselius-kadrą į RAM1. Ir taip vyksta persijungimai tarp RAM1 ir RAM2, CPU ir GPU procesorių, kad sutaupyt laiko. Jei CPU procesorius naujo kadro nepateikė, tada GPU į monitorių išvedinėja tą patį kadrą 60 Hz dažniu. Bet be Double Freame Buffer greičiau galima atlikti perdavimą, jeigu reikia pakeisti tik kai kuriuos kadro pikselius RAM atmintyje. Bet tada tai negali būti GPU RAM atmintis, nes GPU turi visad išvedinėti pixelius-kadrą 60 Hz dažniu... Todėl tada reikia iš CPU atminties krauti į GPU atmintį (RAM). Bet jeigu niekas nesikeičia kaip ant Desktop'o, tai CPU procesoriui tuo metu daryti nieko nereikia (nereikia nieko persiuntinėt į GPU RAM) ir GPU visada išvedinėja tą patį kadrą 60 Hz dažnių. Bet jei CPU procesorius nusiunčia naujus pixelius į GPU procesoriaus RAM atmintį, tada GPU turi palaukt kažkiek ir gali pasidaryt jau 59 Hz... Bet protingiau būtų vis tiek naudot Double buffering, tiesiog, kai CPU procesorius neatsiunčia naujo kadro į, sakykim, RAM1, tada GPU procesorius tiesiog visad išvedinėja kadrą iš RAM2, 60 Hz dažniu. Arba tiesiog be Double Buffering pešasi CPU su GPU dėl GPU RAM atminties, dedami ir imdami iš jos datą asinchroniškai (kas pirmas prie GPU RAM prisijungia, tas ir įdeda pixelius / paima pixelius; tada kadrų dažnis gali truputi svyruoti...).]
- [Update 2 (2025 02 05 - Some time later). Va toks kodas:
var a:longint; c:real; begin c:=1; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; writeln(c); Readln; End.
- duoda rezultatą "2.7182818271394744E+000" po ~5.5 (maždaug po 6 sekundžių kaip ir anksčiau) sekundžių su 4.16 GHz veikiančių procesorium (pirmą kartą duoda šitą atsakymą po maždaug 87 sekundžių (su apkrautu Interneto Brauzeriais kompiuteriu, bet tai nieko nereiškia kaip ištyriau, nes joks video negroja ten...).
- O toks kodas:
:var a:longword; c:real; begin c:=1; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; for a:=1 to 200000000 do c:=c*1.000000001; writeln(c); Readln; End.
- duoda rezultatą "2.7182818271394744E+000" po ~5.5 (maždaug po 5 su puse sekundės) sekundės su 4.16 GHz veikiančių procesorium (pirmą kartą duoda šitą atsakymą po maždaug 87 sekundžių). Čia naudojamas žodis a:longword vietoj a:longint.
- Čia https://wiki.freepascal.org/Data_type aprašoma ką tie žodžiai reiškia. Žodis "longword" reiškia sveikuosius teigiamus skaičius (jų gali būti 2^32 = 4,294,967,296, nuo 0 iki 4294967295). Žodis "longint" reiškia sveikuosius skaičius, kurie gali būti ir neigiami (nuo -2147483648 iki 2147483647). 2^32 yra apytiksliai 4.3 milijardai. O aš buvau pagalvojęs, kad čia skaičiuojama iki 10 milijardų, nors žinojau, kad skaičiuojama iki vieno milijardo... Tai galvojau, kad 32 bitų neužtenka vienam a skaičiui, kai jis lyginamas su milijardu (for a:=1 to 1000000000 do) iteracijose... Tai galvojau, gal a skaičius yra 64 bitų ir dėl to tokios lėtos tos iteracijos, bet pasirodo, kad čia ~4 milijardai ir daugiau nereikia (užtenka longint skaičiui a). Dėl to dalinau tą kodą į 5 dalis, kad paskui a būtų 32 bitų su tinkamu prierašu, bet jis ir taip buvo 32 bitų (o ne 64 bitų) ir tas prierašas (longint arba longword) ir taip buvo ir yra tinkamas (~4 milijardai yra daugiau nei 1 milijardas).]
- [Update 3 (2025 02 06). Toks FP kodas:
var a:longword; c:real; begin c:=1; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; writeln(c); Readln; End.
- duoda rezultatą "2.7182818291803450E+000" su 4.16 GHz procesorium. Iš pirmo karto paleidus, duoda šitą rezultatą po 2 minučių ir 17 sekundžių (137 sekundžių). Antrą ir daugiau kartų leidžiant šitą kodą, atsakymas gaunamas po 56 sekundžių. Čia yra 10 kartų daugiau iteracijų negu praeituose analogiškose koduose (iš viso 10 milijardų iteracijų, t. y. gauname ). Todėl ir atsakymas [iš antro karto] gaunamas po 10 kartų ilgesnio laiko (vietoj 5 su puse sekundės, gaunamas atsakymas po 56 sekundžių).
- Iš pradžių nepridėjau po nulį (po kablelio) skaičiuje 1.000000001 (Free Pascal kode), tai tada gavau atsakymą "2.2026465687886852E+004" (tai reiškia ), kuris yra neteisingas, nes e ne taip skaičiuojamas (tada pirmą kartą atsakymas irgi buvo gautas po maždaug 2 minučių ir 18 sekundžių, o antrą kartą irgi po maždaug 56 sekundžių).
- Tai gaunas apie 80 sekundžių procesorius kažkokį mėšlą daro pirmą kartą, kažką kompiliuodamas. Anksčiau prieš porą metų tai darė gerokai greičiau. Gal į Windows 10 įdėtas dešiniam apatiniam kampe orų ir naujienų žinių pateikimas stabdo kompiliavimo greitį (nemokamoj Windows 10 versijoje negalima jų atjungt, o anksčiau jų nebuvo), arba Dievas arba Microsoft specialiai palėtino Free Pascal skaičiavimus (kompiliavimą) pirmam kartui...
- Pateiktas palyginimas e skaičiavimo su 1 milijardų iteracijų, su 10 milijardų iteracijų ir palygininama su su Winows 10 kalkuliatoriaus reikšmėm:
- 2.7182818271394744E+000,
- 2.7182818291803450E+000,
- 2.7182818270999043223766440238603 (),
- 2.7182818284590452353602874713527 (tiksli Windows 10 kalkuliatoriaus e reikšmė, esanti pačiam kalkuliatoriuje).]
- [Update 4 (2025 02 06 - The same day). Toks FP kodas:
var a:uint16; b:uint16; c:real; begin c:=1; for a:=1 to 60000 do begin for b:=1 to 60000 do //60000*60000= 3,600,000,000 c:=c*1.0000000001; end; writeln(c); Readln; End.
- duoda rezultatą "1.4333294146972801E+000" po 19 sekundžių iš antro ir velesnių kartų ir po maždaug minutės ir 40 sekundžių paleidus pirmą kartą (po 100 sekundžių). Čia naudojami du 16 bitų sveikieji teigiami skaičiai a ir b. Kodas yra šuolis į skaičiaus e skaičiavimą naudojant iteracijoms ne 32 bitų skaičių, bet 2 16 bitų skaičius (bet galima kaip ir anksčiau longint naudot vietoj uint16; su tokiais skaičiavimais galima skaičiuot/patikrint dvilypius integralus). Čia iš viso yra 60000*60000 = 3,600,000,000 iteracijų. Tai 3.6 karto daugiau nei kai analogiškas kodas skaičiuojamas iki 1 milijardo iteracijų. Todėl 3.6 * 5.5 (s) = 20.16 (sekundžių), kas panašiai kaip 19 (sekundžių).
- Toks Free Pascal kodas:
var a:uint16; b:uint16; c:real; begin c:=1; for a:=1 to 60000 do begin for b:=1 to 60000 do //60000*60000= 3,600,000,000 c:=c*1.0000000001; end; for a:=1 to 20000 do begin for b:=1 to 20000 do //20000*20000= 400,000,000 c:=c*1.0000000001; //400,000,000 + 3,600,000,000 = 4,000,000,000 end; for a:=1 to 60000 do begin for b:=1 to 60000 do //60000*60000= 3,600,000,000 c:=c*1.0000000001; end; for a:=1 to 20000 do begin for b:=1 to 20000 do //20000*20000= 400,000,000 c:=c*1.0000000001; //400,000,000 + 3,600,000,000 = 4,000,000,000 end; writeln(c); //4,000,000,000 + 4,000,000,000 = 8,000,000,000 Readln; End.
- duoda rezultatą "2.2255409289648935E+000" po 42-43 sekundžių po antro karto, po trečio, po ketvirto ir velesnių kartų (su 4.16 GHz CPU). O pirmą kartą paleidus duoda šitą rezultatą po apytiksliai 2 minučių ir 5 sekundžių (125 sekundžių). Su vienu milijardu iteracijų atsakymas buvo gautas po 5.5 sekundės, o čia yra 8 milijardai iteracijų. Todėl 5.5*8 = 44 sekundės (panašiai kaip 43 sekundės dabar). UInt16 - Range: (0 .. 65535) - tai 16 bitu teigimas sveikas skaičius, galintis būti nuo 0 iki 65535 (2^16 = 65536).
- Toks Free Pascal kodas (skaičiuojantis e):
var a:uint16; b:uint16; c:real; begin c:=1; for a:=1 to 60000 do begin for b:=1 to 60000 do //60000*60000= 3,600,000,000 c:=c*1.0000000001; end; for a:=1 to 20000 do begin for b:=1 to 20000 do //20000*20000= 400,000,000 c:=c*1.0000000001; //400,000,000 + 3,600,000,000 = 4,000,000,000 end; for a:=1 to 60000 do begin for b:=1 to 60000 do //60000*60000= 3,600,000,000 c:=c*1.0000000001; end; for a:=1 to 20000 do begin for b:=1 to 20000 do //20000*20000= 400,000,000 c:=c*1.0000000001; //400,000,000 + 3,600,000,000 = 4,000,000,000 end; for a:=1 to 50000 do begin for b:=1 to 40000 do //50000*40000= 2,000,000,000 c:=c*1.0000000001; end; writeln(c); //4,000,000,000 + 4,000,000,000 + 2,000,000,000 = 10,000,000,000 Readln; End.
- duoda rezultatą "2.7182818291803450E+000" po 55 sekundžių per antrą, trečią ir ketvirtą kartą su 4.16 GHz procesorium. O pirmą kartą duodą rezultatą po 2 minučių ir 20 sekundžių (140 sekundžių). Tai yra apytiksli skaičiaus e reikšmė. O skaičiavimo laikas yra 10 kartų ilgesnis nei su 1 milijardų iteracijų (5.5 sekundės kart 10 = 55 sekundės), nes čia yra iš viso 10 milijardų iteracijų. Iteracijoms naudojami vien tik 16 bitų sveiki skaičiai (with loop in loop - total 10 loops or 5 loops with one loop inside).]
- [Update 5 (2025 02 07). Toks FP kodas (skaičiuojantis e):
var a:uint16; b:longint; d:longint; c:real; begin c:=1; // for a:=1 to 100 do //don't work with those two lines, whatever try like putting "end;" somewhere // begin //(can't be 3 nested loops with FP or I don't know how to do that) for b:=1 to 100000 do begin for d:=1 to 100000 do //100000*100000= 10,000,000,000 c:=c*1.0000000001; end; writeln(c); Readln; End.
- duoda rezultatą "2.7182818291803450E+000" po 54 sekundžių paleidus antrą ir trečią kartą ir po 2 minučių ir 15 sekundžių (135 sekundžių) paleidus pirmą kartą su 4.16 GHz CPU. Kode yra 10 milijardų iteracijų. Bandžiau padaryti su trimis loop 10 milijardų iteracijų naudojant 16 bitų sveikus skaičius uint16 (bandžiau ir 32 bitų sveikus skaičius (longint)), bet nesigavo.
- Papildymas. Pabandžiau šitą Free Pascal kodą paleisti, kai groja 1440p60 video (2560*1440 ir 60 kadrų/s). Bet video grojo ne ant viso ekrano, o lange, kurio rezoliucija yra apie 800p. Pačio monitoriaus rezoliucija yra 1920*1080 pixelių (1080p). Video https://www.youtube.com/watch?v=wWVnYUPIEOw grojo Browser'yje Google Chrome, o Free Pascal langas buvo pastumtas į dešine, kad beveik neužemė video lango ekrano. Gautas atsakymas buvo "2.7182818291803450E+000" po 2 minučių ir 55 sekundžių (175 sekundžių) per pirmą paleidimą su 4.16 GHz procesorium (video korta GeForce GTX 750 Ti) ir po minutės ir 12 sekundžių (72 sekundžių) per antrą, trečią ir paskesnius paleidimus (žinoma, irgi su 4.16 GHz dažniu veikiančiu CPU). 175/135 = 1.296296 karto lėčiau nei be grojančio video. 72/55 = 1.30909 karto lėčiau nei be grojančio video. Jeigu 1.3333 yra keturi ketvirtadaliai, tai 1 yra trys ketvirtadaliai, o 0.3333 yra vienas ketvirtadalis. Vadinasi, apie trečdaliu (arba 30 %) lėčiau veikia su grojančiu video FP kodas. Kad paversti ant kiek greičiau veikia FP kodo skaičiavimas be video, reikia vienetą padalinti iš 1.3 ir vienetą padalinti iš 1. Tada 1/1.3 = 0.76923 ir 1/1=1. Gaunasi, 1/0.76923 = 1.3000013. Vadinasi, taipogi, be grojančio video Free Pascal skaičiuoja šį kodą 1.3 karto greičiau (arba 30 % greičiau) negu, kai atlieka skaičiavimus su 1440p60 grojančiu youtube video.]
- [Update 5 (2025 02 07 - later).
- http://www.bitsavers.org/pdf/intel/ISIS_II/121725-001_8087_Support_Library_Reference_Nov83.pdf - Intel 8087 FPU dokumentacija.
- https://bitsavers.trailing-edge.com/components/intel/80286/210498-005_80286_and_80287_Programmers_Reference_Manual_1987.pdf - intel 286 CPU ir 287 FPU dokumentacija (80286 AND 80287 PROGRAMMER'S REFERENCE MANUAL).
- https://www.datasheetarchive.com/datasheet/387DX/Intel?id=eedb800508923ff5&term=387DX - Intel 387DX FPU dokumentacija.
- https://ohwc.narod.ru/man-dat/cpu/intel/387/387sx.pdf - Intel 387SX FPU dokumentacija.
- http://bitsavers.informatik.uni-stuttgart.de/components/intel/80386/231917-001_80387_Programmers_Reference_Manual_1987.pdf - intel 387 dokumentacija (387 FPU instrukcijų suvestinė prasideda 230 puslapyje Acrobat Reader'yje).
- https://0x04.net/~mwk/doc/intel/80187.pdf - intel 80187 FPU dokumentacija; aprašymas:
- Fully Compatible with 387DX and 387SX
- Math Coprocessors Implements all 387
- Architectural Enhancements over 8087
- Directly Interfaces with 80C186 CPU
- 80C186/80C187 Provide a Software
- Binary Compatible Upgrade from 80186/82188/8087 Systems. - Skirtas 80C186 procesoriams https://en.wikipedia.org/wiki/Intel_80186 .
- https://datasheets.chipdb.org/Intel/x86/8018x/manuals/27095003.PDF - 80C186EA/80C188EA Microprocessor User’s Manual.
- Truputi paaiškinimų. Intel 286 CPU ir 8086 CPU praktiškai niekuo nesisikiria, gal tik dažniais ir smulkmenom. Todėl jų FPU (8087 ir 287) tinka ir 286 ir 8086 procesoriams. 387 FPU tinka tik 386 procesoriui, nes jis yra 32 bitų, o anie - 16 bitų.
- Panašu, kad x87 FPU veikia su CPU (8086 ar 386 ar vėlesniu) taip, kad, pavyzdžiui, 387 FPU turi 32 bitų data bus ir 386 CPU turi 32 bitų data bus. 387 FPU turi pagal oficialią teoriją 8 tipo registrus. Vienas iš jų turėtų buti akumuliatorius - ST(0). Kiti - nuo ST(1) iki ST(7) yra analogiški, manau, kaip procesoriaus registrai (ir gali būti, kad pop ir push operacijos su jais neatliekamos). Įkrovus vieną 64 bitų Floating point skaičių į ST(0) registrą (kuris, manau yra ne 80 bitų, o realiau, 64 bitų), o kitą Floating point skaičių į ST(i) registrą (i gali buti nuo 1 iki 7), galima atlikti veiksmus, kaip sudėti, daugybą, dalybą (arba kvadratinės šaknies traukimą iš ST(0) esančio 64 bitų skaičiaus ir išsaugant atsakymą jame), išsaugant rezultatą ST(0) registre, nes jis greičiausiai yra akumuliatorius-registras. Ten rašo, kad galima rezultatą saugot ir kituose registruose (ST(1) - ST(7)), bet tai gali būti tik pagiros.
- 8087 - 387 coprocesoriai turi tik pora kontaktu, kontaktuojančius su CPU (ir 32 kontaktus 32 bitų datos perdavimui - 387'tas). Tie kontaktai, kurie kažkaip susije su x87 fpu adresavimu, beveik nieko neadresuoja į x87 (pvz., A31 (Adreso 31 kontaktas iš A0-A31 kontakto) kontaktas jungiasi prie x87 FPU ir nelabai aišku ką jis daro). Procesorius (CPU) pasako ką reikia daryti, pvz., 387 FPU, nusiusdamas jam per data bus 16 bitų opcode. Tame opcode'e yra instrukcija kokią operaciją reikia padaryti 387 FPU coprocesoriui ir su kokiais registrais. Tuose Manual'uose rašo, kad vienas iš operand'ų gali būti iš RAM atminties (nebutinai, pavyzdžiui, dauginti ST(0) su ST(1) ir rezultatą patalpinti į ST(0); galima padauginti ST(0) su 64 bitų operand'u iš RAM atmtinties ir rezultatą išsaugoti ST(0) FPU registre). Iš esmės galėtų egzistuoti tik du 387 registrai: ST(0) ir ST(1), bet tai jau butų per daug ekonomiška. Tiesiog gal dalis FPU instrukcijų (opcode'ų) neveikia, be kurių galima laisvai apseit. Pats FPU negali nieko padėti ar išskaityti iš RAM atminties. CPU procesorius nurodo RAM adresą iš savo registro, iš kurio (iš to nurodyto adreso) bus paimtas operandas (64 Floating Point skaičius (iš dviejų atminties lokacijų [sekančių viena po kitos], nes viena RAM lokacija saugo 32 bitus)) ir įdėtas į kuri nors ST(i) registrą (i gali buti nuo 0 iki 7). Taip pat CPU procesoriaus registras, pasirinkęs RAM adresą, ir nusiuntus CPU procesoriui į FPU 387 koprocesoriu per pora kontaktų signalus, kad ateis data, 387 FPU pasiruoš tai datai ir atliks su ja ir su ST(0) arba ST(i) registru kokią nors operaciją, kaip sudėtį, atimtį, daugybą ar dalybą.
- Jeigu reikia paimti iš FPU ST(0) registro kokį nors apskaičiuotą 64 bitų skaičių, tai CPU procesorius iš savo registro ar iš PC (Program Counter) nurodo į kokį RAM adresą reikia įdėti tą 64 floating point skaičių (per 32 bitų data bus - tam reikia dviejų perdavimų 64 bitų skaičiui). Dar galėtų FPU koprocesorius įkrauti tą 64 bitų skaičių iškart į du CPU procesoriaus registrus, bet tai greičiausiai per sudėtinga ar tiesiog nepasirinkta, nes ten apie tai nieko nerašo (ten [opcode'e] yra pasirinkimo laukeliai tarp registro ir atminties, bet turint galvoj, kad daug tų opcode'ų/instrukcijų gali neveikt, labiau tikėtina, kad negalima krauti 64 bitų skaičius iš FPU į CPU du registrus ir net turbut negalima 32 bitų skaičiaus krauti į vieną CPU registrą, nes gali net nebūti tų 32 bit Single Precision skaičiavimų, o tik 64 bitų Double Precision skaičiavimai (bent jau naujuose procesoriuose, kuriuose FPU integruotas, o senuose gal tik ir skaičiuoja su 32 bitų Single Precision)). Tie FPU opcode'ai FPU koprocesoriaus registrų (kaip ST(0) ar ST(1)) pasirinkimui yra aiškūs maždaug, o kad žinot kokie naudojami registrai CPU procesoriaus RAM atminties adresavimui (iš kurių adresuoto adreso imamas operandas ar į kurį adresą dedamas apskaičiuotas [su FPU] rezultatas) reikia tas 16 bitų opcode'o dalis tyrinėt 386 (ar 8086 ar 286) procesoriaus manual'e. Kaip MOD laukelis yra 2 bitų laukas, o r/m laukelis yra 3 bitų laukas opcode'e. Šitie du laukeliai - jų reikšmė - aprašyti čia: https://bitsavers.org/components/intel/80286/210498-001_iAPX_286_Programmers_Reference_1983.pdf (259 puslapyje Acrobat Reader'yje).]
- Toks Free Pascal kodas:
var a:longint; c:real; begin for a:=1 to 1000000000 do c:=c+1/a; writeln(c); Readln; End.
- duoda atsakymą "2.1300481502506980E+001" po 6 sekundžių su 4.16 GHz veikiančių procesorium (pirmą kartą duoda šitą atsakymą po maždaug 103 sekundžių). Kode yra milijardas iteracijų, 1 milijardas sudeties operacijų ir 1 milijardas dalybos operacijų. Sudeties operacija užima apie pora ciklų, o dalybos operacija užima daug daugiau negu sudeties ar daugybos operacija. Čia mes gavome, kad vienai dalybos operacijai reikia 6*4.16=24.96=~25 ciklų. Gali būti, kad kol planuojama nauja iteracija, FPU (floating point unit) nesnaudžia ir daro dalybos operaciją ar kažkas tokio.
- Su 2.6 GHz procesorium buvo skaičiuotas toks pat kodas čia: https://lt.wikibooks.org/wiki/Matematika/Kreiviniai_integralai#Kreivės_masė
- ten buvo gautas atsakymas 21,3004815025070 po 8 sekundžių su 2,6 GHz procesoriumi. Išeina, kad tenai 2.6 GHz procesoriui vienai dalybos operacijai reikėjo 8*2.6=20.8=~21 ciklo. Arba viena sekunde ten netiksliai paskaičiuota (tada 9*2.6=23.4 ciklo), arba 2.6 GHz procesoriui reikėjo truputi mažiau ciklų šitame kode dalybai (ir viskam kitam) negu 4.16 GHz dažniu veikiančiam procesoriui.
- Toks Free Pascal kodas:
var a:longint; c:real; begin for a:=1 to 1000000000 do c:=c+sqrt(a); writeln(c); Readln; End.
- duoda atsakymą "2.1081851083598383E+013" po 7 sekundžių su 4.16 GHz veikiančių procesorium (pirmą kartą [paleidus] duoda šitą atsakymą po maždaug 90 sekundžių). Kode yra milijardas iteracijų, 1 milijardas sudeties operacijų ir 1 milijardas kvadratinės šaknies traukimo operacijų. Oficialiai [kvadratinės] šaknies traukimo operacija užima tik truputi daugiau ciklų nei dalybos operacija. Čia mes gavome, kad vienai šaknies traukimo operacijai reikia 7*4.16=29.12=~29 ciklų.
- Kodas skaičiuoja integralą
- =21,081,851,067,789.195546659290296218.
- Su Free Pascal kodo skaičiavimu gavome 9 pirmus teisingus skaitmenis.
- Toks Free Pascal kodas:
var a:longint; c:real; begin for a:=1 to 1000000000 do c:=c+a*1.0*a; writeln(c); Readln; End.
- duoda atsakymą "3.3333333383324648E+026" po 5 sekundžių su 4.16 GHz veikiančių procesorium (pirmą kartą [paleidus] duoda šitą atsakymą po maždaug 101 sekundės). Kode yra milijardas iteracijų ir 1 milijardą kartų skaičius a keliamas kvadratu. Galima eilutę "c:=c+a*1.0*a;" pakeisti eilute "c:=c+sqr(a*1.0);". Tuomet skaičiavimas užtruks irgi 5 sekundes. Jei a nebus padaugintas iš 1.0 tada bus klaidos... Gaunasi, kad vienai sudečiai ir vienai [naudingai] daugybos operacijai reikia 5*4.16=20.8=~21 ciklo. Žinoma, čia greičiausiai kažkaip iteracijos paima daug ciklų, o pati daugybos operacija kokius 4 ciklus naudoja.
- Kodas skaičiuoja integralą
- =333,333,333,333,333,333,333,333,333.33333.
- Su FP kodu gavome 9 pirmus teisingus skaitmenis.
- [Update 6 (2025 02 08). Toks Free Pacal kodas:
var a:longint; c:real; begin for a:=1 to 1000000000 do c:=c+sqrt(sqr(0.000000005)+sqr(sqr(0.000000005*a)-sqr(0.000000005*(a-1)))); writeln(c); readln; end.
- duoda atsakymą "2.5874244790371570E+001" (tai reiškia 25.874244790371570) po 15 skundžių per antrą, trečią ir vėlesnius kartus su 4.16 GHz procesorium. Ir duoda šitą atsakymą po minutės ir 29 sekundžių (89 sekundžių), pirmą kartą paleidus (su 4.16 GHz procesorium). Bandžiau kelis kartus leisti tą "pirmą kartą", uždarinėjant ir atidarinėjant Free Pascal ir atsakymas buvo tas pats - po 88-90 sekundžių (priklausomai ar grojo muzika youtube, kai nesimato video). Tai čia išeina net truputi greičiau sukompiliuoja (ar kažką kitą padaro ar užloadina) nei per 80 skundžių, nors kodas sudėtingesnis. Šitam kode yra 1 milijardas iteracijų.
- Čia
- apskaičiuojamas parabolės (y=x^2) ilgis, kai x kinta nuo 0 iki 5, taikant integralus. Šitas kodas ir apskaičiuoja parabolės ilgį (kai x kinta nuo 0 iki 5). Šitas kodas duoda 13 pirmų teisingų [dešimtainių] skaitmentų, lyginant su atsakymu gautu integruojant. Jei norima žinot kiek maždaug teisingu mantisos bitų duoda koks nors kodas, tai reikia dešimtainius skaitmenis padauginti iš 3. Tada gaunasi apie 13*3 = ~39 teisingi mantisos bitai. Double precision ( https://en.wikipedia.org/wiki/Double-precision_floating-point_format ) turi 52 mantisos bitus, o čia skaičiavimo tikslumas yra apie 15-17 dešimtainių skaitmenų, tai 15*3=45, o 17*3=51. Dar pavyzdys: 2^300 = 2.0370359763344860862684456884094e+90 (). 90*3 = 270 = ~300.]
- [Update 6 (2025 02 08 - Later). Toks Free Pacal kodas:
var a:longint; b:longint; c:real; begin for a:=1 to 100000 do begin for b:=1 to 100000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*a)-sqr(0.0000000005*(a-1)))); end; writeln(c); readln; end.
- duoda rezultatą "5.0000003686016354E+000" po 2 minučių ir 33 sekundžių (153 sekundžių), antrą kartą paleidus, su 4.16 GHz CPU. Pirmą karta paleidus duoda šitą rezultatą (5.0000003686016354E+000) po 3 minučių ir maždaug 45 sekundžių (225 sekundžių; 225-80=145 s). Atsakymas kažkodėl yra neteisingas. 5.0000003686016354^2 = 25.000003686016489867165619554533 - irgi neteisingas.
- Toks Free Pacal kodas:
var a:longint; b:longint; c:real; begin for a:=1 to 100000 do begin for b:=1 to 100000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*1.0*a*b)-sqr(0.0000000005*(1.0*a*b-1)))); end; writeln(c); readln; end.
- duoda atsakymą "1.4264437461184009E+001" po 4 minučių ir 36 sekundžių (276 sekundžių; 276-80 = 196 s) iš pirmo paleidimo su 4.16 GHz procesorium. Antrą kartą paleidus, duoda šį rezultatą "1.4264437461184009E+001" po 3 minučių ir 15 sekundžių (195 sekundžių). Kode yra 10 milijardų iteracijų.
- Toks Free Pacal kodas (apskaičiuojantis parabolės ilgį):
var a:longint; b:longint; d:real; c:real; begin for a:=0 to 9999 do begin for b:=1 to 100000 do c:=c+sqrt(sqr(0.000000005)+sqr(sqr(0.000000005*(100000*a+b))-sqr(0.000000005*((100000*a+b)-1)))); end; writeln(c); readln; end.
- duoda atsakymą "2.5874244790371570E+001" po 1 minutės ir 29 sekundžių (89 sekundžių) per pirmą paleidimą su 4.16 GHz procesorium. Per antrą ir trečią paleidimą duoda šį atsakymą "2.5874244790371570E+001" po 16 sekundžių. Kode yra 1 milijardas iteracijų. Gaunama 13 pirmų teisingų skaitmenų.
- Šio kodo versija su 10 milijardu iteracijų po maždaug dviejų minučių skaičiavimo išduoda klaidą "exited with exitecode = 215". O skaičiavimo juodame ekrane rašo (kai paleidi antrą kart ar bet kokį kitą kodą neuždarius Free Pascal programos):
- Runtime error 215 at $004015AF
- $004015AF
- $00409627
- Tas kodas (su 10 milijardų iteracijų), kuris turėtų veikt, yra toks:
var a:longint; b:longint; d:real; c:real; begin for a:=0 to 99999 do begin for b:=1 to 100000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*(100000*a+b))-sqr(0.0000000005*((100000*a+b)-1)))); end; writeln(c); readln; end.
- Viskas jame yra teisingai, bet vat, kažkodėl neveikia (po dviejų minučių skaičiavimo išmeta žinutę "exited with exitecode = 215").]
- [Update 7 (2025 02 08). Toka FP kodas turėtų veikt:
var a:longword; c:real; begin for a:=1 to 4000000000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*a)-sqr(0.0000000005*(a-1)))); for a:=1 to 4000000000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*(4000000000+a))-sqr(0.0000000005*(4000000000+a-1)))); for a:=1 to 2000000000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*(8000000000+a))-sqr(0.0000000005*(8000000000+a-1)))); writeln(c); readln; end.
- bet po 6 minučių ir kažkiek sekundžių skaičiavimo, gaunamas atsakymas kaip ir anksčiau: "exited with exitecode = 215". Jame yra 10 milijardų iteracijų. Antrą kart paleidus, išmeta tą pačią žinutę "exited with exitecode = 215" po 4 minučių ir 30 sekundžių skaičiavimo. Antrą kartą paleidus, matosi užrašai juodame lange nuo pirmo kart. Ten rašoma:
- Runtime error 215 at $0040161E
- $0040161E
- $00409727
- Trečią kart paleidus duodami/matosi tie patys užrašai (kuriuos ką tik ir užrašiau) nuo antro karto.]
- [Update 8 (2025 02 09). Toks Free Pacal kodas:
var a:longword; c:real; d:real; e:real; begin d:=4000000000; e:=3999999999; for a:=1 to 4000000000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*a)-sqr(0.0000000005*(a-1)))); for a:=1 to 4000000000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*(d+1.0*a))-sqr(0.0000000005*(e+1.0*a)))); for a:=1 to 2000000000 do c:=c+sqrt(sqr(0.0000000005)+sqr(sqr(0.0000000005*(d+d+1.0*a))-sqr(0.0000000005*(d+e+1.0*a)))); writeln(c); readln; end.
- duoda rezultatą "2.5874244789837178E+001" po mažadaug 13 minučių (13*60 = ~780 sekundžių) per pirmą paleidimą su 4.16 GHz procesorium. Antrą kart paleidus šitą kodą, gaunamas tas pats atsakymas "2.5874244789837178E+001" po 11 minučių (tiksliau, po kažkur 10 minučių ir 56-58 sekundžių; 11*60 = 660 sekundžių). Šiame kode yra 10 milijardų iteracijų. 660/15 = 44 kartus ilgiau skaičiuoja nei su 1 milijardu iteracijų. Bet šis kodas yra nedaug sudetingesnis nei kodas su 1 milijardu iteracijų.
- Palyginimai:
- 2.5874244790371570E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 1 milijardą iteracijų),
- 2.5874244789837178E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 10 milijardų iteracijų),
- 25.874244790376718110259811166298 (atsakymas gautas integravimo budu).
- Atsakymai yra parabolės y=x^2 šakos ilgis, kai x kinta nuo 0 iki 5. Atsakymas su 1 milijardu iteracijų duoda 13 pirmų teisingų skaitmenų. O atsakymas su 10 milijardų iteracijų duoda 9 pirmus teisingus skaitmenis.
- 2^32 = 4,294,967,296. Reiškia 32 bitai gali saugoti apie 9-10 dešimtainių skaitmenų. Todėl turbut ir yra tikslumas tik 9 pirmi dešimtainiai skaitmenys (kodo su 10 milijardo iteracijų). Nes Free Pacal išsaugo mantisos 52 bits (jeigu 64 bit Double precisions) arba 64 bit'us (jeigu 80 bit Double Extended precision formatas), kai iteracijos yra netrūkios. O kai baigiamos vienos iteracijos ir pradedamos kitos, tada Free Pascal išsaugo tik pirmus 32 mantisos bitus, ignoruodamas likusius.
- Apie Free Pacal, paėmus "Help", parašyta:
- Free Pascal IDE for Win32 for i386
- Target CPU: i386
- Version 1.0.12 2020/06/04
- (Compiler Version 3.2.0)
- (Debugger GNU gdb (GDB) 7.2, using MI interface)
- Copyright (C) 1998-2017 by
- .................
- Taigi, intel 386 turi 32 bitų data bus, todėl krauna tik 32 bitų gabalais (taip ir įkrauna į RAM tik 32 bitus mantisos, nusispjaudamas ant kitų mantisos bitų), ko pasekoje ir gaunamas tik pirmų 9 dešimtainių skaitmenų tikslumas.]
- [Update 8 (2025 02 11). Toks Free Pacal kodas:
var a:longint; b:longint; c:real; d:real; e:real; begin e:=sqr(0.0000000005); d:=100000; for a:=0 to 99999 do begin for b:=1 to 100000 do c:=c+sqrt(e+sqr(sqr(0.0000000005*(d*a+1.0*b))-sqr(0.0000000005*((d*a+1.0*b)-1.0)))); end; writeln(c); readln; end.
- duoda atsakymą "2.5874244789837178E+001" po 2 minučių ir 37-42 sekundžių [gali būti priklausomai nuo to ar youtube groja muzika ar ne, kai groja, atsakymas gaunamas iki 5 sekundžių vėliau] (2*60+40 = 160 sekundžių) per antrą, trečią kartą su 4.16 GHz procesorium. Per pirmą paleidimą šis atsakymas gaunamas po 3 minučių ir 52 sekundžių (60*3+52 = 232 sekundžių). Šiame kode yra 10 milijardų iteracijų. Gaunami 9 pirmi teisingi dešimtainiai skaitmenys parabolės lanko ilgio, kai 0<x<5. Čia maždaug skaičiuojama 10 kartų ilgiau nei kode su 1 milijardų iteracijų, kuris duoda atsakymą po 15 sekundžių su 4.16 GHz CPU. Bet šitam kode truputi daugiau operacijų (nei kode su 1 milijardu iteracijų). Visuose šiose kodose parabolės lanko ilgis skaičiuojamas naudojant Pitagoro teoremą.
- Toks Free Pacal kodas:
var a:longint; b:longint; c:real; d:real; e:real; begin e:=sqr(0.000000005); d:=10000; for a:=0 to 99999 do begin for b:=1 to 10000 do c:=c+sqrt(e+sqr(sqr(0.000000005*(d*a+1.0*b))-sqr(0.000000005*((d*a+1.0*b)-1.0)))); end; writeln(c); readln; end.
- duoda rezultatą "2.5874244790371570E+001" po 16 sekundžių per antrą ir trečią kartą ir po minutės ir 31 sekunės (po 91 sekundės) per pirmą paleidimą (su 4.16 GHz procesorium). Šiame kode yra 1 milijardas iteracijų, o atsakymas yra toks pats kaip pats pirmas kodas skaičiuojantis parabolės lanko ilgį su 1 milijardų iteracijų (Update 6 kodas). Šiame kode irgi gavosi 13 pirmų teisingu dešimtainių skaitmenų (lyginant su integravimo budu gautu atsakymu). Vadinasi iteracijos iteracijose ar trūkios iteracijos nemažina tikslumo... Be to, su 10 milijardų iteracijų parabolės šakos ilgis (0<x<5) turi būti vos vos mažesnis nei su 1 milijardų iteracijų, nes funkcijos y=x^2 reikšmė pradedama skaičiuoti nuo 1 (padauginto iš 0.000...0005; tai yra x ašies padalijimo žingsnis), o ne nuo 0 ir baigiama skaičiuoti su reikšme 10^9 (jeigu yra 1 milijardas iteracijų) arba su reikšme 10^10 (jeigu yra 10 milijardų iteracijų).
- Galima atlikti tokį bandymą: kas yra daugiau, ar
- 1^2 + 2^2 + 3^2 + 4^2 + 5^2 + 6^2 + 7^2 + 8^2 + 9^2 + 10^2 = 385;
- 385*1=385 ar
- 0.5^2 + 1^2 + 1.5^2 + 2^2 + 2.5^2 + 3^2 + 3.5^2 + 4^2 + 4.5^2 + 5^2 + 5.5^2 + 6^2 + 6.5^2 + 7^2 + 7.5^2 + 8^2 + 8.5^2 + 9^2 + 9.5^2 + 10^2 = 717.75;
- 717.5*0.5 = 358.75.
- 385 > 358.75 ir abu šie atsakymai yra skaičiavimas ploto po parabolės šaka, kai x kinta nuo 0 iki 10. Šiuo principu didinant žingsių skaičių (arba padalų/padalijimų skaičių), atsakymas po truputi mažėja, o atsakymo tikslumas didėja.
- Išeina, kad gal arba integravimo teorija nevisai teisinga, arba kažko nedaskaičiuoja Windows 10 kalkuliatorius skaičiuodamas arsinh(x) ir/ar kvadratinę šaknį. Funkcija arsinh(x) yra atvirkštinė hiperbolinio sinuso funkcija (area sine hyperbolic), kuri apskaičiuojama su naturiniu logaritmu, kaip matosi čia:
- Jeigu Windows 10 kalkuliatorius skaičiuoja ln(x) ir/ar kvadratinę šaknį tik 32 bitų mantisos tikslumu, tai kodėl 13 pirmų dešimtainių skaitmenų turėjo sutapt?
- Papildymas-pataisymas. Čia:
- https://lt.wikibooks.org/wiki/Aptarimas:Matematika/Kreiviniai_integralai#Parabolės_lanko_ilgio_patikrinimas
- ir čia
- https://lt.wikibooks.org/wiki/Aptarimas:Matematika/Kreiviniai_integralai#Parabolės_lanko_ilgio_patikrinimas_2
- skaičiuojamas parabolės lanko ilgis rankiniu budu, padalinant parabolę į 10 ir į 20 dalių atitinkamai. Tai padalinus į 10 dalių parabolės šaką, apytikslis atsakymas yra toks:
- 16.80542614 (kai x kinta nuo 0 iki 4),
- o padalinus į 20 dalių parabolės šaką, atsakymas yra toks:
- 16.81532597 (kai x kinta nuo 0 iki 4).
- Padalinus į daugiau dalių, atsakymas yra didesnis.]
- [Update 9 (2025 02 14). Toks Free Pacal kodas:
var a:longint; c:real; begin for a:=1 to 2000000000 do c:=c+sqrt(sqr(0.0000000025)+sqr(sqr(0.0000000025*a)-sqr(0.0000000025*(a-1)))); writeln(c); readln; end.
- duoda atsakymą "2.5874244790303923E+001" po 31 sekundės iš antro ir trečio karto ir po minutės ir 45 sekundžių (105 sekundžių) iš pirmo karto (su 4.16 GHz procesorium). Kode yra 2 milijardai iteracijų.
- Palyginus yra:
- 2.5874244790371570E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 1 milijardą iteracijų),
- 2.5874244790303923E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 2 milijardus iteracijų),
- 25.874244790376718110259811166298 (atsakymas gautas integravimo budu).
- Su 2 milijardais iteracijų gaunami 12 teisingų pirmų skaitmenų. Su 1 milijardų iteracijų, gaunami 13 pirmų teisingų skaitmenų.
- [Mini-Update. Jeigu vietoj a:longint;, įrašyt a:longword;, tai atsakymas "2.5874244790303923E+001" šio kodo su 2 milijardais iteracijų bus gautas pirmą kartą po 3 minučių ir 33 sekundžių (213 sekundžių), o antrą ir trečią kartą - po 2 minučių ir 9 sekundžių (129 sekundžių) su 4.16 GHz procesorium. Iš čia https://wiki.freepascal.org/Data_type sužinoma:
- Longword - Range: (0 .. 4294967295),
- LongInt - Range: (-2147483648 .. 2147483647).
- 32 bitų sveikojo skaičiaus longint neigiami skaičiai yra užkoduoti Two's complement formate (skaičiau apie tai beveik naujausiame Intel CPU manual'e (2023 metų)). Reiškia, jeigu nereikia daugiau nei 2147483647 iteracijų , geriau naudoti LongInt 32 bitų sveikuosius skaičius, o ne Longword sveikuosius skaičius. Nes pirmam kartui: 213/105 = 2.02857 karto greičiau su longint. Antram kartui: 129/31 = 4.16129 karto greičiau su longint nei su longword.]
- Toks Free Pacal kodas:
var a:longword; c:real; begin for a:=1 to 4000000000 do c:=c+sqrt(sqr(0.00000000125)+sqr(sqr(0.00000000125*a)-sqr(0.00000000125*(a-1)))); writeln(c); readln; end.
- duoda atsakymą "2.5874244790194215E+001" po 4 minučių ir 9 sekundžių (249 sekundžių) per antrą ir trečią paleidimą ir po 5 minučių ir ~40 sekundžių per pirmą paleidimą (340 sekundžių) su 4.16 GHz CPU. Kode yra 4 milijardai iteracijų.
- Palyginami rezultatai su skirtingu skaičium iteracijų:
- 2.5874244790371570E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 1 milijardą iteracijų),
- 2.5874244790303923E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 2 milijardus iteracijų),
- 2.5874244790194215E+001 (atsakymas apskaičiuotas su Free Pascal, naudojant 4 milijardus iteracijų),
- 25.874244790376718110259811166298 (atsakymas gautas integravimo budu).
- Atsakymas su 4 milijardais iteracijų turi 11 pirmų teisingų skaitmenų.]
- [Update 10 (2025 02 15). Toks Free Pacal kodas (skaičiuojantis parabolės lanko ilgį):
var a:longint; c:extended; begin for a:=1 to 2000000000 do c:=c+sqrt(sqr(0.0000000025)+sqr(sqr(0.0000000025*a)-sqr(0.0000000025*(a-1)))); writeln(c); readln; end.
- duoda atsakymą "2.58742447903767098238E+0001" po 40 sekundžių per antrą ir trečią kartą su 4.16 GHz procesorium. Per pirmą kartą duoda šį atsakymą po maždaug minutės ir 55 sekundžių (~115 sekundžių). Kode yra 2 milijardai iteracijų. Skaičiavimas vyksta Double Extended Precision formate (80 bitų). Tie kodai skaičiavo Double Precision formate (64 bitų). Dabar atsakymas duotas 21 dešimtainio skaitmens tikslumu (pora paskutiniu skaitmenų gali buti neteisingi). O Double Precision formatas duoda rezultatus 17 dešimtainių skaitmenų tikslumu (garantuoti yra tik 15 dešimtainių skaitmenų, o likę 2 skaitmenys tiesiog yra apytikslis tikslumas, kuris geriau nei 15 skaitmenų, bet vis tiek dažniausiai neteisingas (dėl [neužtektinai] mantisos bitų vertimo į dešimtainius skaitmenis)). Dabar gauta 15 pirmų teisingų dešimtainių skaitmenų.
- Anksčiau tikslumas pradėdavo mažėt, kai skaičiuojama su daugiau nei 1 milijardų iteracijų, nes, pavyzdžiui, su 2 milijardais iteracijų 0.0000000025^2 = 0.00000000000000000625 = 6.25*10^{-18}. O double precision formatas turi tik 17 skaitmneų. Tada
- 2.5874244790303923E+001
- 25.874244790303923
- _0.00000000000000000625.
- T. y., tik iš pradžių tie paskutiniai skaičiai prisideda į bendrą sumą (su mažom a reikšmėm), o paskui (su didelėm a reikšmėm) paskutiniai skaitmenys ar mantisos bitai yra išstumiami ir tikslumas prarandamas...
- [Update 11 (2025 02 16). Toks Free Pacal kodas (skaičiuojantis parabolės lanko ilgį):
var a:longword; c:extended; begin for a:=1 to 4000000000 do c:=c+sqrt(sqr(0.00000000125)+sqr(sqr(0.00000000125*a)-sqr(0.00000000125*(a-1)))); writeln(c); readln; end.
- duoda atsakymą "2.58742447903767140427E+0001" po 4 minučių ir 53 sekundžių (293 sekundžių) per antrą paleidimą ir po 6 minučių ir 26 sekundžių (386 sekundžių) per pirmą paleidimą (su 4.16 GHz CPU). Kode yra 4 milijardai iteracijų. Gaunami 16 pirmų teisingų skaitmenų. Tai 293/243 = 1.20576 karto ilgiau nei kodas irgi su 4 milijardų iteracijų, bet skaičiuojantis Double precision formate (dalinta iš 243 s (o ne 249 s), nes paskui skaičiavo labiau per tokį laiką...).
- Toks Free Pacal kodas (skaičiuojantis parabolės lanko ilgį):
var a:longword; c:extended; d:extended; begin d:=sqr(0.00000000125); for a:=1 to 4000000000 do c:=c+sqrt(d+sqr(d*sqr(1.0*a)-d*sqr(1.0*(a-1)))); writeln(c); readln; end.
- duoda atsakymą "2.58742447903767183778E+0001" po 4 minučių ir 56 sekundžių (296 sekundžių) per antrą ir trečią paleidimą ir po 6 minučių ir 40 sekundžių (400 sekundžių) per pirmą paleidimą (su 4.16 GHz CPU). Kode yra 4 milijardai iteracijų. Gaunami 17 pirmų teisingų skaitmenų.
- 2.58742447903767183778E+0001 (atsakymas gautas dabar su 4 milijardais iteracijų ir Extended double precision),
- 25.874244790376718110259811166298 (atsakymas gautas integravimo budu).
- Su superkompiuteriu iš 1000 procesorių būtų galima gauti 34 pirmus teisingus skaitmenis, panaudojus maždaug (4*10^9)^2 = 16,000,000,000,000,000,000 = 1.6*10^{19} iteracijų. Jeigu butų skaičiuojama su Hardware (kas neįmanoma, nes čia yra tik apie 20 dešimtainių skaitmenų tikslumas), tai tam reikėtų antram kartui 296 (s) * 4*10^9 = 1,184,000,000,000 sekundžių (apie trilijoną sekundžių). Tai yra 1,184,000,000,000/(3600*24) = 13,703,703.7037 = ~13703703 dienų (~13.7 milijonai dienų arba 13,703,703.7037/365 = 37,544.393708767 = ~37544 metai). Reikėtų apie 37 su puse tūkstančių metų su vienu CPU. Su 1000 procesorių reikėtų apie 38 metų. Skaičiuojant su Software galėtų prireikt nuo 1000 iki milijono kartų daugiau laiko (nuo 38 tūkstančių iki 38 milijonų metų).
- [Update 11 (2025 02 17). Toks Free Pacal kodas (skaičiuojantis parabolės lanko ilgį):
var a:longint; b:longint; c:extended; d:extended; e:extended; begin e:=sqr(0.0000000005); d:=100000; for a:=0 to 99999 do begin for b:=1 to 100000 do c:=c+sqrt(e+sqr(sqr(0.0000000005*(d*a+1.0*b))-sqr(0.0000000005*((d*a+1.0*b)-1.0)))); end; writeln(c); readln; end.
- duoda atsakymą "2.58742447903767277956E+0001" po 3 minučių ir 52 sekundžių (232 sekundžių) paleidus antrą kartą ir po 5 minučių ir 16 sekundžių (316 sekundžių) paleidus pirmą kartą (su 4.16 GHz CPU). Kode yra 10 milijardų iteracijų. Gaunama 15 pirmų teisingų skaitmenų, nors skaičiuojama Double Extended precision formate (80 bits). Tai 232/160 = 1.45 karto ilgiau nei analogiškas kodas skaičiuojantis Double precision formate (64 bits) iš Update 8.
- Toks Free Pacal kodas (skaičiuojantis parabolės lanko ilgį):
var a:longint; b:longint; c:extended; d:extended; e:extended; begin e:=sqr(0.0000000005); d:=100000; for a:=0 to 99999 do begin for b:=1 to 100000 do c:=c+sqrt(e+sqr(e*sqr(1.0*d*a+1.0*b)-e*sqr(1.0*d*a+1.0*b-1.0))); end; writeln(c); readln; end.
- duoda atsakymą "2.58742447903767125578E+0001" po lygiai 4 minučių (240 s) per antrą kartą; po 3 minučių ir 57 sekundžių (237 sekundžių) per trečią kartą; ir po 5 minučių ir 22 sekundžių (322 s) per pirmą kartą (su 4.16 GHz procesorium). Kode yra 10 milijardų iteracijų. Atsakymas gaunamas su 16 pirmų teisingų skaitmenų. Padidinus iteracijų skaičių nuo 4 milijardų iki dabartinių 10 milijardų, tikslumas nepadidėjo... Tai gali būti dėl to, kad sqr(0.0000000005) = 0.0000000005^2 = 0.00000000000000000025 = 2.5*10^19 ir
- 2.58742447903767125578E+0001
- 25.8742447903767125578
- _0.00000000000000000025
- Todėl pvz šitoje eilutėje:
c:=c+sqrt(e+sqr(e*sqr(1.0*d*a+1.0*b)-e*sqr(1.0*d*a+1.0*b-1.0)));
- skaičius e pridedamas kaip 0.0000000000000000002, o ne 0.00000000000000000025. Be to, lygtais ne visi 21 skaitmuo yra garantuotai teisingi dėl dvejetainių skaitmenų konvertavimo į dešimtainius. Pora ar keli paskutiniai dešimtainiai skaitmenys yra tik apytiksli reikšmė paskutinių dvejetainių skaitmenų (pvz, paskutiniai 3 skaitmenys 578 galėtų būti 635 garantuoti-tikri). Be to, po daug iteracijų tikslumas prarandamas paskutiniausiuose skaitmenyse... Vikipedijoje lygtais rašo, kad tik 18 dešimtainių skaitmenų Double Extended precision formate yra garantuoti...
- Iš tikro tai geriau patikrint kiek skaičius e prideda pošaknyje toje eilutėje.
c:=c+sqrt(e+sqr(e*sqr(1.0*d*a+1.0*b)-e*sqr(1.0*d*a+1.0*b-1.0)));
- (e+(e*(1.0*d*a+1.0*b)^2-e*(1.0*d*a+1.0*b-1.0)^2)^2)^0.5,
- (0.00000000000000000025+(0.00000000000000000025*(100000*99999+100000)^2-0.00000000000000000025*(100000*99999+100000-1)^2)^2)^0.5 =
- = (0.00000000000000000025 + 2.49999999975000000000625e-17)^0.5 = (2.52499999975000000000625e-17)^0.5 = 5.024937810311685837557196746547e-9.
- Prisidėjo visgi į bendrą sumą tas visas 0.00000000000000000025.]
- [Update 12 (2025 03 02). Toks Free Pacal kodas (skaičiuojantis e):
var a:longint; c:extended; begin c:=1; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; writeln(c); Readln; End.
- duoda atsakymą "2.71828182925008117595E+0000" po 2 minučių ir 4 sekundžių (124 sekundžių) per antrą ir trečią paleidimą ir po 2 minučių ir 30 sekundžių (150 sekundžių) pirmą kartą paleidus (su 4.16 GHz CPU). Dabar ne skaičiavimams buvo sugaišta mažiau laiko (apie 26 sekundės). Gal dėl to, kad uždariau vieną youtube kanalą su AI sugeneruotais video Opera browser'yje (gal tas kanalas su daug video kažkaip daug atminties užkraudavo, nepriklausomai ar Opera paleista ar ne). Nes ir su 1 milijardu iteracijų pradėjo greičiau pirmą kart skaičiuot e reikšmę. Šiame kode yra 10 milijardų iteracijų. Skaičiavimas gavosi 124/56 = 2.2142857 karto ilgesnis iš antro karto nei skaičiuojant Double precision (64 bit) formate irgi su 10 milijardų iteracijų (dabar skaičiavimas vyko Double Extended precision (80 bit) formate).
- Pateiktas palyginimas e skaičiavimo su 1 milijardų iteracijų, su 10 milijardų iteracijų ir palygininama su Winows 10 kalkuliatoriaus reikšmėm:
- 2.7182818271394744E+000 (FP kodas su 1 milijardų iteracijų Double precision formate),
- 2.7182818291803450E+000 (FP kodas su 10 milijardų iteracijų Double precision formate),
- 2.71828182925008117595E+0000 (FP kodas su 10 milijardų iteracijų Double Extended precision formate),
- 2.7182818270999043223766440238603 (),
- 2.7182818284590452353602874713527 (tiksli Windows 10 kalkuliatoriaus e reikšmė, esanti pačiam kalkuliatoriuje).
- Apskaičiuota skaičiaus e reikšmė neturėtų būti didesnė už tikslią [Windows 10 kalkuliatoriaus] reikšmę.
- Toks Free Pacal kodas (skaičiuojantis e):
var a:longword; c:extended; begin c:=1; for a:=1 to 4000000000 do // 1/4000000000 = 0.00000000025 c:=c*1.00000000025; writeln(c); Readln; End.
- duoda rezultatą "2.71828182786734385266E+0000" po 49-50 sekundžių per pirmą ir trečia paleidimą ir po minutės ir 13 sekundžių (73 sekundžių) per pirmą paleidimą (su 4.16 GHz CPU). Kode yra 4 milijardai iteracijų. 10/4 = 2.5 . Ir 124/50 = 2.48 .
- Palyginus su tikslia e reikšme:
- 2.71828182786734385266E+0000 (FP kodas su 4 milijardais iteracijų Double Extended precision formate),
- 2.7182818270999043223766440238603 (),
- 2.7182818284590452353602874713527 (tiksli Windows 10 kalkuliatoriaus e reikšmė, esanti pačiam kalkuliatoriuje).
- Toks Free Pacal kodas (skaičiuojantis e):
var a:longword; c:extended; begin c:=1; for a:=1 to 4000000000 do c:=c*1.0000000001; for a:=1 to 4000000000 do c:=c*1.0000000001; for a:=1 to 2000000000 do c:=c*1.0000000001; writeln(c); Readln; End.
- duoda atsakymą "2.71828182925008117595E+0000" po 2 minučių ir 5 sekundžių (125 sekundžių) per antrą paleidimą ir po 2 minučių ir 24 sekundžių (144 sekundžių) per pirmą paleidimą (su 4.16 GHz CPU). Kode yra 10 milijardų iteracijų ir skaičiuojama Double Extended precision foramte (80 bit).
- Toks Free Pacal kodas (skaičiuojantis e):
var a:longint; c:extended; begin c:=1; for a:=1 to 2000000000 do c:=c*1.0000000005; // 1/2000000000 = 0.0000000005 writeln(c); Readln; End.
- duoda atsakymą "2.71828182752755757065E+0000" po 25 sekundžių per antrą ir trečią paleidimus ir po 47 sekundžių per pirmą paleidimą (su 4.16 GHz CPU). Kode yra 2 milijardai iteracijų ir skaičiuojama Double Extended precision foramte (80 bit). Su 4 milijardais iteracijų atsakymas gaunamas po 2 kartus ilgesnio laiko (25 s vs 50 s). Skaičiuojant Double precision formate (64 bit) ir su 1 milijardu iteracijų, atsakymas gaunamas iš antro karto po 5.5 sekundžių. 25/5.5 = 4.54545 karto ilgiau. 4.54545/2 = 2.2727 karto ilgiau jei ir ten ir ten yra 1 milijardas iteracijų.
- Palyginus su tikslia e reikšme:
- 2.7182818271394744E+000 (FP kodas su 1 milijardų iteracijų Double precision formate),
- 2.7182818275053831E+000 (FP kodas su 2 milijardais iteracijų Double precision formate; pirmą kartą po ~34-37 s, antrą po 11-12 s),
- 2.71828182752755757065E+0000 (FP kodas su 2 milijardais iteracijų Double Extended precision formate),
- 2.71828182786734385266E+0000 (FP kodas su 4 milijardais iteracijų Double Extended precision formate),
- 2.7182818291803450E+000 (FP kodas su 10 milijardų iteracijų Double precision formate),
- 2.71828182925008117595E+0000 (FP kodas su 10 milijardų iteracijų Double Extended precision formate),
- 2.7182818270999043223766440238603 (),
- 2.7182818284590452353602874713527 (tiksli Windows 10 kalkuliatoriaus e reikšmė, esanti pačiam kalkuliatoriuje).]
Sinuso ir kosinuso greitesnis skaičiavimas
[keisti]- -2.7725921425635885468885319116808e-48 Toks atsakymas gaunamas su Windows 10 kalkuliatorium. Toks greičiausiai ir yra didžiausias Windows 10 kalkuliatoriaus tikslumas ().
- -9.3009073833400679282048050079536e-48
- Taigi, galima apskaičiuoti, pavyzdžiui, sinusą su Makloreno eilute nuo 0 iki 45 laipsnių, o sin(x) reikšmes nuo 45 laipsnių iki 90 laipsnių (kai 45<x<90) galima gauti taip:
- O kosinuso reikšmes, kai 45<x<90 (laipsnių), galima gauti, apskaičiavus sinuso Makloreno eilutę su ir paskui atlikti tokias operacijas (kelimo kvadratu ir šaknies traukimo operacijas):
- Tik truputi neaišku, kas procesoriaus FPU yra greičiau ar ištraukti kvadratinę šaknį, ar skaičiuot maždaug turbut dvigubai ilgesnę Makloreno eilutę.
- Sinuso liekamojo nario formulė yra tokia:
- Tarkime, kad k=10, o Tada paklaida bus
- = 1.5707963^23/25852016738884976640000 = 32415.819192852196702666149390257/25852016738884976640000 =
- = 1.2538990485834848423867495105035e-18
- O dabar tarkime, kad k=5, o Tada paklaida bus
- = 0.785398^13/6227020800 = 0.04326804604255175660997065487462/6227020800 =
- = 6.9484344813095463901406359321326e-12
- Matome, kad dvigubai ilgesnė Makloreno eilutė (su ) sinusui skaičiuoti duoda geresnį tikslumą () negu trumpesnė Makloreno eilutė (su ir kurios tikslumas ).
- Gal maždaug pusantro karto pailgėja Makloreno eilutė, kai pakeičiamas kad gauti tą patį tikslumą. Sakyčiau, ilgesnę Makloreno eilutę paskaičiuot turėtų būti greičiau procesoriaus FPU, negu skaičiuoti trumpesnę Makloreno eilutę ir traukuti šaknį. Bet jeigu reikia vienu metu apskaičiuoti ir sinusą ir kosinusą, tai tikriausiai greičiau apskaičiuoti sinusą pagal Makloreno eilutę, o kosinusą paskui apskaičiuoti pagal formulę (naudojant šaknies traukimą):
- Taip kompiuteriui bus greičiau nei skaičiuoti ir sinuso, ir kosinuso Makloreno eilutes.
- Pradžioje ten klaidos (nereikia traukti jokios šaknies!) ir gaunasi, kad galima labai greitai apskaičiuoti sin(x), kai 45<x<90 laipsnių. Štai taip:
- Pavyzdžiui, jeigu x=90 laipsnių, tai cos(90-x)=cos(90-90)=cos(0)=1 ir sin(x)=sin(90)=1.
- Kosinusas gi, apskaičiuojamas taip: