Wikibooks
ltwikibooks
https://lt.wikibooks.org/wiki/Pagrindinis_puslapis
MediaWiki 1.47.0-wmf.19
first-letter
Medija
Specialus
Aptarimas
Naudotojas
Naudotojo aptarimas
Wikibooks
Wikibooks aptarimas
Vaizdas
Vaizdo aptarimas
MediaWiki
MediaWiki aptarimas
Šablonas
Šablono aptarimas
Pagalba
Pagalbos aptarimas
Kategorija
Kategorijos aptarimas
TimedText
TimedText talk
Module
Module talk
Event
Event talk
Dešimtainių skaičių vertimas į dvejetainius skaičius
0
11878
59026
59020
2026-09-10T09:17:18Z
Paraboloid
1294
/* Kaip FPU galėtų apseit be barrel shifters sudedant skaičius su skirtingom eksponentėm */
59026
wikitext
text/x-wiki
==Dešimtainių skaičių vertimas į dvejetainius skaičius==
[https://www.google.com/search?client=opera&q=single+precision&sourceid=opera&ie=UTF-8&oe=UTF-8&udm=50&fbs=ABfTbFVyMZGZf1hfvX9uKjN_-G8c4u0nXx4bEIpwm1lnNH832VTJOOCxW_fyN-Q_ezyf8gKjm3rhh_G8jdZ2Q6tji4z8Mva6cfvBj5rpPch2IC955IbzrbwHf6tkbFbd-nuFdbKth9Y-58t5LtnYgTA4TWDFYCUNl4d7F9XBAwiEq2T2c7SNgrvMxYiRBXL4fnQnnTOewx28&aep=10&ntc=1&sxsrf=APpeQnvZo3fX2PwO881uSdWsoRELP18NhQ%3A1788348274309&mstk=AUtExfAJ0Kp9BqT3EJbzHOz3MHMalnyXkoOTLvgho5ZQ9lzjrnmmUFCgux_RVe7bouRlyBSGpkUcLpvfZ39N45TUoor6TyyEW7_PpE7eOwP-kU7IFxE6monk-HtsDXkDu7g362AN4NTW3oRjfwn9OtD0BIj2r4QAVauMhDPCIAiWgLimDuate6cf2o6bB5R-6-vzcCqbCju1KJQTdR4HSF9puZ_HCG1hXQ8QaCIqD1LJ_HWonNnZGma6Biu3I1wWFPy6esLnfaKpIX6VFWZsArfQN97XGipwkH7FLKCyZiev5rZBrhE8KincMthP7B9AJDugKLy8qJfORQ7PQR2GslAaCLnkechjBCnXfrmSohwJskbEWU9db6Q279uO4dGu0eI2YUvpEwoQUqkbREqJqrGpJQWFy7WoTzI93_o_2I61PU1FTiuGq-4AVN7erT0WmuWizHDEE2XsTes&aioh=3&csuir=1&atvm=2&mtid=dQeYaumuDL2Ixc8Px6Ts0QE DI atsakymas]
:Single-precision is a 32-bit computer number format that stores fractional and large numbers using a sign bit, an exponent, and a mantissa. Structure of Single-Precision (Binary32) According to Wikipedia, “Single-precision floating-point format (sometimes called FP32, float32, or float) is a computer number format, usually occupying 32 bits in computer memory.” The 32 bits are divided into three specific parts:
*Sign bit (1 bit): Determines if the number is positive (0) or negative (1).
*Biased exponent (8 bits): Stores the magnitude with an excess-127 bias, giving an actual exponent range of -126 to +127.
*Mantissa/Significand (23 explicitly stored bits): Represents the precision digits of the number, with an implicit leading 1 for normalized values.
:Precision and Applications. Decimal Precision: Provides about 7 to 8 decimal digits of accuracy. Most users on StackOverflow agree that “Single precision typically offers 7-8 decimal digits of precision.”
:'''Dešimtainių skaičių vertimas į dvejetainius.'''
:Step-by-Step Conversion Framework.
*Step 1: Determine the Sign Bit (1 bit). If the number is positive, the sign bit is 0. If the number is negative, the sign bit is 1. Example (-12.625): It is negative, so the sign bit is 1.
*Step 2: Convert the Absolute Value to Binary. Separate the number into its integer and fractional parts, convert both to binary, and combine them.
:Integer part '''12''': (12/2 = 6) ('''rem'''ainder 0), (6/2 = 3) (rem 0), (3/2 = 1) (rem 1), (1/2 = 0) (rem 1) <math>\rightarrow </math> '''1100'''.
:Fractional part '''0.625''': (0.625 * 2 = 1.25) (1), (0.25 * 2 = 0.5) (0), (0.5 * 2 = 1.0) (1) <math>\rightarrow </math> '''101'''.
:Example Combined: '''12.625''' = '''1100.101'''.
*Step 3: Normalize the Binary Number. Shift the binary point so that exactly one non-zero digit (which will always be 1) remains to its left. Count how many places you moved the point. This count is your exponent (E).
:Move the point 3 places to the left: '''1100.101''' <math>\rightarrow </math> '''1.100101'''.
:Example Exponent: Because we moved it 3 places, E = 3.
*Step 4: Calculate the Biased Exponent (8 bits) IEEE 754 single-precision uses a bias of 127. Add your exponent E to this bias, and convert the resulting integer to an 8-bit binary number.
:Calculation: 127 + 3 = 130.
:Convert 130 to 8-bit binary: '''130''' = '''10000010'''.
*Step 5: Extract the Mantissa/Significand (23 bits).
:Take the fractional bits from your normalized number (everything to the right of the binary point). Drop the leading 1. If the bits are fewer than 23, pad the right side with zeros until you reach exactly 23 bits.
:Normalized bits: 1.100101 <math>\rightarrow </math> Drop the ''1.'' (atmesti kairiausią vienetą ir tašką po jo) to get '''100101'''.
:Example Pad to 23 bits: 10010100000000000000000.
:'''Final Assembly.'''
:Combine the three components in order: Sign (1 bit) + Exponent (8 bits) + Mantissa (23 bits).
:Component_____________Bits______________Value for -12.625_______
:Sign____________________1_______________________1______________
:Exponent________________8____________________10000010__________
:Mantissa________________23______________10010100000000000000000
*Final 32-bit Binary String: '''11000001010010100000000000000000'''.
*Hexadecimal Representation: '''C1450000'''.
:Sign: '''1'''1000001010001010000000000000000.
:Exponent: 1'''10000010'''10001010000000000000000.
:Mantissa: 110000010'''10010100000000000000000'''.
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Mantissa: 100101 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 0.578125.
:8 * 0.578125 = 4.625.
:8 * 1 = 8.
:8 + 4.625 = 12.625.
:Reikia gražinti paimtą vienetą priekyje ('''12.625''' = '''1100.101'''; '''1100.101''' <math>\rightarrow </math> '''1.100101'''):
:8 * (1 + 0.578125) = 8 * 1.578125 = 12.625.
:-12.625 = -1.2625 * 10^1.
:Čia mantisa 0.2625, o ekspontentė 1 dešimtainėje sistemoje. Skačių 0.2625 reikia kaskart dauginti iš 2.
:0.2625*2 = 0.525 (ne vienetas, reiškia pirmas skaitmuo 0),
:0.525*2 = 1.05 (vienetas, reiškia antras skaitmuo 1),
:0.05*2 = 0.1 (ne vienetas, reiškia trečias skaitmuo 0),
:0.1*2 = 0.2 (ne vienetas, reiškia ketvirtas skaitmuo 0),
:0.2*2 = 0.4 (ne vienetas, reiškia penktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia šeštas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia septintas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia aštuntas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia devintas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia dešimtas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia vienuoliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia dviliktas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia tryliktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia keturioliktas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia penkioliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia šešioliktas skaitmuo 1).
:Tada galutinis skaičius '''0.2625''' yra toks:
:0.0100001100110011.
:Kad gauti didesnį tikslumą, procesą reikia kartoti begalo arba iki reikiamo tikslumo.
:Skaičiau '''0.2625''' reikšmė yra:
:0.0100001100110011 =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*0 + 0.015625*0 + 0.0078125*1 + 0.00390625*1 + 0.001953125*0 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 =
:= 0.26171875 + 0.0007781982421875 = '''0.26249'''69482421875.
:Taigi, 0.26249 labai panašus į 0.2625.
:Vadinasi '''1.2625''' dvejetainėje sistemoje apytiksliai yra '''1.0100001100110011'''.
:Eksponentę 10 reikia versti taip:
:10 = 8 + 2. Tada 10 dvejetainėje sistemoje yra 00001010 (1*0 + 2*1 + 4*0 + 8*1 + 0 + 0 + 0 + 0 = 10).
:Bet eksponentė single precision formate gali būti tik, 0, 1, 2, 4, 8, 16, 32, 64, 128 ir taip toliau.
:Arba reikia daryti taip:
:10/2 = 5 (rem 0),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0)
:1/2 = 0.5 (rem 1).
:Va štai šitaip ir gaunamas skaičius 10. Tai yra 1010.
:Skaičius 1010 pakeičiamas skaičiu 1.010 su eksponente 3 (2^3 = 8).
:Tada skaičius 1.010 iškoduojamas taip:
:1 + 0.5*0 + 0.25*1 + 0.125*0 = 1.25.
:Padauginama iš dviejų pakelta eksponente 3:
:1.25 * 2^3 = 1.25 * 8 = 10.
:Tuomet
:1.2625 * 10 = 12.625.
:'''Grįžimas prie pavyzdžio.'''
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Skaičiaus 1100.101 kablelį galima pastumti 4 pozicijom į kairę ir eksponentę duoti 4. Mantisa tada yra 0.1100101, o eksponentė yra 4.
:0.5*1 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*0 + 0.0078125*1 = 0.7890625.
:2^4 * 0.7890625 = 16 * 0.7890625 = '''12.625'''.
:Bandomas kitas skaičius 24:
:24/2 = 12 (rem 0),
:12/2 = 6 (rem 0),
:6/2 = 3 (rem 0),
:3/2 = 1 (rem 1),
:1/2 = 0.5 (rem 1).
:Reiškia 24 yra 11000 (16*1 + 8*1 + 4*0 + 2*0 + 1*0 = 24).
:Bandomas kitas skaičius 19:
:19/2 = 9 (rem 1),
:9/2 = 4 (rem 1),
:4/2 = 2 (rem 0),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 19 yra 10011 (16*1 + 8*0 + 4*0 + 2*1 + 1*1 = 19).
:Bandomas skaičius 22:
:22/2 = 11 (rem 0),
:11/2 = 5 (rem 1),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 22 yra 10110 (16*1 + 8*0 + 4*1 + 2*1 + 1*0 = 16+4+2 = 22).
:Trumpas paaiškinimas kodėl eksponentė saugoma formate nuo 1 iki 254, pridedant 127 prie -126 ir iki 127. Tai greičiausia yra todėl, kad sudedant single precision skaičių tokiame formate kokiame jis yra, jo reiikšmė gali būti gana tiksliai traktuojama kaip sveikojo skaičiaus reikšmė. Su kai kuriais pakeitimais.
:'''-12.625''' yra '''11000001010010100000000000000000'''.
:Pridėjus 127 ('''01111111''') prie eksponentės 3 ('''00000011''') gauname 130 ('''10000010'''):
:'''00000011'''+
:'''01111111'''=
:'''10000010'''.
:Vadinasi tada skaičius '''-12.625''' tampa
:Mantissa: 1100101 = 1 + 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 1.578125;
:2^130 = 1.3611294676837538538534984297271e+39 = 1.3611294676837538538534984297271 * 10^39.
:1.578125 * 2^130 = 2.148032441188424050612552209413e+39.
:O sveikasis skaičius '''11000001010010100000000000000000''' be minuso yra '''01000001010010100000000000000000''' ir užrašomas taip:
:1*0 + 2*0 + 4*0 + 8*0 + 16*0 + 32*0 + 64*0 + 128*0 + 256*0 + 512*0 + 1024*0 +
:+ 2048*0 + 4096*0 + 8192*0 + 16,384*0 + 32,768*0 + 65,536*0 + 131,072*0 +
:+ 262,144*1 + 524,288*0 + 1,048,576*1 + 2,097,152*0 + 4,194,304*0 + 8,388,608*1 + 16,777,216*0 + 33,554,432*1 +
:+ 67,108,864*0 + 134,217,728*0 + 268,435,456*0 + 536,870,912*0 + 1,073,741,824*0 +
:+ 2,147,483,648*1 + 4,294,967,296*0 =
:= 0 + 0 + 43,253,760 + 0 + 2,147,483,648 = 2,190,737,408.
:Kažka supainiojau. Atrodė, kad eksponentė beveik tas pats kas didesni skaičiai priekyje.
==Skaičiavimas inverse square root su C kodu==
:Čia https://en.wikipedia.org/wiki/Fast_inverse_square_root aiškinama kaip naudojant sveikuosius skaičius galima apsakičiuoti <math>\frac{1}{\sqrt{x}}.</math>
:"The fast inverse square generates a good approximation through integer operations by adding and subtracting the integer form of floating-point numbers, and taking a square root by dividing by two (which is just a right-shift)."
:Iš čia https://lt.wikibooks.org/wiki/Sekos_riba#Greitas_šaknies_iš_skaičiaus_a_traukimo_būdas gauta formulė
:<math>y_{n+1} = \frac{y_{n}\left(3-xy_n^2\right)}{2}. \quad (4)</math>
:<math>y_n\approx \frac{1}{\sqrt{x}}.</math>
:Ją dar galima pagreitint, pakeitus z = x/2. Tada ši formulė tampa tokia:
:<math>y_{n+1} = y_{n}\left(1.5 - zy_n^2\right). \quad (4.1)</math>
:Programavimo kalbos C kodas skaičiuoti <math>\frac{1}{\sqrt{x}}</math> yra toks:
<syntaxhighlight lang="c">
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
</syntaxhighlight>
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Toliau eilutė
i = 0x5f3759df - ( i >> 1 );
:reiškia pastumti sveikąjį skaičių '''i''' viena pozicija į dešinę ir paskui atimti iš sveikojo skaičiaus 0x5f3759df ir rezultatą įdėti į skaičiaus '''i''' adresą. Skaičius 5f3759df turi 8 hexodecimal skaitmenis. Vienas heksodecimal skaitmuo sudaro 4 bitus.
:5 yra 0101, f yra 1111, 3 yra 0011, 7 yra 0111, 5 yra 0101, 9 yra 1001, d yra 1101 (nes A yra 1010, B yra 1011, C yra 1100), f yra 1111. Todėl
:5F3759DFh = 0101,1111,0011,0111,0101,1001,1101,1111;
:5F3759DFh = 01011111001101110101100111011111.
:Gale raidė h reiškia, kad tai hexodecimal skaičius.
:Ši eilutė:
y = * ( float * ) &i;
:paverčia long int '''i''' į float, o paskui tą float perkelią į '''y'''.
:Toliau daromos Niutono iteracijos:
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
:čia y jau turi apytikslią reikšmę <math>\frac{1}{\sqrt{\text{number}}}.</math>
===Worked example===
As an example, the number <math>x=0.15625</math> can be used to calculate <math display=inline>\frac{1}{\sqrt{x}} \approx 2.52982</math>. The first steps of the algorithm are illustrated below:
0011_1110_0010_0000_0000_0000_0000_0000 Bit pattern of both x and i
0001_1111_0001_0000_0000_0000_0000_0000 Shift right one position: (i >> 1)
0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
0100_0000_0010_0111_0101_1001_1101_1111 The result of 0x5F3759DF - (i >> 1)
Interpreting as [[IEEE 754|IEEE]] 32-bit representation:
0_01111100_01000000000000000000000 1.25 × 2<sup>−3</sup>
0_00111110_00100000000000000000000 1.125 × 2<sup>−65</sup>
0_10111110_01101110101100111011111 1.432430... × 2<sup>63</sup>
0_10000000_01001110101100111011111 1.307430... × 2<sup>1</sup>
Reinterpreting this last bit pattern as a floating point number gives the approximation <math>y=2.61486</math>, which has an error of about 3.4%. After one iteration of Newton's method, the final result is <math>y=2.52549</math>, an error of only 0.17%.
:'''Patikrinimas.'''
:<math>x=0.15625.</math>
:Sing: '''0'''011_1110_0010_0000_0000_0000_0000_0000 (1 bitas).
:Exponent: 0'''011_1110_0'''010_0000_0000_0000_0000_0000 (8 bitai).
:Mantissa: 0011_1110_0'''010_0000_0000_0000_0000_0000''' (23 bitai).
:Exponente yra '''01111100'''. Tai yra
:128*0 + 64*1 + 32*1 + 16*1 + 8*1 + 4*1 + 2*0 + 1*0 = 124.
:124-127 = -3. Tada eksponentė yra '''-3'''. 2^(-3) = 0.125.
:Mantisa yra '''010_0000_0000_0000_0000_0000'''.
:Arba '''101''' = 1 + 0.5*0 + 0.25*1 = '''1.25'''.
:1.25 * 0.125 = 0.15625.
:Visi bitai pastumiami viena bito pozicija į dešinę. Tai tas pats kas padalinti iš dviejų mantisą ir eksponentę. Po pastumimo 32 bitų skaičius tampa:
:0001_1111_0001_0000_0000_0000_0000_0000
:Eksponentė tampa 001_1111_0 arba '''00111110'''. Tai yra 128*0+64*0+32*1+16*1+8*1+4*1+2*1+1*0 = 62.
:62-127 = -65. Padauginus iš 2^(-65) = 2.7105054312137610850186320021749e-20 mantisoje nieko neliks.
:Mantisa tapo 001_0000_0000_0000_0000_0000, kas yra
:1001 = 1 + 0.5*0 + 0.25*0 + 0.125*1 = 1.125.
:1.125 * 2^62 = 5,188,146,770,730,811,392.
:1.125 * 2^(-65) = 3.0493186101154812206459610024467e-20 = 3.0493186101154812206459610024467*10^(-20).
:Vat ir išlindo yla iš maišo. Jei nereikėtų prirašyt priekyje vienetoje mantisoje, o eksponentė butų vienetu didesnė, tai rezultatas būtų
:1001 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 = 0.5 + 0.0625 = 0.5625. (0.5625*2 = 1.125)
:Eksponentė būtų vienetu didesnė (63 arba -64 po atėmimo 127).
:0.5625 * 2^63 = 5,188,146,770,730,811,392.
:0.5625 * 2^(-64) = 3.0493186101154812206459610024467e-20.
:Vadinasi išekstraktuojant atskirai eksponentę ir mantisą ir pastumus mantisos bitus į dešine viena pozicija, į kairiausį bitą turi įeitį vienetas. O prie eksponentės prisidėti vienetas, jeigu norima turėti tą patį skaičių.
:'''Magiškas skaičius reikalingas šiam darbui.'''
:Magiškas skaičius yra
:0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
:Sing: '''0''' (1 bitas).
:Exponent: '''101_1111_0''' (8 bitai).
:Mantissa: '''011_0111_0101_1001_1101_1111''' (23 bitai).
:Eksponentė yra 10111110 = 128 + 0 + 32 + 16 + 8 + 4 + 2 + 0 = 190. Atėmus 127 ji tampa 190-127 = 63.
:Mantisa yra
:011_0111_0101_1001_1101_1111 =
:= 0.5*0 + 0.25*1 + 0.125*1 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.431640625 + 0.0007781982421875 + 0.00078868865966796875 + 0.00000083446502685546875 = 0.43320834636688232421875.
:Viso skaičiaus reikšmė yra:
:1.43320834636688232421875 * 2^190 = 2.2490986495375580205008307141844e+57 = 2.2490986495375580205008307141844 * 10^57.
:Arba:
:1.43320834636688232421875 * 2^63 = 13,219,013,784,867,176,448 = 1.3219013784867176448 * 10^19.
:Kaip sakyta, 32 bitų skaičius pastumtas viena bito pozicija į dešinę atimamas iš magiško skaičiaus kaip atiminėjami sveikieji skaičiai.
:0101_1111_0011_0111_0101_1001_1101_1111-
:0001_1111_0001_0000_0000_0000_0000_0000=
:0100_0000_0010_0111_0101_1001_1101_1111.
:Gautas skaičius 0100_0000_0010_0111_0101_1001_1101_1111.
:Sign bit: '''0'''100_0000_0010_0111_0101_1001_1101_1111.
:Exponent: 0'''100_0000_0'''010_0111_0101_1001_1101_1111.
:Mantissa: 0100_0000_0'''010_0111_0101_1001_1101_1111'''.
:Eksponentė yra '''10000000''' = 128. Atėmus 127, eksponentė yra 128-127 = 1.
:Mantisa yra '''010_0111_0101_1001_1101_1111''' =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.306640625 + 0.0007781982421875 + 0.00001049041748046875 + 0.00000083446502685546875 = 0.30743014812469482421875.
:Gautas skaičius yra:
:1.30743014812469482421875 * 2^1 = 2.6148602962493896484375.
:Arba:
:1.30743014812469482421875 * 2^128 = 4.4489542538766432951275906695431e+38 = 4.4489542538766432951275906695431 * 10^38.
:Toliau tereikia daryti iteracijas pagal formulę:
:y = y * ( threehalfs - ( x2 * y * y ) );
:y_0 = 2.6148602962493896484375. z = x/2 = 0.15625/2 = 0.078125.
:y_1 = y_0 * (1.5 - z * y_0 * y_0) =
:= 2.6148602962493896484375 * (1.5 - 0.078125 * 2.6148602962493896484375 * 2.6148602962493896484375) =
:= '''2.52'''54863388218057175296506574507.
:Tikroji reikšmė:
:1/(0.15625)^0.5 = 2.5298221281347034655991148355462.
:Antra iteracija:
:y_2 = y_1 * (1.5 - z * y_1 * y_1) =
:= 2.5254863388218057175296506574507 * (1.5 - 0.078125 * 2.5254863388218057175296506574507 * 2.5254863388218057175296506574507) =
:= '''2.5298'''109880258619023169570466177. 2.5298109880258619023169570466177
:Trečia iteracija:
:y_3 = y_2 * (1.5 - z * y_2 * y_2) =
:= 2.5298109880258619023169570466177 * (1.5 - 0.078125 * 2.5298109880258619023169570466177^2) =
:= '''2.529822128'''0611201246242590927426. 2.5298221280611201246242590927426
:10 pirmų teisingų skaitmenų (beveik 11).
:Ketvirta iteracija:
:y_4 = y_3 * (1.5 - z * y_3 * y_3) =
:= 2.5298221280611201246242590927426 * (1.5 - 0.078125 * 2.5298221280611201246242590927426^2) =
:= '''2.52982212813470346559'''59044271886.
:21 teisingas skaitmenuo po 4 iteracijų. Tai didesnis tikslumas nei Double precision (64 bits). Maždaug toks tikslumas kaip Double Extended precision (80 bits).
:Darant dalybą ar šaknies traukimą gali prireikti panaikinti eksponentę (padaryti ją lygia nuliui ar vienetui). Tai galima atlikti labai paprastai. Reikia padaryti logišką '''AND''' operaciją skaičiaus
:'''1000,0000,0111,1111,1111,1111,1111,1111''' = '''10000000011111111111111111111111''' su norimu skaičiumi, kaip pvz. '''0011_1110_0010_0000_0000_0000_0000_0000''', kuris yra <math>x=0.15625.</math>
:Po '''AND operacjos''' bus gautas skaičius:
:'''0011_1110_0010_0000_0000_0000_0000_0000''' '''AND'''
:'''1000_0000_0111_1111_1111_1111_1111_1111''' =
:'''0000_0000_0010_0000_0000_0000_0000_0000'''.
:Gauta mantisa 01 = 0.5*0 + 0.25*1 = 0.25. Pridėjus nematomą vienetą priekyje galutinis teisingas skaičius lieka:
:1 + 0.25 = 1.25.
:Eksponentė tampa lygi 0. 2^0 = 1.
:AND operacija veikia tokiu budu:
:1 AND 1 = 1,
:1 AND 0 = 0,
:0 AND 1 = 0,
:0 AND 0 = 0.
:Analogišku budu galima paversti mantisos bitus nuliais ir palikti tik eksponentę, prie kurios paskui galima pridėti arba atimti 8 bitus esančius viena bito pozicija į dešinę nuo kairės. Analogiškai galima panaikinti minuso ženklą su AND operacija. Arba suteikti minuso ženklą pridėjus 32 bitų skaičių '''1000_0000_0000_0000_0000_0000_0000_0000''' prie kokio nors teigiamo 32 bitų skaičiaus (kai pirmas bitas yra 0).
==Kaip FPU galėtų apseit be barrel shifters sudedant skaičius su skirtingom eksponentėm==
:https://en.wikipedia.org/wiki/Barrel_shifter
:Barrel Shifter gali stumti pavyzdžiui 32 bito skaičiaus bitus į kairę ar į dešinę ne per vieną bito poziciją, bet per kelias kaip, kad per dvi bito pozicijas ar per 4 bito pozicijas ar per 8 bito pozicijas.
:Sudedant 32 bitų single precision skaičius su skirtingomis eksponentėmis, mažesnio skaičiaus mantisos bitai turi būti pastumti į dešinę per tiek pozicijų koks yra tu dviejų skaičių eksponenčių skirtumas, atėmus eksponentę mažesnio skaičiaus iš eksponentės didesnio skaičiaus.
:Be barrel shifter dviejų 32 bitų floating point skaičių sudėtį su skirtingomis eksponentėmis galima atlikti pakeitus mažesnio skaičiaus eksponentę į denormal ir padauginus tą mažesnį skaičių iš skaičiaus, kurio fractional part yra vienetas (visa mantisa lygi nuliui, jeigu be nematomo vieneto) o eksponentė yra neigiamas skaičius gautas atėmus didesnio 32 bitų float skaičiaus eksponentę iš mažesnio 32 bitų float skaičiaus eksponentės. Tada šio skaičiaus eksponentė bus neigiama ir ją padauginus iš paversto į denormal mažesniojo skaičiaus, bus gautas rezultatas, kad mažesnio skaičiaus mantisa bus pastumta į dešinę tiek pozicijų koks yra tos neigiamos eksponentės skaičiaus modulis.
:Bet turbūt vis tiek reikės šiftinti mantisos bitus į dešinę, kad paversti mažesnio 32 bitų skaičiaus mantisą kaip pas denormal skaičius pastumta atitinkama bitų skaičių į dešinę, kad paskui pridėti gautą mantisą prie didesnio skaičiaus mantios, ir eksponentę po sudeties palikti didesnio skaičiaus, jeigu ji nepakils vienetu dėl sudeties.
:Tai tiesiog čia kaip veikia skaičių sudėtis su skirtingom eksponentėm. Buvau pagalvojęs, kad galima apseiti be barrel shifter, bet pasirodo, kad ne. Arba reikia šiftinti po vieną bitą (jeigu nėra barrel shifter circuit), kas gali pareikalauti nemažai laiko, jeigu sudedamų skaičių eksponentės stipriai skiriasi.
:Todėl iš esmės sudėtis gali būti ilgesnė nei daugyba net su barrel shifter, nes daugyba su padaryta schema galinčia padauginti mantisas iš vieno ciklo, bus daug greitesnė. Ten iš esmės ta daugybos schema turėtų ir užimti didžiausią dalį FPU. Senesni FPU daugino iš daug ciklų, nes negalėjo sau leisti daug transistorių daugybos transistorių schemai. Maždaug Pentium procesoriai pradėjo dauginti iš vieno ciklo, jei neskaičiuoti visokių įkrovimo, užkrovimo, išėmimo operacijų dviejų dauginamų skaičių.
:'''Update 1.''' Pagal kitą informaciją Pentium (1993 metų) procesorius daugino 3 kartus greičiau nei 387 FPU (apie 1986 metų), nes daugino ir shiftino ne po vieną bitą, o po tris bitus. Buvo sukurtos schemos dauginti iš 0, 1, 2, 3, 4, 5, 6, 7. Nes 3 bitai gali turėti 8 kombinacijas. Tai vietoje 64 ar 66 sudėčių (2 bitai apvalinimui su 80 bitų Extended precision) reikėjo sudėti tik 22 kartus. Ir šiftinimas buvo po 3 bitus į dešinę Su Pentium procesorium.
:Intel procesoriai Nahalem pradėjo dauginti iš vieno ciklo su gerokai sudetingesne dauginimo schema ir su daug daugiau tam reikalingų transistorių. https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
:Nahalem architektura yra 2008 metų. Variantai:
:Core i5-7xx
:Core i7-8xx
:Core i7-9xx Extreme.
:Tai patys pirmi Core procesoriai. Po jų sekė Core 2 procesoriai (2009 metų):
:Core 2 Solo
:Core 2 Duo
:Core 2 Quad
:Core 2 Extreme.
:Dauginimas kart 0 yra dauginimo praleidimas, jei kalbėti apie 64 bit * 64 bit sveikųjų skaičių daugybą.
:Dauginimas kart 2 yra pastumimas skaičiaus viena bito pozicija į kairę.
:Dauginimas kart 3 reikalauja specialios schemos. Arba pastumimas viena bito pozcija į kairę + jis pats, t. y. 2+1.
:Dauginimas kart 4 yra pastumimas skaičiaus dviem bito pozicijom į kairę.
:Dauginimas kart 5 yra dauginimas iš 4 + pats skačius.
:Dauginimas iš 6 yra dauginimas iš 8 + pats skaičius pastumtas į kairę viena bito pozicija ir paverstas į Two's compliment neigiamą skaičių (Padarius XOR su vienetų seka ir pridėjus vienetą). Kitaip tariant dauginimas iš 6 yra 8-2. Arba galima 4+2, tada pastumiamas dviem pozicijom į kairę ir pridedamas jis pats pastumtas viena pozicija į kairę.
:Dauginimas iš 7 yra 8-1. Pastumiamas skaičius į kairę trim bitų pozicijom ir prie jo pridedamas pats tas skaičius paverstas į neigiamą skaičių two's compliment formate. Pastumimas galimas daiktas vyksta per automatišką schemą ir taip pat pridedamas jis pats paverstas per XOR operacija su vienetais ir +1 (per automatišką schemą, kitaip tariant viskas vyksta per vieną ciklą).
:Todėl maksimaliai daugyba su pentium Gali vykti 3 kartus greičiau nei su 387. Bet realus pagreitėjimas gali būti ir 2x. Tai priklauso kaip ten visa ta dauginimo vidinė schema padaryta (iš skaičių nuo 0 iki 7).
:Jei FPU neegzistuoja, o yra reklama ar panašiai, tai kad sudauginti du 64 bitų skaičius, kurių mantisa yra 52 bitai, eksponentė 11 bitų, ir minuso ženklas 1 bitas, tai pagal antro skaičiaus pirmą (kairiausią) mantisos bitą pirmas skaičius arba paliekamas (jei tas bitas 1) arba vietoj jo rašomas 0 (ignoruojamas), jei tas bitas 0.
:Toliau, pagal antrą nuo kairės bitą antro skaičiaus, pirmas skaičius arba paliekamas, jei tas bitas 1 arba ignoruojamas, jei tas bitas 0. Paskui jei tas bitas buvo 1, tai pirmas skaičius pastumiamas viena bito pozicija į dešinę su shift right instrukcija ir pridedamas prie to nepastumto pirmo skaičiaus.
:Iš pradžiu pridedami 32 dešiniausi bitai nepastumto ir pastumto pirmo skaičiaus. Jeigu buvo carry iš jų sudeties. Tai sudedant kairiasias 32 bitų tas dvi dalis dar pridedamas carry, naudojant instrukciją '''ADD with Carry'''.
:Dabar yra 64 bitų skaičius saugomas dviejuose 32 bitų RAM adresuose.
:Toliau pagal antro dauginamo skaičiaus trečia bitą, pirmas skaičius pastumiamas trim pozicijom į dešinę ir pridedamas prie gauto 64 bito skaičiaus, jei tas antro dauginamo skaičiaus trečias bitas 1 ir nepridedamas, jei tas trečias bitas 0.
:Kiekvieną kartą nereikia šiftinti iki 52 kartų į dešinę. Užtenka išsisaugot kas kart pašiftintą viena poziciją pirmą 64 bitų skaičių (šiftinama du kartus, vieną kart kairiausi 32 bitai ir antrą kart dešiniausi 32 bitai).
:Tai iš viso reikia 52 šiftinimų *2 (nes šiftinama į dešine po 32 bitus) ir 52 sudėčių *2 (nes sudedama po 32 bitus). Tai iš viso reikia 52*2 + 52*2 = 104+104 = 208 operacijos. Bet kartais sudėties operacijos gali būti praleidžiamos, tai vidutiniškai reikia ne 104 sudeties opeacijų (32 bitų), bet 52, nes vidutiniškai pusė bitų antro skaičiaus nuliai ir pusė vienetai. Tai tada vidutiniškai reikia 104+52 = 156 operacijų, kad sudauginti du 64 bitų double precision skaičius.
:Tada realus procesoriaus dažnis vietoje 5 GHz yra apie 156*5 = 780 GHz.
:Single precision (32 bitų) su 23 bitų mantisą reikėtų 46 šift ir 46 add operacijų. Iš viso 92 operacijų maksimum. Bet jeigu vidutiniškai yra 23 sudeties operacijos, tai tada apie 23+46 = 69 opracijų. Free Pascal koduose galima sakyti 4 GHz procesorius daugina 64 bitų dauble precision skaičius naudodamas 4 ciklus ir tai taip pat įskaitant visokius įkrovimus iškraovimus į/iš FPU į/iš RAM atminties. Tai dėl to 780 GHz dažnis nustatytas gana tiksliai ir nereikia jo dauginti iš 4.
:Atradau tokią gudrybę:
:12345 * 12345 = 152399025
:ir
:1.2345 * 1.2345 = 1.52399025.
:Arba
:12345 * 14789 = 182570205,
:1.2345 * 1.4789 = 1.82570205.
:Tai iš esmės galima dauginti single precision mantisas kaip sveikuosius skaičius, o paskui tik padėti kablelį kur reikia (gauto rezultatą įterpti į float mantisą be priekinio vieneto).
:Tikrinimas:
:123456789123456789 * 123456789123456789 = 1.5241578780673678515622620750191e+34
:1.23456789 * 1.23456789 = '''1.52415787'''50190521.
:1.23456789123456789 * 1.23456789123456789 = 1.5241578780673678515622620750191.
:Visgi tikslumas nukentėjo - gudrybė neveikia.
:Antras tikrinimas:
:1.23456789123456 * 1.23456789123456 = 1.52415787806734,83700809383936.
:1.2345678 * 1.2345678 = '''1.524157'''6,5279684.
:Neveikia gudrybė. Dauginant "trumpesnius" skaičius ir tikslumas mažesnis.
:Bet vis tiek nušiftinus minėtą pirmą 64 bitų skaičių jau 32 bitų pozicijom į dešinę, to pirmo skaičiaus 32 dešiniausius bitus jau galima nešiftini ir neatlikinėti su jais sudeties operacijų. Taip dar sutaupant trečdalį laiko (nes pusė laiko dirbama su 64 bitais, o paskui likusią pusę laiko šiftinami ir sudedami tik 32 bitai prie dešinios 32 bitų dalies pirmojo skaičiaus). Todėl sutaupoma 1/3 operacijų. Iš to gaunasi, kad iš viso reikia ne 156 ciklų (dviejų double precision skaičių daugybai), o
:156 - 156/3 = 156 - 52 = 104 ciklų. Vadinasi tada procesoriaus 5GHz dažnis būtų ne 780 GHz, o 5*104 = 520 GHz.
:Tai yra, iš pradžiu loop šiftina ir sudeda 32 kartus kaip su 64 bitais, o paskui pereinama prie antros loop, kur šiftinami ir sudedami 32 bitai.
:Arba, kad nedaryti loop kas irgi naudoja laiką, nes dviem instrukcijom daugiau (decrement register and jump to loop begining if loop register flag not zero). Tai geriau surašyti visas instrukcijas iš eilės kiek kartų viską daryt. Tai užims daugiau RAM atminties, bet veiks greičiau, nes nereikės palaikyti loop vykdymo. Tai užims salyginai mažai RAM - iki vieno kilobaito. Bet gali veikti 1.5 karto ar net 2 kartus greičiau. Ir dar neužims vieno registro kaip counterio skaičiavimui kiek iteracijų atlikta. O jeigu procesorius dar ir negali skaičiuoti per tam skirtą registrą kaip to negali intel 8080, tai reikės dar perkraudinėti į akumuliatorių counter'inimo skaičių, kad pažiurėti ar tas skaičius ne nulis ir neįjungs Zero flag (kai bus nulis). Nors šiaip intel 8080 turi CMP instrukciją (CMP, Compare register with A). Tai tas pats kas atimti kitą registrą ar operand'ą iš atminties iš registro A (akumuliatoriaus), tik rezultatas neįdedamas į akumuliatorių. Tai iš esmės kol count koks nors registras po decrement instrukcijos nėra nulis, tol compare su registru A neįjungs Zero flag. Bet tada išeina į registrą A kiekvieną kartą reikia krauti sveiką skaičių 0 po kiekvienos šiftinimo ir sudeties operacijos.
:Ką tik pažiūrėjau [https://bitsavers.trailing-edge.com/components/intel/MCS80/98-153B_Intel_8080_Microcomputer_Systems_Users_Manual_197509.pdf intel 8080 manual] (51 psl.), tai visgi Zero flag yra '''set''', kai pasirinktas registras po ''decrement register'' instrukcijos tapo lygus nuliui. Tai praktiškai nereikia naujiems procesoriams jokių geresnių instrukcijų išeina. Tik FPU dauginimo ir sudeties bei atimties greitų instrukcijų užtenka. Nes iš mano samprotavimų, viską darant per mikrokodą, viskas veiks praktiškai tokiu pačiu greičiu kaip viską darant per RAM, tik truputi mažiau RAM ilgos instrukcijos užims kaip integer multiply pas intel 8088 per mikrokodą. Vis tiek daugiausiai vietos RAM užima paveiksliukai, teksturos ir kita data, o ne pačios instrukcijos, tai šiuolaikiniams laikams tai neaktualu. O RAM parinkus vieną ROW, ateina iš tos parinktos ROW bitai iš visų Column į kelis tūkstančius SRAM bitų registrų. Paskui iš tų SRAM (static RAM) registrų, kurie dažniausiai yra 4 bitų viename RAM čipe, atgal gražinami nutekėję tos DRAM row visos stulpelių (column) bitų reikšmės atgal į tas pačias vietas ir Row atjungiama, o tie nutekėję bitai į SRAM lasteles (registrus) niekur nedingsta iki tol kol nekreipiamasi į kitą Row. Todėl paskui procesorius ima informaciją ne iš DRAM lastelių, o iš SRAM lastelių. Ir įrašo į SRAM ląsteles, o parinkus naują ROW, tą informacija iš kelių tukstančių SRAM lastelių (registrų) per kelis ciklus grąžinama į visas column tos Row. Instrukcijos vykdomos dažniausiai nuosekliai ir dažniausiai peršokama ribose tos pačios DRAM ROW. Todėl vis naudojami tie patys SRAM registrai nutekintų bitų DRAM stulpelių iš tos pačios ROW. Dėl to DRAM praktiškai gali veikti greičiau nei bet koks cache ir naudoti daug mažiau energijos už cache, kuriam reikia milijonų SRAM lastelių, o ne tukstančių. Tik didesnis atstumas tarp CPU ir DRAM sulėtiną informacijos keliavimą iš DRAM esančių SRAM lastelių dėl šviesos greičio limito iš dalies. Tai, kad cache atmintis gaunasi visiškai beprasmiška, jei tinkamai panaudoti tą DRAM veikimą su SRAM registrais viduje. Kas teoriškai turėtų taip ir būti.
:Jeigu 5 GHz procesorius iš tikro veikia 500 GHz dažniu, o šviesos greitis nėra sumeluotas, tai 1 metrą šviesą gali nueiti 300 milijonų kartų arba 300 MHz. Atstumas tarp procesoriaus ir RAM yra apie 10 cm, tai tada RAM dažnis max gali buti apie 3 GHz. Bet jeigu procesorius veikia 500 GHz, nes jos pagrindinė dalis yra labai sumažinta dėl naujesnės litografijos, o pats CPU base core užima ne daugiau nei milijoną tranzistorių, tai išeitų, kad nespėtų imti iš RAM atminties informaciją CPU dėl šviesos greičio abribojimu (dėl atstumo tarp CPU ir RAM). Tai šita dalyką galima būtų išspręsti, jei visos programos ir programavimo kalbos naudotų 64 bitų ar 32 bitų skaičių daugybai tam skirtą RAM adresą vieno kilobaito ar mažiau, kuris visada būtų adresuojamas ne per DRAM, bet per SRAM esančią pačiame mikroprocesoriuje. Pavyzdžiui tos SRAM kilobaitas galėtų būti pačioje RAM pradžioje, tam skirus 256 adresus saugančius po 32 bitus. Tai tiesiog pirmi 8 (ar 10, nes 2 bitai reiškia 4 baitus..., o procesorius ne 8, o 32 bitų...) kairiausių bitų būtų nukreipti į tą SRAM atminti, o like 24 (ar 22) bitai einantys į SRAM atmintį būtų nuliai. Jei nors vienas iš dešiniausių 24 (ar 22 bitų) būtų ne nulis, tai Adresas iš CPU nukeliautų ne į vidinę SRAM atmintį, o į išorinę lėtesnę DRAM atmintį. Tai tiesiog prieš pradedant daugybą, visos programos galėtų naudoti tą SRAM atmintį pernešant ten du dauginamuosius į atitinkamas vietas, kuriuos jau supa daugybos kodas... Ir su 500 GHz mažyčiu procesorium (be visokių nereikalingų kvailų instrukcijų) galėtų užtekti šviesos greičio būtent tokiam dideliam dažniui, kad atlikti double ar single precision daugybą. Intel iš tikro sudetingos instrukcijos asembleryje galėtų būti kaip aukštesnio lygio programavimo kalba, kuri paskui visas tas instrukcijas paverčia į 8080 CPU tipo instrukcijas, o FPU daugybą perkelia į tam skirtą SRAM atminties vietą, o rezultatą gražina lyg niekur nieko, ten kur ištransliavo asembleris... O vėliau ir aukštesnio lygio programavimo kalbos kaip C ir Pascal.
:Ne. Visgi skaičiavimo trukmė sumažėja ne trečdaliu, o ketvirtadaliu. Tuomet 5 GHz procesoriaus dažnis būtų ne 520 GHz, o
:(156 - 156/4) * 5 (GHz) = (156 - 39) * 5 = 585 GHz.
:'''Update 2.''' Dar yra toks variantas, kad 5 GHz procesorius dirba apie 50 GHz dažniu ir neturi FPU. Iš esmės rodos kodus kaip daugybos ir sudeties benchmarkai su viena daugyba ir/ar sudėtimi buvo skaičiuojami per apie 25-30 sekundžių pirmą kartą su 1 milijardu iteracijų. Jei viena operacija užima 4 ciklus ant 4 GHz procesoriaus tai viena daugyba ir sudėtis užimtu 8 ciklus. O 25-30 sekundžių yra 100-120 ciklų. Tai 120/8 = 15 ciklų daugybai ir sudečiai, jei tik daugybai tai 30 ciklų. Bet buvau ir be daugybos tik, kur yra sudetis ir vis tiek pat kaip su daugyba. Be to ten maži skaičiai iš pradžiu ėjo, tai juos galėjo greičiau daugint. Tik kur nebuvo daugybos, kodėl irgi 25-30 sekundžių darė sudeties operacijas milijardą kartų? Tai gal vistiek ten 20-25 sekundes pirmą kartą kompiliavimas užima ir FPU iš tikro yra. Arba sudetis yra ilga nes sudedami skaičiai su skirtingom eksponentėm ir reikalingas šiftinimas per kelis ar keliasdešimt bitų. Tai visgi gali būti, kad Tikrasis CPU dažnis yra ne 4 ar 5 GHz, o apie 40-50 GHz ar net 100 GHz. Jeigu FPU neegzistuoja. Tada kvadratinę šaknį ir dalyba tikrai ne greičiau bus su Niutono iteracijomis daryti, o taikyti kitus metodus. Dalyba tai ir taip bus beveik toks pat greitis kaip daugyba (jei FPU nėra). O kvadratinei šakniai kažką reik sugalvot.
:Vienintelis įrodymas, kur sinuso Teiloro eilutė pirmą kartą labai greit skaičiuojama. Sudedami sinusai su įvairiom reikšmėm. Problema ta, kad negalima patikrinti teisingumo tų visų ilgo kodo skaičiavimu, gal kažką nukerta ten... Kiti kodai kaip parabolės ilgis ar integralų sumos duoda patikrinamą per integralus rezultatą, bet jie ir skaičiuojami pirmą kartą ilgiau nei antrą kartą.
:Tai geriau kur nereikia taikyti trumpesnes daugybas su tuo 50 GHz procesorium, ką tuomet išeitų žaidimų kurėjai ir game engines kūrėjai ir daro.
:Intel387TM SX MATH COPROCESSOR
:https://datasheets.chipdb.org/Intel/x86/387/datashts/24022509.PDF
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240225-009.pdf
:Intel 387 DX MATH COPROCESSOR
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005.pdf
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005_alt.pdf
:https://ardent-tool.com/CPU/Docs_Intel.html#387
:Intel 386
:www.digchip.com/datasheets/download_datasheet.php?id=59667&part-number=386DX
:https://ardent-tool.com/CPU/docs/Intel/386/datasheets/231630-011.pdf
:https://www.dosdays.co.uk/media/intel/1986_80386_Hardware_Reference_Manual.pdf
:https://www.bitsavers.org/components/intel/80386/230985-001_80386_Programmers_Reference_Manual_1986.pdf
:https://pdos.csail.mit.edu/6.828/2018/readings/i386.pdf
:https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf
===Intel 386 procesoriaus suderinamumas su 286 ir 8086===
:Skaitant čia https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf 129 puslapyje ir ankstesnius kitus puslapius, galima suprasti, kad intel 386 CPU iš tikro niekaip neoptimizuoja apatinės ir viršutinės dalies (16 bitų) RAM adreso saugančio 32 bitus. Pasakyta, kad jeigu dedama apatinė 16 bitų dalis, tai aktyvuojamos kojelės BE0 ir BE1, atsakančios už perdavimą apatinės 16 bitų dalies į 32 bitų RAM vieną adresą. Jeigu aktyvuojamos kojelės BE2 ir BE3, tai viršutiniai 16 bitų dedami ir į apatinę ir į viršutinę dalį 32 bitų RAM vieno adreso. Amerikiečiams labai svarbu būti konkurencingams.
:Tai jeigu pernešti visą intel 286 procesoriaus kodą į 386 procesorių, tai jeigu naudoti tik 32 bitų registrus (EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP) ir 286 procesoriaus 16 bitų kodą įrašyti tik į apatinius 16 bitų visuose adresuose, tai tos visos 16 bitų programos neveiks, nes viršutiniai 16 bitų bus nuliai kai bus pridedama neigiama displacement '''two's complement''' formate. O jeigu visi viršutinai 16 bitų vieno 32 bitų RAM adreso bus vienetai, kad veiktų neigiama displacement, tai tada neveiks teigiama displacement, nes pas teigiama displacement reikia, kad visi viršutiniai 16 bitų būtų nuliai.
:Dar gali būti, kad intel 386 ir intel 286 procesoriai naudojai visiškai skirtingus opcode'us. Su '''REP'''eat prefixu kai kurių instrukcijų 386 procesoriuje nėra, kurios yra 8086 procesorioju. Vietoje to, 386 naudoja REPeat instrukcijas kaip atskirą opcode'ą nuo ne Repeat instrukcijų. Kitų opcode'ų nelyginau, norintys gali palyginti ir patikrinti kas kaip suderinta. Bet manau ten visi opkodai skirtingi pas 386 nuo 286 ir 8086.
:Gali būti, kad kažkaip dedant į 386 procesoriaus 16 bitų registrus (AX, BX, CX, DX, DI, SI, BP, SP) ir panaudojant kojelę BS16 ant 386 CPU, visi 16 bitų kodai iš 286/8086 veiks su 386 procesorium. Tada 386 procesorius visada skaitys tik iš minėtų 16 bitų registrų (AX, BX, CX, DX, DI, SI, BP, SP). Bet tada tai reiškia, kad kai aktyvuota BS16 kojelę, 386 pradeda suprasti 286 procesoriaus opcode'us iš žemutinių 16 bitų RAM adreso 32 bitų. Į viršutinius 16 bitų to paties RAM adreso tada išeina niekas nededama. Aš tai sakyčiau, kad intel inžinieriai nesivargino palaikyti 286 procesoriaus opkodų, o teoretikams kuriantiems instrukcijas ir datasheets ir user's manual'us pasakė, kad viskas veiks maksimaliai optimaliai ir optimizuotai. Todėl greičiausiai reikia perrašinėti 286 visas programas 386 procesoriams arba kai buvo kuriamos programos, buvo kuriamos iškart ir 386 ir 286 procesoriui, o senesnės programos 8086 tuomet arba neveik su 386 CPU arba buvo per kokį nors asemblerį konvertuojamos į 386 procesoriaus kodą paleidus, kas turėdavo atimti laiko paleidimui. Kad intel 8080 procesoriaus opkodai neveikia su 8088/8086 pasakyta wikipedijoje. Todėl nėra prasmės iš 8080 registrų, bet gal jie duoda pasirnkti baitus (A, B, C, D, E, H, L registrai). Arba ten tik girti suderinamumą su 8080 ir 286/8088, bet iš tikro tuomet yra tik 32 bitų 8 registrai EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP. Be to 8086 galėjo būti neveikiantis procesorius, dėl to IBM pasirinko 8088. 8086 deda į RAM adresą 16 bitų, o 8088 į vieną RAM adresą deda tik 8 bitus. O jeigu skaičius yra 16 bitų, tada deda į du RAM adresus kaip intel 8080 CPU.
[https://lt.wikibooks.org/wiki/Wikibooks:Knygos/High_Level_Shading_Language High Level Shading Language]
21vpig8q3vze373ve0voppulk7vei9b
59027
59026
2026-09-10T09:19:47Z
Paraboloid
1294
/* Intel 386 procesoriaus suderinamumas su 286 ir 8086 */
59027
wikitext
text/x-wiki
==Dešimtainių skaičių vertimas į dvejetainius skaičius==
[https://www.google.com/search?client=opera&q=single+precision&sourceid=opera&ie=UTF-8&oe=UTF-8&udm=50&fbs=ABfTbFVyMZGZf1hfvX9uKjN_-G8c4u0nXx4bEIpwm1lnNH832VTJOOCxW_fyN-Q_ezyf8gKjm3rhh_G8jdZ2Q6tji4z8Mva6cfvBj5rpPch2IC955IbzrbwHf6tkbFbd-nuFdbKth9Y-58t5LtnYgTA4TWDFYCUNl4d7F9XBAwiEq2T2c7SNgrvMxYiRBXL4fnQnnTOewx28&aep=10&ntc=1&sxsrf=APpeQnvZo3fX2PwO881uSdWsoRELP18NhQ%3A1788348274309&mstk=AUtExfAJ0Kp9BqT3EJbzHOz3MHMalnyXkoOTLvgho5ZQ9lzjrnmmUFCgux_RVe7bouRlyBSGpkUcLpvfZ39N45TUoor6TyyEW7_PpE7eOwP-kU7IFxE6monk-HtsDXkDu7g362AN4NTW3oRjfwn9OtD0BIj2r4QAVauMhDPCIAiWgLimDuate6cf2o6bB5R-6-vzcCqbCju1KJQTdR4HSF9puZ_HCG1hXQ8QaCIqD1LJ_HWonNnZGma6Biu3I1wWFPy6esLnfaKpIX6VFWZsArfQN97XGipwkH7FLKCyZiev5rZBrhE8KincMthP7B9AJDugKLy8qJfORQ7PQR2GslAaCLnkechjBCnXfrmSohwJskbEWU9db6Q279uO4dGu0eI2YUvpEwoQUqkbREqJqrGpJQWFy7WoTzI93_o_2I61PU1FTiuGq-4AVN7erT0WmuWizHDEE2XsTes&aioh=3&csuir=1&atvm=2&mtid=dQeYaumuDL2Ixc8Px6Ts0QE DI atsakymas]
:Single-precision is a 32-bit computer number format that stores fractional and large numbers using a sign bit, an exponent, and a mantissa. Structure of Single-Precision (Binary32) According to Wikipedia, “Single-precision floating-point format (sometimes called FP32, float32, or float) is a computer number format, usually occupying 32 bits in computer memory.” The 32 bits are divided into three specific parts:
*Sign bit (1 bit): Determines if the number is positive (0) or negative (1).
*Biased exponent (8 bits): Stores the magnitude with an excess-127 bias, giving an actual exponent range of -126 to +127.
*Mantissa/Significand (23 explicitly stored bits): Represents the precision digits of the number, with an implicit leading 1 for normalized values.
:Precision and Applications. Decimal Precision: Provides about 7 to 8 decimal digits of accuracy. Most users on StackOverflow agree that “Single precision typically offers 7-8 decimal digits of precision.”
:'''Dešimtainių skaičių vertimas į dvejetainius.'''
:Step-by-Step Conversion Framework.
*Step 1: Determine the Sign Bit (1 bit). If the number is positive, the sign bit is 0. If the number is negative, the sign bit is 1. Example (-12.625): It is negative, so the sign bit is 1.
*Step 2: Convert the Absolute Value to Binary. Separate the number into its integer and fractional parts, convert both to binary, and combine them.
:Integer part '''12''': (12/2 = 6) ('''rem'''ainder 0), (6/2 = 3) (rem 0), (3/2 = 1) (rem 1), (1/2 = 0) (rem 1) <math>\rightarrow </math> '''1100'''.
:Fractional part '''0.625''': (0.625 * 2 = 1.25) (1), (0.25 * 2 = 0.5) (0), (0.5 * 2 = 1.0) (1) <math>\rightarrow </math> '''101'''.
:Example Combined: '''12.625''' = '''1100.101'''.
*Step 3: Normalize the Binary Number. Shift the binary point so that exactly one non-zero digit (which will always be 1) remains to its left. Count how many places you moved the point. This count is your exponent (E).
:Move the point 3 places to the left: '''1100.101''' <math>\rightarrow </math> '''1.100101'''.
:Example Exponent: Because we moved it 3 places, E = 3.
*Step 4: Calculate the Biased Exponent (8 bits) IEEE 754 single-precision uses a bias of 127. Add your exponent E to this bias, and convert the resulting integer to an 8-bit binary number.
:Calculation: 127 + 3 = 130.
:Convert 130 to 8-bit binary: '''130''' = '''10000010'''.
*Step 5: Extract the Mantissa/Significand (23 bits).
:Take the fractional bits from your normalized number (everything to the right of the binary point). Drop the leading 1. If the bits are fewer than 23, pad the right side with zeros until you reach exactly 23 bits.
:Normalized bits: 1.100101 <math>\rightarrow </math> Drop the ''1.'' (atmesti kairiausią vienetą ir tašką po jo) to get '''100101'''.
:Example Pad to 23 bits: 10010100000000000000000.
:'''Final Assembly.'''
:Combine the three components in order: Sign (1 bit) + Exponent (8 bits) + Mantissa (23 bits).
:Component_____________Bits______________Value for -12.625_______
:Sign____________________1_______________________1______________
:Exponent________________8____________________10000010__________
:Mantissa________________23______________10010100000000000000000
*Final 32-bit Binary String: '''11000001010010100000000000000000'''.
*Hexadecimal Representation: '''C1450000'''.
:Sign: '''1'''1000001010001010000000000000000.
:Exponent: 1'''10000010'''10001010000000000000000.
:Mantissa: 110000010'''10010100000000000000000'''.
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Mantissa: 100101 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 0.578125.
:8 * 0.578125 = 4.625.
:8 * 1 = 8.
:8 + 4.625 = 12.625.
:Reikia gražinti paimtą vienetą priekyje ('''12.625''' = '''1100.101'''; '''1100.101''' <math>\rightarrow </math> '''1.100101'''):
:8 * (1 + 0.578125) = 8 * 1.578125 = 12.625.
:-12.625 = -1.2625 * 10^1.
:Čia mantisa 0.2625, o ekspontentė 1 dešimtainėje sistemoje. Skačių 0.2625 reikia kaskart dauginti iš 2.
:0.2625*2 = 0.525 (ne vienetas, reiškia pirmas skaitmuo 0),
:0.525*2 = 1.05 (vienetas, reiškia antras skaitmuo 1),
:0.05*2 = 0.1 (ne vienetas, reiškia trečias skaitmuo 0),
:0.1*2 = 0.2 (ne vienetas, reiškia ketvirtas skaitmuo 0),
:0.2*2 = 0.4 (ne vienetas, reiškia penktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia šeštas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia septintas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia aštuntas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia devintas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia dešimtas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia vienuoliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia dviliktas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia tryliktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia keturioliktas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia penkioliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia šešioliktas skaitmuo 1).
:Tada galutinis skaičius '''0.2625''' yra toks:
:0.0100001100110011.
:Kad gauti didesnį tikslumą, procesą reikia kartoti begalo arba iki reikiamo tikslumo.
:Skaičiau '''0.2625''' reikšmė yra:
:0.0100001100110011 =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*0 + 0.015625*0 + 0.0078125*1 + 0.00390625*1 + 0.001953125*0 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 =
:= 0.26171875 + 0.0007781982421875 = '''0.26249'''69482421875.
:Taigi, 0.26249 labai panašus į 0.2625.
:Vadinasi '''1.2625''' dvejetainėje sistemoje apytiksliai yra '''1.0100001100110011'''.
:Eksponentę 10 reikia versti taip:
:10 = 8 + 2. Tada 10 dvejetainėje sistemoje yra 00001010 (1*0 + 2*1 + 4*0 + 8*1 + 0 + 0 + 0 + 0 = 10).
:Bet eksponentė single precision formate gali būti tik, 0, 1, 2, 4, 8, 16, 32, 64, 128 ir taip toliau.
:Arba reikia daryti taip:
:10/2 = 5 (rem 0),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0)
:1/2 = 0.5 (rem 1).
:Va štai šitaip ir gaunamas skaičius 10. Tai yra 1010.
:Skaičius 1010 pakeičiamas skaičiu 1.010 su eksponente 3 (2^3 = 8).
:Tada skaičius 1.010 iškoduojamas taip:
:1 + 0.5*0 + 0.25*1 + 0.125*0 = 1.25.
:Padauginama iš dviejų pakelta eksponente 3:
:1.25 * 2^3 = 1.25 * 8 = 10.
:Tuomet
:1.2625 * 10 = 12.625.
:'''Grįžimas prie pavyzdžio.'''
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Skaičiaus 1100.101 kablelį galima pastumti 4 pozicijom į kairę ir eksponentę duoti 4. Mantisa tada yra 0.1100101, o eksponentė yra 4.
:0.5*1 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*0 + 0.0078125*1 = 0.7890625.
:2^4 * 0.7890625 = 16 * 0.7890625 = '''12.625'''.
:Bandomas kitas skaičius 24:
:24/2 = 12 (rem 0),
:12/2 = 6 (rem 0),
:6/2 = 3 (rem 0),
:3/2 = 1 (rem 1),
:1/2 = 0.5 (rem 1).
:Reiškia 24 yra 11000 (16*1 + 8*1 + 4*0 + 2*0 + 1*0 = 24).
:Bandomas kitas skaičius 19:
:19/2 = 9 (rem 1),
:9/2 = 4 (rem 1),
:4/2 = 2 (rem 0),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 19 yra 10011 (16*1 + 8*0 + 4*0 + 2*1 + 1*1 = 19).
:Bandomas skaičius 22:
:22/2 = 11 (rem 0),
:11/2 = 5 (rem 1),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 22 yra 10110 (16*1 + 8*0 + 4*1 + 2*1 + 1*0 = 16+4+2 = 22).
:Trumpas paaiškinimas kodėl eksponentė saugoma formate nuo 1 iki 254, pridedant 127 prie -126 ir iki 127. Tai greičiausia yra todėl, kad sudedant single precision skaičių tokiame formate kokiame jis yra, jo reiikšmė gali būti gana tiksliai traktuojama kaip sveikojo skaičiaus reikšmė. Su kai kuriais pakeitimais.
:'''-12.625''' yra '''11000001010010100000000000000000'''.
:Pridėjus 127 ('''01111111''') prie eksponentės 3 ('''00000011''') gauname 130 ('''10000010'''):
:'''00000011'''+
:'''01111111'''=
:'''10000010'''.
:Vadinasi tada skaičius '''-12.625''' tampa
:Mantissa: 1100101 = 1 + 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 1.578125;
:2^130 = 1.3611294676837538538534984297271e+39 = 1.3611294676837538538534984297271 * 10^39.
:1.578125 * 2^130 = 2.148032441188424050612552209413e+39.
:O sveikasis skaičius '''11000001010010100000000000000000''' be minuso yra '''01000001010010100000000000000000''' ir užrašomas taip:
:1*0 + 2*0 + 4*0 + 8*0 + 16*0 + 32*0 + 64*0 + 128*0 + 256*0 + 512*0 + 1024*0 +
:+ 2048*0 + 4096*0 + 8192*0 + 16,384*0 + 32,768*0 + 65,536*0 + 131,072*0 +
:+ 262,144*1 + 524,288*0 + 1,048,576*1 + 2,097,152*0 + 4,194,304*0 + 8,388,608*1 + 16,777,216*0 + 33,554,432*1 +
:+ 67,108,864*0 + 134,217,728*0 + 268,435,456*0 + 536,870,912*0 + 1,073,741,824*0 +
:+ 2,147,483,648*1 + 4,294,967,296*0 =
:= 0 + 0 + 43,253,760 + 0 + 2,147,483,648 = 2,190,737,408.
:Kažka supainiojau. Atrodė, kad eksponentė beveik tas pats kas didesni skaičiai priekyje.
==Skaičiavimas inverse square root su C kodu==
:Čia https://en.wikipedia.org/wiki/Fast_inverse_square_root aiškinama kaip naudojant sveikuosius skaičius galima apsakičiuoti <math>\frac{1}{\sqrt{x}}.</math>
:"The fast inverse square generates a good approximation through integer operations by adding and subtracting the integer form of floating-point numbers, and taking a square root by dividing by two (which is just a right-shift)."
:Iš čia https://lt.wikibooks.org/wiki/Sekos_riba#Greitas_šaknies_iš_skaičiaus_a_traukimo_būdas gauta formulė
:<math>y_{n+1} = \frac{y_{n}\left(3-xy_n^2\right)}{2}. \quad (4)</math>
:<math>y_n\approx \frac{1}{\sqrt{x}}.</math>
:Ją dar galima pagreitint, pakeitus z = x/2. Tada ši formulė tampa tokia:
:<math>y_{n+1} = y_{n}\left(1.5 - zy_n^2\right). \quad (4.1)</math>
:Programavimo kalbos C kodas skaičiuoti <math>\frac{1}{\sqrt{x}}</math> yra toks:
<syntaxhighlight lang="c">
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
</syntaxhighlight>
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Toliau eilutė
i = 0x5f3759df - ( i >> 1 );
:reiškia pastumti sveikąjį skaičių '''i''' viena pozicija į dešinę ir paskui atimti iš sveikojo skaičiaus 0x5f3759df ir rezultatą įdėti į skaičiaus '''i''' adresą. Skaičius 5f3759df turi 8 hexodecimal skaitmenis. Vienas heksodecimal skaitmuo sudaro 4 bitus.
:5 yra 0101, f yra 1111, 3 yra 0011, 7 yra 0111, 5 yra 0101, 9 yra 1001, d yra 1101 (nes A yra 1010, B yra 1011, C yra 1100), f yra 1111. Todėl
:5F3759DFh = 0101,1111,0011,0111,0101,1001,1101,1111;
:5F3759DFh = 01011111001101110101100111011111.
:Gale raidė h reiškia, kad tai hexodecimal skaičius.
:Ši eilutė:
y = * ( float * ) &i;
:paverčia long int '''i''' į float, o paskui tą float perkelią į '''y'''.
:Toliau daromos Niutono iteracijos:
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
:čia y jau turi apytikslią reikšmę <math>\frac{1}{\sqrt{\text{number}}}.</math>
===Worked example===
As an example, the number <math>x=0.15625</math> can be used to calculate <math display=inline>\frac{1}{\sqrt{x}} \approx 2.52982</math>. The first steps of the algorithm are illustrated below:
0011_1110_0010_0000_0000_0000_0000_0000 Bit pattern of both x and i
0001_1111_0001_0000_0000_0000_0000_0000 Shift right one position: (i >> 1)
0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
0100_0000_0010_0111_0101_1001_1101_1111 The result of 0x5F3759DF - (i >> 1)
Interpreting as [[IEEE 754|IEEE]] 32-bit representation:
0_01111100_01000000000000000000000 1.25 × 2<sup>−3</sup>
0_00111110_00100000000000000000000 1.125 × 2<sup>−65</sup>
0_10111110_01101110101100111011111 1.432430... × 2<sup>63</sup>
0_10000000_01001110101100111011111 1.307430... × 2<sup>1</sup>
Reinterpreting this last bit pattern as a floating point number gives the approximation <math>y=2.61486</math>, which has an error of about 3.4%. After one iteration of Newton's method, the final result is <math>y=2.52549</math>, an error of only 0.17%.
:'''Patikrinimas.'''
:<math>x=0.15625.</math>
:Sing: '''0'''011_1110_0010_0000_0000_0000_0000_0000 (1 bitas).
:Exponent: 0'''011_1110_0'''010_0000_0000_0000_0000_0000 (8 bitai).
:Mantissa: 0011_1110_0'''010_0000_0000_0000_0000_0000''' (23 bitai).
:Exponente yra '''01111100'''. Tai yra
:128*0 + 64*1 + 32*1 + 16*1 + 8*1 + 4*1 + 2*0 + 1*0 = 124.
:124-127 = -3. Tada eksponentė yra '''-3'''. 2^(-3) = 0.125.
:Mantisa yra '''010_0000_0000_0000_0000_0000'''.
:Arba '''101''' = 1 + 0.5*0 + 0.25*1 = '''1.25'''.
:1.25 * 0.125 = 0.15625.
:Visi bitai pastumiami viena bito pozicija į dešinę. Tai tas pats kas padalinti iš dviejų mantisą ir eksponentę. Po pastumimo 32 bitų skaičius tampa:
:0001_1111_0001_0000_0000_0000_0000_0000
:Eksponentė tampa 001_1111_0 arba '''00111110'''. Tai yra 128*0+64*0+32*1+16*1+8*1+4*1+2*1+1*0 = 62.
:62-127 = -65. Padauginus iš 2^(-65) = 2.7105054312137610850186320021749e-20 mantisoje nieko neliks.
:Mantisa tapo 001_0000_0000_0000_0000_0000, kas yra
:1001 = 1 + 0.5*0 + 0.25*0 + 0.125*1 = 1.125.
:1.125 * 2^62 = 5,188,146,770,730,811,392.
:1.125 * 2^(-65) = 3.0493186101154812206459610024467e-20 = 3.0493186101154812206459610024467*10^(-20).
:Vat ir išlindo yla iš maišo. Jei nereikėtų prirašyt priekyje vienetoje mantisoje, o eksponentė butų vienetu didesnė, tai rezultatas būtų
:1001 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 = 0.5 + 0.0625 = 0.5625. (0.5625*2 = 1.125)
:Eksponentė būtų vienetu didesnė (63 arba -64 po atėmimo 127).
:0.5625 * 2^63 = 5,188,146,770,730,811,392.
:0.5625 * 2^(-64) = 3.0493186101154812206459610024467e-20.
:Vadinasi išekstraktuojant atskirai eksponentę ir mantisą ir pastumus mantisos bitus į dešine viena pozicija, į kairiausį bitą turi įeitį vienetas. O prie eksponentės prisidėti vienetas, jeigu norima turėti tą patį skaičių.
:'''Magiškas skaičius reikalingas šiam darbui.'''
:Magiškas skaičius yra
:0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
:Sing: '''0''' (1 bitas).
:Exponent: '''101_1111_0''' (8 bitai).
:Mantissa: '''011_0111_0101_1001_1101_1111''' (23 bitai).
:Eksponentė yra 10111110 = 128 + 0 + 32 + 16 + 8 + 4 + 2 + 0 = 190. Atėmus 127 ji tampa 190-127 = 63.
:Mantisa yra
:011_0111_0101_1001_1101_1111 =
:= 0.5*0 + 0.25*1 + 0.125*1 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.431640625 + 0.0007781982421875 + 0.00078868865966796875 + 0.00000083446502685546875 = 0.43320834636688232421875.
:Viso skaičiaus reikšmė yra:
:1.43320834636688232421875 * 2^190 = 2.2490986495375580205008307141844e+57 = 2.2490986495375580205008307141844 * 10^57.
:Arba:
:1.43320834636688232421875 * 2^63 = 13,219,013,784,867,176,448 = 1.3219013784867176448 * 10^19.
:Kaip sakyta, 32 bitų skaičius pastumtas viena bito pozicija į dešinę atimamas iš magiško skaičiaus kaip atiminėjami sveikieji skaičiai.
:0101_1111_0011_0111_0101_1001_1101_1111-
:0001_1111_0001_0000_0000_0000_0000_0000=
:0100_0000_0010_0111_0101_1001_1101_1111.
:Gautas skaičius 0100_0000_0010_0111_0101_1001_1101_1111.
:Sign bit: '''0'''100_0000_0010_0111_0101_1001_1101_1111.
:Exponent: 0'''100_0000_0'''010_0111_0101_1001_1101_1111.
:Mantissa: 0100_0000_0'''010_0111_0101_1001_1101_1111'''.
:Eksponentė yra '''10000000''' = 128. Atėmus 127, eksponentė yra 128-127 = 1.
:Mantisa yra '''010_0111_0101_1001_1101_1111''' =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.306640625 + 0.0007781982421875 + 0.00001049041748046875 + 0.00000083446502685546875 = 0.30743014812469482421875.
:Gautas skaičius yra:
:1.30743014812469482421875 * 2^1 = 2.6148602962493896484375.
:Arba:
:1.30743014812469482421875 * 2^128 = 4.4489542538766432951275906695431e+38 = 4.4489542538766432951275906695431 * 10^38.
:Toliau tereikia daryti iteracijas pagal formulę:
:y = y * ( threehalfs - ( x2 * y * y ) );
:y_0 = 2.6148602962493896484375. z = x/2 = 0.15625/2 = 0.078125.
:y_1 = y_0 * (1.5 - z * y_0 * y_0) =
:= 2.6148602962493896484375 * (1.5 - 0.078125 * 2.6148602962493896484375 * 2.6148602962493896484375) =
:= '''2.52'''54863388218057175296506574507.
:Tikroji reikšmė:
:1/(0.15625)^0.5 = 2.5298221281347034655991148355462.
:Antra iteracija:
:y_2 = y_1 * (1.5 - z * y_1 * y_1) =
:= 2.5254863388218057175296506574507 * (1.5 - 0.078125 * 2.5254863388218057175296506574507 * 2.5254863388218057175296506574507) =
:= '''2.5298'''109880258619023169570466177. 2.5298109880258619023169570466177
:Trečia iteracija:
:y_3 = y_2 * (1.5 - z * y_2 * y_2) =
:= 2.5298109880258619023169570466177 * (1.5 - 0.078125 * 2.5298109880258619023169570466177^2) =
:= '''2.529822128'''0611201246242590927426. 2.5298221280611201246242590927426
:10 pirmų teisingų skaitmenų (beveik 11).
:Ketvirta iteracija:
:y_4 = y_3 * (1.5 - z * y_3 * y_3) =
:= 2.5298221280611201246242590927426 * (1.5 - 0.078125 * 2.5298221280611201246242590927426^2) =
:= '''2.52982212813470346559'''59044271886.
:21 teisingas skaitmenuo po 4 iteracijų. Tai didesnis tikslumas nei Double precision (64 bits). Maždaug toks tikslumas kaip Double Extended precision (80 bits).
:Darant dalybą ar šaknies traukimą gali prireikti panaikinti eksponentę (padaryti ją lygia nuliui ar vienetui). Tai galima atlikti labai paprastai. Reikia padaryti logišką '''AND''' operaciją skaičiaus
:'''1000,0000,0111,1111,1111,1111,1111,1111''' = '''10000000011111111111111111111111''' su norimu skaičiumi, kaip pvz. '''0011_1110_0010_0000_0000_0000_0000_0000''', kuris yra <math>x=0.15625.</math>
:Po '''AND operacjos''' bus gautas skaičius:
:'''0011_1110_0010_0000_0000_0000_0000_0000''' '''AND'''
:'''1000_0000_0111_1111_1111_1111_1111_1111''' =
:'''0000_0000_0010_0000_0000_0000_0000_0000'''.
:Gauta mantisa 01 = 0.5*0 + 0.25*1 = 0.25. Pridėjus nematomą vienetą priekyje galutinis teisingas skaičius lieka:
:1 + 0.25 = 1.25.
:Eksponentė tampa lygi 0. 2^0 = 1.
:AND operacija veikia tokiu budu:
:1 AND 1 = 1,
:1 AND 0 = 0,
:0 AND 1 = 0,
:0 AND 0 = 0.
:Analogišku budu galima paversti mantisos bitus nuliais ir palikti tik eksponentę, prie kurios paskui galima pridėti arba atimti 8 bitus esančius viena bito pozicija į dešinę nuo kairės. Analogiškai galima panaikinti minuso ženklą su AND operacija. Arba suteikti minuso ženklą pridėjus 32 bitų skaičių '''1000_0000_0000_0000_0000_0000_0000_0000''' prie kokio nors teigiamo 32 bitų skaičiaus (kai pirmas bitas yra 0).
==Kaip FPU galėtų apseit be barrel shifters sudedant skaičius su skirtingom eksponentėm==
:https://en.wikipedia.org/wiki/Barrel_shifter
:Barrel Shifter gali stumti pavyzdžiui 32 bito skaičiaus bitus į kairę ar į dešinę ne per vieną bito poziciją, bet per kelias kaip, kad per dvi bito pozicijas ar per 4 bito pozicijas ar per 8 bito pozicijas.
:Sudedant 32 bitų single precision skaičius su skirtingomis eksponentėmis, mažesnio skaičiaus mantisos bitai turi būti pastumti į dešinę per tiek pozicijų koks yra tu dviejų skaičių eksponenčių skirtumas, atėmus eksponentę mažesnio skaičiaus iš eksponentės didesnio skaičiaus.
:Be barrel shifter dviejų 32 bitų floating point skaičių sudėtį su skirtingomis eksponentėmis galima atlikti pakeitus mažesnio skaičiaus eksponentę į denormal ir padauginus tą mažesnį skaičių iš skaičiaus, kurio fractional part yra vienetas (visa mantisa lygi nuliui, jeigu be nematomo vieneto) o eksponentė yra neigiamas skaičius gautas atėmus didesnio 32 bitų float skaičiaus eksponentę iš mažesnio 32 bitų float skaičiaus eksponentės. Tada šio skaičiaus eksponentė bus neigiama ir ją padauginus iš paversto į denormal mažesniojo skaičiaus, bus gautas rezultatas, kad mažesnio skaičiaus mantisa bus pastumta į dešinę tiek pozicijų koks yra tos neigiamos eksponentės skaičiaus modulis.
:Bet turbūt vis tiek reikės šiftinti mantisos bitus į dešinę, kad paversti mažesnio 32 bitų skaičiaus mantisą kaip pas denormal skaičius pastumta atitinkama bitų skaičių į dešinę, kad paskui pridėti gautą mantisą prie didesnio skaičiaus mantios, ir eksponentę po sudeties palikti didesnio skaičiaus, jeigu ji nepakils vienetu dėl sudeties.
:Tai tiesiog čia kaip veikia skaičių sudėtis su skirtingom eksponentėm. Buvau pagalvojęs, kad galima apseiti be barrel shifter, bet pasirodo, kad ne. Arba reikia šiftinti po vieną bitą (jeigu nėra barrel shifter circuit), kas gali pareikalauti nemažai laiko, jeigu sudedamų skaičių eksponentės stipriai skiriasi.
:Todėl iš esmės sudėtis gali būti ilgesnė nei daugyba net su barrel shifter, nes daugyba su padaryta schema galinčia padauginti mantisas iš vieno ciklo, bus daug greitesnė. Ten iš esmės ta daugybos schema turėtų ir užimti didžiausią dalį FPU. Senesni FPU daugino iš daug ciklų, nes negalėjo sau leisti daug transistorių daugybos transistorių schemai. Maždaug Pentium procesoriai pradėjo dauginti iš vieno ciklo, jei neskaičiuoti visokių įkrovimo, užkrovimo, išėmimo operacijų dviejų dauginamų skaičių.
:'''Update 1.''' Pagal kitą informaciją Pentium (1993 metų) procesorius daugino 3 kartus greičiau nei 387 FPU (apie 1986 metų), nes daugino ir shiftino ne po vieną bitą, o po tris bitus. Buvo sukurtos schemos dauginti iš 0, 1, 2, 3, 4, 5, 6, 7. Nes 3 bitai gali turėti 8 kombinacijas. Tai vietoje 64 ar 66 sudėčių (2 bitai apvalinimui su 80 bitų Extended precision) reikėjo sudėti tik 22 kartus. Ir šiftinimas buvo po 3 bitus į dešinę Su Pentium procesorium.
:Intel procesoriai Nahalem pradėjo dauginti iš vieno ciklo su gerokai sudetingesne dauginimo schema ir su daug daugiau tam reikalingų transistorių. https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
:Nahalem architektura yra 2008 metų. Variantai:
:Core i5-7xx
:Core i7-8xx
:Core i7-9xx Extreme.
:Tai patys pirmi Core procesoriai. Po jų sekė Core 2 procesoriai (2009 metų):
:Core 2 Solo
:Core 2 Duo
:Core 2 Quad
:Core 2 Extreme.
:Dauginimas kart 0 yra dauginimo praleidimas, jei kalbėti apie 64 bit * 64 bit sveikųjų skaičių daugybą.
:Dauginimas kart 2 yra pastumimas skaičiaus viena bito pozicija į kairę.
:Dauginimas kart 3 reikalauja specialios schemos. Arba pastumimas viena bito pozcija į kairę + jis pats, t. y. 2+1.
:Dauginimas kart 4 yra pastumimas skaičiaus dviem bito pozicijom į kairę.
:Dauginimas kart 5 yra dauginimas iš 4 + pats skačius.
:Dauginimas iš 6 yra dauginimas iš 8 + pats skaičius pastumtas į kairę viena bito pozicija ir paverstas į Two's compliment neigiamą skaičių (Padarius XOR su vienetų seka ir pridėjus vienetą). Kitaip tariant dauginimas iš 6 yra 8-2. Arba galima 4+2, tada pastumiamas dviem pozicijom į kairę ir pridedamas jis pats pastumtas viena pozicija į kairę.
:Dauginimas iš 7 yra 8-1. Pastumiamas skaičius į kairę trim bitų pozicijom ir prie jo pridedamas pats tas skaičius paverstas į neigiamą skaičių two's compliment formate. Pastumimas galimas daiktas vyksta per automatišką schemą ir taip pat pridedamas jis pats paverstas per XOR operacija su vienetais ir +1 (per automatišką schemą, kitaip tariant viskas vyksta per vieną ciklą).
:Todėl maksimaliai daugyba su pentium Gali vykti 3 kartus greičiau nei su 387. Bet realus pagreitėjimas gali būti ir 2x. Tai priklauso kaip ten visa ta dauginimo vidinė schema padaryta (iš skaičių nuo 0 iki 7).
:Jei FPU neegzistuoja, o yra reklama ar panašiai, tai kad sudauginti du 64 bitų skaičius, kurių mantisa yra 52 bitai, eksponentė 11 bitų, ir minuso ženklas 1 bitas, tai pagal antro skaičiaus pirmą (kairiausią) mantisos bitą pirmas skaičius arba paliekamas (jei tas bitas 1) arba vietoj jo rašomas 0 (ignoruojamas), jei tas bitas 0.
:Toliau, pagal antrą nuo kairės bitą antro skaičiaus, pirmas skaičius arba paliekamas, jei tas bitas 1 arba ignoruojamas, jei tas bitas 0. Paskui jei tas bitas buvo 1, tai pirmas skaičius pastumiamas viena bito pozicija į dešinę su shift right instrukcija ir pridedamas prie to nepastumto pirmo skaičiaus.
:Iš pradžiu pridedami 32 dešiniausi bitai nepastumto ir pastumto pirmo skaičiaus. Jeigu buvo carry iš jų sudeties. Tai sudedant kairiasias 32 bitų tas dvi dalis dar pridedamas carry, naudojant instrukciją '''ADD with Carry'''.
:Dabar yra 64 bitų skaičius saugomas dviejuose 32 bitų RAM adresuose.
:Toliau pagal antro dauginamo skaičiaus trečia bitą, pirmas skaičius pastumiamas trim pozicijom į dešinę ir pridedamas prie gauto 64 bito skaičiaus, jei tas antro dauginamo skaičiaus trečias bitas 1 ir nepridedamas, jei tas trečias bitas 0.
:Kiekvieną kartą nereikia šiftinti iki 52 kartų į dešinę. Užtenka išsisaugot kas kart pašiftintą viena poziciją pirmą 64 bitų skaičių (šiftinama du kartus, vieną kart kairiausi 32 bitai ir antrą kart dešiniausi 32 bitai).
:Tai iš viso reikia 52 šiftinimų *2 (nes šiftinama į dešine po 32 bitus) ir 52 sudėčių *2 (nes sudedama po 32 bitus). Tai iš viso reikia 52*2 + 52*2 = 104+104 = 208 operacijos. Bet kartais sudėties operacijos gali būti praleidžiamos, tai vidutiniškai reikia ne 104 sudeties opeacijų (32 bitų), bet 52, nes vidutiniškai pusė bitų antro skaičiaus nuliai ir pusė vienetai. Tai tada vidutiniškai reikia 104+52 = 156 operacijų, kad sudauginti du 64 bitų double precision skaičius.
:Tada realus procesoriaus dažnis vietoje 5 GHz yra apie 156*5 = 780 GHz.
:Single precision (32 bitų) su 23 bitų mantisą reikėtų 46 šift ir 46 add operacijų. Iš viso 92 operacijų maksimum. Bet jeigu vidutiniškai yra 23 sudeties operacijos, tai tada apie 23+46 = 69 opracijų. Free Pascal koduose galima sakyti 4 GHz procesorius daugina 64 bitų dauble precision skaičius naudodamas 4 ciklus ir tai taip pat įskaitant visokius įkrovimus iškraovimus į/iš FPU į/iš RAM atminties. Tai dėl to 780 GHz dažnis nustatytas gana tiksliai ir nereikia jo dauginti iš 4.
:Atradau tokią gudrybę:
:12345 * 12345 = 152399025
:ir
:1.2345 * 1.2345 = 1.52399025.
:Arba
:12345 * 14789 = 182570205,
:1.2345 * 1.4789 = 1.82570205.
:Tai iš esmės galima dauginti single precision mantisas kaip sveikuosius skaičius, o paskui tik padėti kablelį kur reikia (gauto rezultatą įterpti į float mantisą be priekinio vieneto).
:Tikrinimas:
:123456789123456789 * 123456789123456789 = 1.5241578780673678515622620750191e+34
:1.23456789 * 1.23456789 = '''1.52415787'''50190521.
:1.23456789123456789 * 1.23456789123456789 = 1.5241578780673678515622620750191.
:Visgi tikslumas nukentėjo - gudrybė neveikia.
:Antras tikrinimas:
:1.23456789123456 * 1.23456789123456 = 1.52415787806734,83700809383936.
:1.2345678 * 1.2345678 = '''1.524157'''6,5279684.
:Neveikia gudrybė. Dauginant "trumpesnius" skaičius ir tikslumas mažesnis.
:Bet vis tiek nušiftinus minėtą pirmą 64 bitų skaičių jau 32 bitų pozicijom į dešinę, to pirmo skaičiaus 32 dešiniausius bitus jau galima nešiftini ir neatlikinėti su jais sudeties operacijų. Taip dar sutaupant trečdalį laiko (nes pusė laiko dirbama su 64 bitais, o paskui likusią pusę laiko šiftinami ir sudedami tik 32 bitai prie dešinios 32 bitų dalies pirmojo skaičiaus). Todėl sutaupoma 1/3 operacijų. Iš to gaunasi, kad iš viso reikia ne 156 ciklų (dviejų double precision skaičių daugybai), o
:156 - 156/3 = 156 - 52 = 104 ciklų. Vadinasi tada procesoriaus 5GHz dažnis būtų ne 780 GHz, o 5*104 = 520 GHz.
:Tai yra, iš pradžiu loop šiftina ir sudeda 32 kartus kaip su 64 bitais, o paskui pereinama prie antros loop, kur šiftinami ir sudedami 32 bitai.
:Arba, kad nedaryti loop kas irgi naudoja laiką, nes dviem instrukcijom daugiau (decrement register and jump to loop begining if loop register flag not zero). Tai geriau surašyti visas instrukcijas iš eilės kiek kartų viską daryt. Tai užims daugiau RAM atminties, bet veiks greičiau, nes nereikės palaikyti loop vykdymo. Tai užims salyginai mažai RAM - iki vieno kilobaito. Bet gali veikti 1.5 karto ar net 2 kartus greičiau. Ir dar neužims vieno registro kaip counterio skaičiavimui kiek iteracijų atlikta. O jeigu procesorius dar ir negali skaičiuoti per tam skirtą registrą kaip to negali intel 8080, tai reikės dar perkraudinėti į akumuliatorių counter'inimo skaičių, kad pažiurėti ar tas skaičius ne nulis ir neįjungs Zero flag (kai bus nulis). Nors šiaip intel 8080 turi CMP instrukciją (CMP, Compare register with A). Tai tas pats kas atimti kitą registrą ar operand'ą iš atminties iš registro A (akumuliatoriaus), tik rezultatas neįdedamas į akumuliatorių. Tai iš esmės kol count koks nors registras po decrement instrukcijos nėra nulis, tol compare su registru A neįjungs Zero flag. Bet tada išeina į registrą A kiekvieną kartą reikia krauti sveiką skaičių 0 po kiekvienos šiftinimo ir sudeties operacijos.
:Ką tik pažiūrėjau [https://bitsavers.trailing-edge.com/components/intel/MCS80/98-153B_Intel_8080_Microcomputer_Systems_Users_Manual_197509.pdf intel 8080 manual] (51 psl.), tai visgi Zero flag yra '''set''', kai pasirinktas registras po ''decrement register'' instrukcijos tapo lygus nuliui. Tai praktiškai nereikia naujiems procesoriams jokių geresnių instrukcijų išeina. Tik FPU dauginimo ir sudeties bei atimties greitų instrukcijų užtenka. Nes iš mano samprotavimų, viską darant per mikrokodą, viskas veiks praktiškai tokiu pačiu greičiu kaip viską darant per RAM, tik truputi mažiau RAM ilgos instrukcijos užims kaip integer multiply pas intel 8088 per mikrokodą. Vis tiek daugiausiai vietos RAM užima paveiksliukai, teksturos ir kita data, o ne pačios instrukcijos, tai šiuolaikiniams laikams tai neaktualu. O RAM parinkus vieną ROW, ateina iš tos parinktos ROW bitai iš visų Column į kelis tūkstančius SRAM bitų registrų. Paskui iš tų SRAM (static RAM) registrų, kurie dažniausiai yra 4 bitų viename RAM čipe, atgal gražinami nutekėję tos DRAM row visos stulpelių (column) bitų reikšmės atgal į tas pačias vietas ir Row atjungiama, o tie nutekėję bitai į SRAM lasteles (registrus) niekur nedingsta iki tol kol nekreipiamasi į kitą Row. Todėl paskui procesorius ima informaciją ne iš DRAM lastelių, o iš SRAM lastelių. Ir įrašo į SRAM ląsteles, o parinkus naują ROW, tą informacija iš kelių tukstančių SRAM lastelių (registrų) per kelis ciklus grąžinama į visas column tos Row. Instrukcijos vykdomos dažniausiai nuosekliai ir dažniausiai peršokama ribose tos pačios DRAM ROW. Todėl vis naudojami tie patys SRAM registrai nutekintų bitų DRAM stulpelių iš tos pačios ROW. Dėl to DRAM praktiškai gali veikti greičiau nei bet koks cache ir naudoti daug mažiau energijos už cache, kuriam reikia milijonų SRAM lastelių, o ne tukstančių. Tik didesnis atstumas tarp CPU ir DRAM sulėtiną informacijos keliavimą iš DRAM esančių SRAM lastelių dėl šviesos greičio limito iš dalies. Tai, kad cache atmintis gaunasi visiškai beprasmiška, jei tinkamai panaudoti tą DRAM veikimą su SRAM registrais viduje. Kas teoriškai turėtų taip ir būti.
:Jeigu 5 GHz procesorius iš tikro veikia 500 GHz dažniu, o šviesos greitis nėra sumeluotas, tai 1 metrą šviesą gali nueiti 300 milijonų kartų arba 300 MHz. Atstumas tarp procesoriaus ir RAM yra apie 10 cm, tai tada RAM dažnis max gali buti apie 3 GHz. Bet jeigu procesorius veikia 500 GHz, nes jos pagrindinė dalis yra labai sumažinta dėl naujesnės litografijos, o pats CPU base core užima ne daugiau nei milijoną tranzistorių, tai išeitų, kad nespėtų imti iš RAM atminties informaciją CPU dėl šviesos greičio abribojimu (dėl atstumo tarp CPU ir RAM). Tai šita dalyką galima būtų išspręsti, jei visos programos ir programavimo kalbos naudotų 64 bitų ar 32 bitų skaičių daugybai tam skirtą RAM adresą vieno kilobaito ar mažiau, kuris visada būtų adresuojamas ne per DRAM, bet per SRAM esančią pačiame mikroprocesoriuje. Pavyzdžiui tos SRAM kilobaitas galėtų būti pačioje RAM pradžioje, tam skirus 256 adresus saugančius po 32 bitus. Tai tiesiog pirmi 8 (ar 10, nes 2 bitai reiškia 4 baitus..., o procesorius ne 8, o 32 bitų...) kairiausių bitų būtų nukreipti į tą SRAM atminti, o like 24 (ar 22) bitai einantys į SRAM atmintį būtų nuliai. Jei nors vienas iš dešiniausių 24 (ar 22 bitų) būtų ne nulis, tai Adresas iš CPU nukeliautų ne į vidinę SRAM atmintį, o į išorinę lėtesnę DRAM atmintį. Tai tiesiog prieš pradedant daugybą, visos programos galėtų naudoti tą SRAM atmintį pernešant ten du dauginamuosius į atitinkamas vietas, kuriuos jau supa daugybos kodas... Ir su 500 GHz mažyčiu procesorium (be visokių nereikalingų kvailų instrukcijų) galėtų užtekti šviesos greičio būtent tokiam dideliam dažniui, kad atlikti double ar single precision daugybą. Intel iš tikro sudetingos instrukcijos asembleryje galėtų būti kaip aukštesnio lygio programavimo kalba, kuri paskui visas tas instrukcijas paverčia į 8080 CPU tipo instrukcijas, o FPU daugybą perkelia į tam skirtą SRAM atminties vietą, o rezultatą gražina lyg niekur nieko, ten kur ištransliavo asembleris... O vėliau ir aukštesnio lygio programavimo kalbos kaip C ir Pascal.
:Ne. Visgi skaičiavimo trukmė sumažėja ne trečdaliu, o ketvirtadaliu. Tuomet 5 GHz procesoriaus dažnis būtų ne 520 GHz, o
:(156 - 156/4) * 5 (GHz) = (156 - 39) * 5 = 585 GHz.
:'''Update 2.''' Dar yra toks variantas, kad 5 GHz procesorius dirba apie 50 GHz dažniu ir neturi FPU. Iš esmės rodos kodus kaip daugybos ir sudeties benchmarkai su viena daugyba ir/ar sudėtimi buvo skaičiuojami per apie 25-30 sekundžių pirmą kartą su 1 milijardu iteracijų. Jei viena operacija užima 4 ciklus ant 4 GHz procesoriaus tai viena daugyba ir sudėtis užimtu 8 ciklus. O 25-30 sekundžių yra 100-120 ciklų. Tai 120/8 = 15 ciklų daugybai ir sudečiai, jei tik daugybai tai 30 ciklų. Bet buvau ir be daugybos tik, kur yra sudetis ir vis tiek pat kaip su daugyba. Be to ten maži skaičiai iš pradžiu ėjo, tai juos galėjo greičiau daugint. Tik kur nebuvo daugybos, kodėl irgi 25-30 sekundžių darė sudeties operacijas milijardą kartų? Tai gal vistiek ten 20-25 sekundes pirmą kartą kompiliavimas užima ir FPU iš tikro yra. Arba sudetis yra ilga nes sudedami skaičiai su skirtingom eksponentėm ir reikalingas šiftinimas per kelis ar keliasdešimt bitų. Tai visgi gali būti, kad Tikrasis CPU dažnis yra ne 4 ar 5 GHz, o apie 40-50 GHz ar net 100 GHz. Jeigu FPU neegzistuoja. Tada kvadratinę šaknį ir dalyba tikrai ne greičiau bus su Niutono iteracijomis daryti, o taikyti kitus metodus. Dalyba tai ir taip bus beveik toks pat greitis kaip daugyba (jei FPU nėra). O kvadratinei šakniai kažką reik sugalvot.
:Vienintelis įrodymas, kur sinuso Teiloro eilutė pirmą kartą labai greit skaičiuojama. Sudedami sinusai su įvairiom reikšmėm. Problema ta, kad negalima patikrinti teisingumo tų visų ilgo kodo skaičiavimu, gal kažką nukerta ten... Kiti kodai kaip parabolės ilgis ar integralų sumos duoda patikrinamą per integralus rezultatą, bet jie ir skaičiuojami pirmą kartą ilgiau nei antrą kartą.
:Tai geriau kur nereikia taikyti trumpesnes daugybas su tuo 50 GHz procesorium, ką tuomet išeitų žaidimų kurėjai ir game engines kūrėjai ir daro.
:Intel387TM SX MATH COPROCESSOR
:https://datasheets.chipdb.org/Intel/x86/387/datashts/24022509.PDF
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240225-009.pdf
:Intel 387 DX MATH COPROCESSOR
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005.pdf
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005_alt.pdf
:https://ardent-tool.com/CPU/Docs_Intel.html#387
:Intel 386
:www.digchip.com/datasheets/download_datasheet.php?id=59667&part-number=386DX
:https://ardent-tool.com/CPU/docs/Intel/386/datasheets/231630-011.pdf
:https://www.dosdays.co.uk/media/intel/1986_80386_Hardware_Reference_Manual.pdf
:https://www.bitsavers.org/components/intel/80386/230985-001_80386_Programmers_Reference_Manual_1986.pdf
:https://pdos.csail.mit.edu/6.828/2018/readings/i386.pdf
:https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf
===Intel 386 procesoriaus suderinamumas su 286 ir 8086===
:Skaitant čia https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf 129 puslapyje ir ankstesnius kitus puslapius, galima suprasti, kad intel 386 CPU iš tikro niekaip neoptimizuoja apatinės ir viršutinės dalies (16 bitų) RAM adreso saugančio 32 bitus. Pasakyta, kad jeigu dedama apatinė 16 bitų dalis, tai aktyvuojamos kojelės BE0 ir BE1, atsakančios už perdavimą apatinės 16 bitų dalies į 32 bitų RAM vieną adresą. Jeigu aktyvuojamos kojelės BE2 ir BE3, tai viršutiniai 16 bitų dedami ir į apatinę ir į viršutinę dalį 32 bitų RAM vieno adreso. Amerikiečiams labai svarbu būti konkurencingams.
:Tai jeigu pernešti visą intel 286 procesoriaus kodą į 386 procesorių, tai jeigu naudoti tik 32 bitų registrus (EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP) ir 286 procesoriaus 16 bitų kodą įrašyti tik į apatinius 16 bitų visuose adresuose, tai tos visos 16 bitų programos neveiks, nes viršutiniai 16 bitų bus nuliai kai bus pridedama neigiama displacement '''two's complement''' formate. O jeigu visi viršutinai 16 bitų vieno 32 bitų RAM adreso bus vienetai, kad veiktų neigiama displacement, tai tada neveiks teigiama displacement, nes pas teigiama displacement reikia, kad visi viršutiniai 16 bitų būtų nuliai.
:Dar gali būti, kad intel 386 ir intel 286 procesoriai naudoja visiškai skirtingus opcode'us. Su '''REP'''eat prefixu kai kurių instrukcijų 386 procesoriuje nėra, kurios yra 8086 procesorioju. Vietoje to, 386 naudoja REPeat instrukcijas kaip atskirą opcode'ą nuo ne Repeat instrukcijų. Kitų opcode'ų nelyginau, norintys gali palyginti ir patikrinti kas kaip suderinta. Bet manau ten visi opkodai skirtingi pas 386 nuo 286 ir 8086.
:Gali būti, kad kažkaip dedant į 386 procesoriaus 16 bitų registrus (AX, BX, CX, DX, DI, SI, BP, SP) ir panaudojant kojelę BS16 ant 386 CPU, visi 16 bitų kodai iš 286/8086 veiks su 386 procesorium. Tada 386 procesorius visada skaitys tik iš minėtų 16 bitų registrų (AX, BX, CX, DX, DI, SI, BP, SP). Bet tada tai reiškia, kad kai aktyvuota BS16 kojelę, 386 pradeda suprasti 286 procesoriaus opcode'us iš žemutinių 16 bitų RAM adreso 32 bitų. Į viršutinius 16 bitų to paties RAM adreso tada išeina niekas nededama. Aš tai sakyčiau, kad intel inžinieriai nesivargino palaikyti 286 procesoriaus opkodų, o teoretikams kuriantiems instrukcijas ir datasheets ir user's manual'us pasakė, kad viskas veiks maksimaliai optimaliai ir optimizuotai. Todėl greičiausiai reikia perrašinėti 286 visas programas 386 procesoriams arba kai buvo kuriamos programos, buvo kuriamos iškart ir 386 ir 286 procesoriui, o senesnės programos 8086 tuomet arba neveik su 386 CPU arba buvo per kokį nors asemblerį konvertuojamos į 386 procesoriaus kodą paleidus, kas turėdavo atimti laiko paleidimui. Kad intel 8080 procesoriaus opkodai neveikia su 8088/8086 pasakyta wikipedijoje. Todėl nėra prasmės iš 8080 registrų, bet gal jie duoda pasirnkti baitus (A, B, C, D, E, H, L registrai). Arba ten tik girti suderinamumą su 8080 ir 286/8088, bet iš tikro tuomet yra tik 32 bitų 8 registrai EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP. Be to 8086 galėjo būti neveikiantis procesorius, dėl to IBM pasirinko 8088. 8086 deda į RAM adresą 16 bitų, o 8088 į vieną RAM adresą deda tik 8 bitus. O jeigu skaičius yra 16 bitų, tada deda į du RAM adresus kaip intel 8080 CPU.
[https://lt.wikibooks.org/wiki/Wikibooks:Knygos/High_Level_Shading_Language High Level Shading Language]
gq4r3op8b6ffmvpdbxpoc7ld40uoc50
59028
59027
2026-09-10T09:20:55Z
Paraboloid
1294
/* Intel 386 procesoriaus suderinamumas su 286 ir 8086 */
59028
wikitext
text/x-wiki
==Dešimtainių skaičių vertimas į dvejetainius skaičius==
[https://www.google.com/search?client=opera&q=single+precision&sourceid=opera&ie=UTF-8&oe=UTF-8&udm=50&fbs=ABfTbFVyMZGZf1hfvX9uKjN_-G8c4u0nXx4bEIpwm1lnNH832VTJOOCxW_fyN-Q_ezyf8gKjm3rhh_G8jdZ2Q6tji4z8Mva6cfvBj5rpPch2IC955IbzrbwHf6tkbFbd-nuFdbKth9Y-58t5LtnYgTA4TWDFYCUNl4d7F9XBAwiEq2T2c7SNgrvMxYiRBXL4fnQnnTOewx28&aep=10&ntc=1&sxsrf=APpeQnvZo3fX2PwO881uSdWsoRELP18NhQ%3A1788348274309&mstk=AUtExfAJ0Kp9BqT3EJbzHOz3MHMalnyXkoOTLvgho5ZQ9lzjrnmmUFCgux_RVe7bouRlyBSGpkUcLpvfZ39N45TUoor6TyyEW7_PpE7eOwP-kU7IFxE6monk-HtsDXkDu7g362AN4NTW3oRjfwn9OtD0BIj2r4QAVauMhDPCIAiWgLimDuate6cf2o6bB5R-6-vzcCqbCju1KJQTdR4HSF9puZ_HCG1hXQ8QaCIqD1LJ_HWonNnZGma6Biu3I1wWFPy6esLnfaKpIX6VFWZsArfQN97XGipwkH7FLKCyZiev5rZBrhE8KincMthP7B9AJDugKLy8qJfORQ7PQR2GslAaCLnkechjBCnXfrmSohwJskbEWU9db6Q279uO4dGu0eI2YUvpEwoQUqkbREqJqrGpJQWFy7WoTzI93_o_2I61PU1FTiuGq-4AVN7erT0WmuWizHDEE2XsTes&aioh=3&csuir=1&atvm=2&mtid=dQeYaumuDL2Ixc8Px6Ts0QE DI atsakymas]
:Single-precision is a 32-bit computer number format that stores fractional and large numbers using a sign bit, an exponent, and a mantissa. Structure of Single-Precision (Binary32) According to Wikipedia, “Single-precision floating-point format (sometimes called FP32, float32, or float) is a computer number format, usually occupying 32 bits in computer memory.” The 32 bits are divided into three specific parts:
*Sign bit (1 bit): Determines if the number is positive (0) or negative (1).
*Biased exponent (8 bits): Stores the magnitude with an excess-127 bias, giving an actual exponent range of -126 to +127.
*Mantissa/Significand (23 explicitly stored bits): Represents the precision digits of the number, with an implicit leading 1 for normalized values.
:Precision and Applications. Decimal Precision: Provides about 7 to 8 decimal digits of accuracy. Most users on StackOverflow agree that “Single precision typically offers 7-8 decimal digits of precision.”
:'''Dešimtainių skaičių vertimas į dvejetainius.'''
:Step-by-Step Conversion Framework.
*Step 1: Determine the Sign Bit (1 bit). If the number is positive, the sign bit is 0. If the number is negative, the sign bit is 1. Example (-12.625): It is negative, so the sign bit is 1.
*Step 2: Convert the Absolute Value to Binary. Separate the number into its integer and fractional parts, convert both to binary, and combine them.
:Integer part '''12''': (12/2 = 6) ('''rem'''ainder 0), (6/2 = 3) (rem 0), (3/2 = 1) (rem 1), (1/2 = 0) (rem 1) <math>\rightarrow </math> '''1100'''.
:Fractional part '''0.625''': (0.625 * 2 = 1.25) (1), (0.25 * 2 = 0.5) (0), (0.5 * 2 = 1.0) (1) <math>\rightarrow </math> '''101'''.
:Example Combined: '''12.625''' = '''1100.101'''.
*Step 3: Normalize the Binary Number. Shift the binary point so that exactly one non-zero digit (which will always be 1) remains to its left. Count how many places you moved the point. This count is your exponent (E).
:Move the point 3 places to the left: '''1100.101''' <math>\rightarrow </math> '''1.100101'''.
:Example Exponent: Because we moved it 3 places, E = 3.
*Step 4: Calculate the Biased Exponent (8 bits) IEEE 754 single-precision uses a bias of 127. Add your exponent E to this bias, and convert the resulting integer to an 8-bit binary number.
:Calculation: 127 + 3 = 130.
:Convert 130 to 8-bit binary: '''130''' = '''10000010'''.
*Step 5: Extract the Mantissa/Significand (23 bits).
:Take the fractional bits from your normalized number (everything to the right of the binary point). Drop the leading 1. If the bits are fewer than 23, pad the right side with zeros until you reach exactly 23 bits.
:Normalized bits: 1.100101 <math>\rightarrow </math> Drop the ''1.'' (atmesti kairiausią vienetą ir tašką po jo) to get '''100101'''.
:Example Pad to 23 bits: 10010100000000000000000.
:'''Final Assembly.'''
:Combine the three components in order: Sign (1 bit) + Exponent (8 bits) + Mantissa (23 bits).
:Component_____________Bits______________Value for -12.625_______
:Sign____________________1_______________________1______________
:Exponent________________8____________________10000010__________
:Mantissa________________23______________10010100000000000000000
*Final 32-bit Binary String: '''11000001010010100000000000000000'''.
*Hexadecimal Representation: '''C1450000'''.
:Sign: '''1'''1000001010001010000000000000000.
:Exponent: 1'''10000010'''10001010000000000000000.
:Mantissa: 110000010'''10010100000000000000000'''.
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Mantissa: 100101 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 0.578125.
:8 * 0.578125 = 4.625.
:8 * 1 = 8.
:8 + 4.625 = 12.625.
:Reikia gražinti paimtą vienetą priekyje ('''12.625''' = '''1100.101'''; '''1100.101''' <math>\rightarrow </math> '''1.100101'''):
:8 * (1 + 0.578125) = 8 * 1.578125 = 12.625.
:-12.625 = -1.2625 * 10^1.
:Čia mantisa 0.2625, o ekspontentė 1 dešimtainėje sistemoje. Skačių 0.2625 reikia kaskart dauginti iš 2.
:0.2625*2 = 0.525 (ne vienetas, reiškia pirmas skaitmuo 0),
:0.525*2 = 1.05 (vienetas, reiškia antras skaitmuo 1),
:0.05*2 = 0.1 (ne vienetas, reiškia trečias skaitmuo 0),
:0.1*2 = 0.2 (ne vienetas, reiškia ketvirtas skaitmuo 0),
:0.2*2 = 0.4 (ne vienetas, reiškia penktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia šeštas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia septintas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia aštuntas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia devintas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia dešimtas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia vienuoliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia dviliktas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia tryliktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia keturioliktas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia penkioliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia šešioliktas skaitmuo 1).
:Tada galutinis skaičius '''0.2625''' yra toks:
:0.0100001100110011.
:Kad gauti didesnį tikslumą, procesą reikia kartoti begalo arba iki reikiamo tikslumo.
:Skaičiau '''0.2625''' reikšmė yra:
:0.0100001100110011 =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*0 + 0.015625*0 + 0.0078125*1 + 0.00390625*1 + 0.001953125*0 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 =
:= 0.26171875 + 0.0007781982421875 = '''0.26249'''69482421875.
:Taigi, 0.26249 labai panašus į 0.2625.
:Vadinasi '''1.2625''' dvejetainėje sistemoje apytiksliai yra '''1.0100001100110011'''.
:Eksponentę 10 reikia versti taip:
:10 = 8 + 2. Tada 10 dvejetainėje sistemoje yra 00001010 (1*0 + 2*1 + 4*0 + 8*1 + 0 + 0 + 0 + 0 = 10).
:Bet eksponentė single precision formate gali būti tik, 0, 1, 2, 4, 8, 16, 32, 64, 128 ir taip toliau.
:Arba reikia daryti taip:
:10/2 = 5 (rem 0),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0)
:1/2 = 0.5 (rem 1).
:Va štai šitaip ir gaunamas skaičius 10. Tai yra 1010.
:Skaičius 1010 pakeičiamas skaičiu 1.010 su eksponente 3 (2^3 = 8).
:Tada skaičius 1.010 iškoduojamas taip:
:1 + 0.5*0 + 0.25*1 + 0.125*0 = 1.25.
:Padauginama iš dviejų pakelta eksponente 3:
:1.25 * 2^3 = 1.25 * 8 = 10.
:Tuomet
:1.2625 * 10 = 12.625.
:'''Grįžimas prie pavyzdžio.'''
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Skaičiaus 1100.101 kablelį galima pastumti 4 pozicijom į kairę ir eksponentę duoti 4. Mantisa tada yra 0.1100101, o eksponentė yra 4.
:0.5*1 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*0 + 0.0078125*1 = 0.7890625.
:2^4 * 0.7890625 = 16 * 0.7890625 = '''12.625'''.
:Bandomas kitas skaičius 24:
:24/2 = 12 (rem 0),
:12/2 = 6 (rem 0),
:6/2 = 3 (rem 0),
:3/2 = 1 (rem 1),
:1/2 = 0.5 (rem 1).
:Reiškia 24 yra 11000 (16*1 + 8*1 + 4*0 + 2*0 + 1*0 = 24).
:Bandomas kitas skaičius 19:
:19/2 = 9 (rem 1),
:9/2 = 4 (rem 1),
:4/2 = 2 (rem 0),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 19 yra 10011 (16*1 + 8*0 + 4*0 + 2*1 + 1*1 = 19).
:Bandomas skaičius 22:
:22/2 = 11 (rem 0),
:11/2 = 5 (rem 1),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 22 yra 10110 (16*1 + 8*0 + 4*1 + 2*1 + 1*0 = 16+4+2 = 22).
:Trumpas paaiškinimas kodėl eksponentė saugoma formate nuo 1 iki 254, pridedant 127 prie -126 ir iki 127. Tai greičiausia yra todėl, kad sudedant single precision skaičių tokiame formate kokiame jis yra, jo reiikšmė gali būti gana tiksliai traktuojama kaip sveikojo skaičiaus reikšmė. Su kai kuriais pakeitimais.
:'''-12.625''' yra '''11000001010010100000000000000000'''.
:Pridėjus 127 ('''01111111''') prie eksponentės 3 ('''00000011''') gauname 130 ('''10000010'''):
:'''00000011'''+
:'''01111111'''=
:'''10000010'''.
:Vadinasi tada skaičius '''-12.625''' tampa
:Mantissa: 1100101 = 1 + 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 1.578125;
:2^130 = 1.3611294676837538538534984297271e+39 = 1.3611294676837538538534984297271 * 10^39.
:1.578125 * 2^130 = 2.148032441188424050612552209413e+39.
:O sveikasis skaičius '''11000001010010100000000000000000''' be minuso yra '''01000001010010100000000000000000''' ir užrašomas taip:
:1*0 + 2*0 + 4*0 + 8*0 + 16*0 + 32*0 + 64*0 + 128*0 + 256*0 + 512*0 + 1024*0 +
:+ 2048*0 + 4096*0 + 8192*0 + 16,384*0 + 32,768*0 + 65,536*0 + 131,072*0 +
:+ 262,144*1 + 524,288*0 + 1,048,576*1 + 2,097,152*0 + 4,194,304*0 + 8,388,608*1 + 16,777,216*0 + 33,554,432*1 +
:+ 67,108,864*0 + 134,217,728*0 + 268,435,456*0 + 536,870,912*0 + 1,073,741,824*0 +
:+ 2,147,483,648*1 + 4,294,967,296*0 =
:= 0 + 0 + 43,253,760 + 0 + 2,147,483,648 = 2,190,737,408.
:Kažka supainiojau. Atrodė, kad eksponentė beveik tas pats kas didesni skaičiai priekyje.
==Skaičiavimas inverse square root su C kodu==
:Čia https://en.wikipedia.org/wiki/Fast_inverse_square_root aiškinama kaip naudojant sveikuosius skaičius galima apsakičiuoti <math>\frac{1}{\sqrt{x}}.</math>
:"The fast inverse square generates a good approximation through integer operations by adding and subtracting the integer form of floating-point numbers, and taking a square root by dividing by two (which is just a right-shift)."
:Iš čia https://lt.wikibooks.org/wiki/Sekos_riba#Greitas_šaknies_iš_skaičiaus_a_traukimo_būdas gauta formulė
:<math>y_{n+1} = \frac{y_{n}\left(3-xy_n^2\right)}{2}. \quad (4)</math>
:<math>y_n\approx \frac{1}{\sqrt{x}}.</math>
:Ją dar galima pagreitint, pakeitus z = x/2. Tada ši formulė tampa tokia:
:<math>y_{n+1} = y_{n}\left(1.5 - zy_n^2\right). \quad (4.1)</math>
:Programavimo kalbos C kodas skaičiuoti <math>\frac{1}{\sqrt{x}}</math> yra toks:
<syntaxhighlight lang="c">
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
</syntaxhighlight>
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Toliau eilutė
i = 0x5f3759df - ( i >> 1 );
:reiškia pastumti sveikąjį skaičių '''i''' viena pozicija į dešinę ir paskui atimti iš sveikojo skaičiaus 0x5f3759df ir rezultatą įdėti į skaičiaus '''i''' adresą. Skaičius 5f3759df turi 8 hexodecimal skaitmenis. Vienas heksodecimal skaitmuo sudaro 4 bitus.
:5 yra 0101, f yra 1111, 3 yra 0011, 7 yra 0111, 5 yra 0101, 9 yra 1001, d yra 1101 (nes A yra 1010, B yra 1011, C yra 1100), f yra 1111. Todėl
:5F3759DFh = 0101,1111,0011,0111,0101,1001,1101,1111;
:5F3759DFh = 01011111001101110101100111011111.
:Gale raidė h reiškia, kad tai hexodecimal skaičius.
:Ši eilutė:
y = * ( float * ) &i;
:paverčia long int '''i''' į float, o paskui tą float perkelią į '''y'''.
:Toliau daromos Niutono iteracijos:
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
:čia y jau turi apytikslią reikšmę <math>\frac{1}{\sqrt{\text{number}}}.</math>
===Worked example===
As an example, the number <math>x=0.15625</math> can be used to calculate <math display=inline>\frac{1}{\sqrt{x}} \approx 2.52982</math>. The first steps of the algorithm are illustrated below:
0011_1110_0010_0000_0000_0000_0000_0000 Bit pattern of both x and i
0001_1111_0001_0000_0000_0000_0000_0000 Shift right one position: (i >> 1)
0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
0100_0000_0010_0111_0101_1001_1101_1111 The result of 0x5F3759DF - (i >> 1)
Interpreting as [[IEEE 754|IEEE]] 32-bit representation:
0_01111100_01000000000000000000000 1.25 × 2<sup>−3</sup>
0_00111110_00100000000000000000000 1.125 × 2<sup>−65</sup>
0_10111110_01101110101100111011111 1.432430... × 2<sup>63</sup>
0_10000000_01001110101100111011111 1.307430... × 2<sup>1</sup>
Reinterpreting this last bit pattern as a floating point number gives the approximation <math>y=2.61486</math>, which has an error of about 3.4%. After one iteration of Newton's method, the final result is <math>y=2.52549</math>, an error of only 0.17%.
:'''Patikrinimas.'''
:<math>x=0.15625.</math>
:Sing: '''0'''011_1110_0010_0000_0000_0000_0000_0000 (1 bitas).
:Exponent: 0'''011_1110_0'''010_0000_0000_0000_0000_0000 (8 bitai).
:Mantissa: 0011_1110_0'''010_0000_0000_0000_0000_0000''' (23 bitai).
:Exponente yra '''01111100'''. Tai yra
:128*0 + 64*1 + 32*1 + 16*1 + 8*1 + 4*1 + 2*0 + 1*0 = 124.
:124-127 = -3. Tada eksponentė yra '''-3'''. 2^(-3) = 0.125.
:Mantisa yra '''010_0000_0000_0000_0000_0000'''.
:Arba '''101''' = 1 + 0.5*0 + 0.25*1 = '''1.25'''.
:1.25 * 0.125 = 0.15625.
:Visi bitai pastumiami viena bito pozicija į dešinę. Tai tas pats kas padalinti iš dviejų mantisą ir eksponentę. Po pastumimo 32 bitų skaičius tampa:
:0001_1111_0001_0000_0000_0000_0000_0000
:Eksponentė tampa 001_1111_0 arba '''00111110'''. Tai yra 128*0+64*0+32*1+16*1+8*1+4*1+2*1+1*0 = 62.
:62-127 = -65. Padauginus iš 2^(-65) = 2.7105054312137610850186320021749e-20 mantisoje nieko neliks.
:Mantisa tapo 001_0000_0000_0000_0000_0000, kas yra
:1001 = 1 + 0.5*0 + 0.25*0 + 0.125*1 = 1.125.
:1.125 * 2^62 = 5,188,146,770,730,811,392.
:1.125 * 2^(-65) = 3.0493186101154812206459610024467e-20 = 3.0493186101154812206459610024467*10^(-20).
:Vat ir išlindo yla iš maišo. Jei nereikėtų prirašyt priekyje vienetoje mantisoje, o eksponentė butų vienetu didesnė, tai rezultatas būtų
:1001 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 = 0.5 + 0.0625 = 0.5625. (0.5625*2 = 1.125)
:Eksponentė būtų vienetu didesnė (63 arba -64 po atėmimo 127).
:0.5625 * 2^63 = 5,188,146,770,730,811,392.
:0.5625 * 2^(-64) = 3.0493186101154812206459610024467e-20.
:Vadinasi išekstraktuojant atskirai eksponentę ir mantisą ir pastumus mantisos bitus į dešine viena pozicija, į kairiausį bitą turi įeitį vienetas. O prie eksponentės prisidėti vienetas, jeigu norima turėti tą patį skaičių.
:'''Magiškas skaičius reikalingas šiam darbui.'''
:Magiškas skaičius yra
:0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
:Sing: '''0''' (1 bitas).
:Exponent: '''101_1111_0''' (8 bitai).
:Mantissa: '''011_0111_0101_1001_1101_1111''' (23 bitai).
:Eksponentė yra 10111110 = 128 + 0 + 32 + 16 + 8 + 4 + 2 + 0 = 190. Atėmus 127 ji tampa 190-127 = 63.
:Mantisa yra
:011_0111_0101_1001_1101_1111 =
:= 0.5*0 + 0.25*1 + 0.125*1 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.431640625 + 0.0007781982421875 + 0.00078868865966796875 + 0.00000083446502685546875 = 0.43320834636688232421875.
:Viso skaičiaus reikšmė yra:
:1.43320834636688232421875 * 2^190 = 2.2490986495375580205008307141844e+57 = 2.2490986495375580205008307141844 * 10^57.
:Arba:
:1.43320834636688232421875 * 2^63 = 13,219,013,784,867,176,448 = 1.3219013784867176448 * 10^19.
:Kaip sakyta, 32 bitų skaičius pastumtas viena bito pozicija į dešinę atimamas iš magiško skaičiaus kaip atiminėjami sveikieji skaičiai.
:0101_1111_0011_0111_0101_1001_1101_1111-
:0001_1111_0001_0000_0000_0000_0000_0000=
:0100_0000_0010_0111_0101_1001_1101_1111.
:Gautas skaičius 0100_0000_0010_0111_0101_1001_1101_1111.
:Sign bit: '''0'''100_0000_0010_0111_0101_1001_1101_1111.
:Exponent: 0'''100_0000_0'''010_0111_0101_1001_1101_1111.
:Mantissa: 0100_0000_0'''010_0111_0101_1001_1101_1111'''.
:Eksponentė yra '''10000000''' = 128. Atėmus 127, eksponentė yra 128-127 = 1.
:Mantisa yra '''010_0111_0101_1001_1101_1111''' =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.306640625 + 0.0007781982421875 + 0.00001049041748046875 + 0.00000083446502685546875 = 0.30743014812469482421875.
:Gautas skaičius yra:
:1.30743014812469482421875 * 2^1 = 2.6148602962493896484375.
:Arba:
:1.30743014812469482421875 * 2^128 = 4.4489542538766432951275906695431e+38 = 4.4489542538766432951275906695431 * 10^38.
:Toliau tereikia daryti iteracijas pagal formulę:
:y = y * ( threehalfs - ( x2 * y * y ) );
:y_0 = 2.6148602962493896484375. z = x/2 = 0.15625/2 = 0.078125.
:y_1 = y_0 * (1.5 - z * y_0 * y_0) =
:= 2.6148602962493896484375 * (1.5 - 0.078125 * 2.6148602962493896484375 * 2.6148602962493896484375) =
:= '''2.52'''54863388218057175296506574507.
:Tikroji reikšmė:
:1/(0.15625)^0.5 = 2.5298221281347034655991148355462.
:Antra iteracija:
:y_2 = y_1 * (1.5 - z * y_1 * y_1) =
:= 2.5254863388218057175296506574507 * (1.5 - 0.078125 * 2.5254863388218057175296506574507 * 2.5254863388218057175296506574507) =
:= '''2.5298'''109880258619023169570466177. 2.5298109880258619023169570466177
:Trečia iteracija:
:y_3 = y_2 * (1.5 - z * y_2 * y_2) =
:= 2.5298109880258619023169570466177 * (1.5 - 0.078125 * 2.5298109880258619023169570466177^2) =
:= '''2.529822128'''0611201246242590927426. 2.5298221280611201246242590927426
:10 pirmų teisingų skaitmenų (beveik 11).
:Ketvirta iteracija:
:y_4 = y_3 * (1.5 - z * y_3 * y_3) =
:= 2.5298221280611201246242590927426 * (1.5 - 0.078125 * 2.5298221280611201246242590927426^2) =
:= '''2.52982212813470346559'''59044271886.
:21 teisingas skaitmenuo po 4 iteracijų. Tai didesnis tikslumas nei Double precision (64 bits). Maždaug toks tikslumas kaip Double Extended precision (80 bits).
:Darant dalybą ar šaknies traukimą gali prireikti panaikinti eksponentę (padaryti ją lygia nuliui ar vienetui). Tai galima atlikti labai paprastai. Reikia padaryti logišką '''AND''' operaciją skaičiaus
:'''1000,0000,0111,1111,1111,1111,1111,1111''' = '''10000000011111111111111111111111''' su norimu skaičiumi, kaip pvz. '''0011_1110_0010_0000_0000_0000_0000_0000''', kuris yra <math>x=0.15625.</math>
:Po '''AND operacjos''' bus gautas skaičius:
:'''0011_1110_0010_0000_0000_0000_0000_0000''' '''AND'''
:'''1000_0000_0111_1111_1111_1111_1111_1111''' =
:'''0000_0000_0010_0000_0000_0000_0000_0000'''.
:Gauta mantisa 01 = 0.5*0 + 0.25*1 = 0.25. Pridėjus nematomą vienetą priekyje galutinis teisingas skaičius lieka:
:1 + 0.25 = 1.25.
:Eksponentė tampa lygi 0. 2^0 = 1.
:AND operacija veikia tokiu budu:
:1 AND 1 = 1,
:1 AND 0 = 0,
:0 AND 1 = 0,
:0 AND 0 = 0.
:Analogišku budu galima paversti mantisos bitus nuliais ir palikti tik eksponentę, prie kurios paskui galima pridėti arba atimti 8 bitus esančius viena bito pozicija į dešinę nuo kairės. Analogiškai galima panaikinti minuso ženklą su AND operacija. Arba suteikti minuso ženklą pridėjus 32 bitų skaičių '''1000_0000_0000_0000_0000_0000_0000_0000''' prie kokio nors teigiamo 32 bitų skaičiaus (kai pirmas bitas yra 0).
==Kaip FPU galėtų apseit be barrel shifters sudedant skaičius su skirtingom eksponentėm==
:https://en.wikipedia.org/wiki/Barrel_shifter
:Barrel Shifter gali stumti pavyzdžiui 32 bito skaičiaus bitus į kairę ar į dešinę ne per vieną bito poziciją, bet per kelias kaip, kad per dvi bito pozicijas ar per 4 bito pozicijas ar per 8 bito pozicijas.
:Sudedant 32 bitų single precision skaičius su skirtingomis eksponentėmis, mažesnio skaičiaus mantisos bitai turi būti pastumti į dešinę per tiek pozicijų koks yra tu dviejų skaičių eksponenčių skirtumas, atėmus eksponentę mažesnio skaičiaus iš eksponentės didesnio skaičiaus.
:Be barrel shifter dviejų 32 bitų floating point skaičių sudėtį su skirtingomis eksponentėmis galima atlikti pakeitus mažesnio skaičiaus eksponentę į denormal ir padauginus tą mažesnį skaičių iš skaičiaus, kurio fractional part yra vienetas (visa mantisa lygi nuliui, jeigu be nematomo vieneto) o eksponentė yra neigiamas skaičius gautas atėmus didesnio 32 bitų float skaičiaus eksponentę iš mažesnio 32 bitų float skaičiaus eksponentės. Tada šio skaičiaus eksponentė bus neigiama ir ją padauginus iš paversto į denormal mažesniojo skaičiaus, bus gautas rezultatas, kad mažesnio skaičiaus mantisa bus pastumta į dešinę tiek pozicijų koks yra tos neigiamos eksponentės skaičiaus modulis.
:Bet turbūt vis tiek reikės šiftinti mantisos bitus į dešinę, kad paversti mažesnio 32 bitų skaičiaus mantisą kaip pas denormal skaičius pastumta atitinkama bitų skaičių į dešinę, kad paskui pridėti gautą mantisą prie didesnio skaičiaus mantios, ir eksponentę po sudeties palikti didesnio skaičiaus, jeigu ji nepakils vienetu dėl sudeties.
:Tai tiesiog čia kaip veikia skaičių sudėtis su skirtingom eksponentėm. Buvau pagalvojęs, kad galima apseiti be barrel shifter, bet pasirodo, kad ne. Arba reikia šiftinti po vieną bitą (jeigu nėra barrel shifter circuit), kas gali pareikalauti nemažai laiko, jeigu sudedamų skaičių eksponentės stipriai skiriasi.
:Todėl iš esmės sudėtis gali būti ilgesnė nei daugyba net su barrel shifter, nes daugyba su padaryta schema galinčia padauginti mantisas iš vieno ciklo, bus daug greitesnė. Ten iš esmės ta daugybos schema turėtų ir užimti didžiausią dalį FPU. Senesni FPU daugino iš daug ciklų, nes negalėjo sau leisti daug transistorių daugybos transistorių schemai. Maždaug Pentium procesoriai pradėjo dauginti iš vieno ciklo, jei neskaičiuoti visokių įkrovimo, užkrovimo, išėmimo operacijų dviejų dauginamų skaičių.
:'''Update 1.''' Pagal kitą informaciją Pentium (1993 metų) procesorius daugino 3 kartus greičiau nei 387 FPU (apie 1986 metų), nes daugino ir shiftino ne po vieną bitą, o po tris bitus. Buvo sukurtos schemos dauginti iš 0, 1, 2, 3, 4, 5, 6, 7. Nes 3 bitai gali turėti 8 kombinacijas. Tai vietoje 64 ar 66 sudėčių (2 bitai apvalinimui su 80 bitų Extended precision) reikėjo sudėti tik 22 kartus. Ir šiftinimas buvo po 3 bitus į dešinę Su Pentium procesorium.
:Intel procesoriai Nahalem pradėjo dauginti iš vieno ciklo su gerokai sudetingesne dauginimo schema ir su daug daugiau tam reikalingų transistorių. https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
:Nahalem architektura yra 2008 metų. Variantai:
:Core i5-7xx
:Core i7-8xx
:Core i7-9xx Extreme.
:Tai patys pirmi Core procesoriai. Po jų sekė Core 2 procesoriai (2009 metų):
:Core 2 Solo
:Core 2 Duo
:Core 2 Quad
:Core 2 Extreme.
:Dauginimas kart 0 yra dauginimo praleidimas, jei kalbėti apie 64 bit * 64 bit sveikųjų skaičių daugybą.
:Dauginimas kart 2 yra pastumimas skaičiaus viena bito pozicija į kairę.
:Dauginimas kart 3 reikalauja specialios schemos. Arba pastumimas viena bito pozcija į kairę + jis pats, t. y. 2+1.
:Dauginimas kart 4 yra pastumimas skaičiaus dviem bito pozicijom į kairę.
:Dauginimas kart 5 yra dauginimas iš 4 + pats skačius.
:Dauginimas iš 6 yra dauginimas iš 8 + pats skaičius pastumtas į kairę viena bito pozicija ir paverstas į Two's compliment neigiamą skaičių (Padarius XOR su vienetų seka ir pridėjus vienetą). Kitaip tariant dauginimas iš 6 yra 8-2. Arba galima 4+2, tada pastumiamas dviem pozicijom į kairę ir pridedamas jis pats pastumtas viena pozicija į kairę.
:Dauginimas iš 7 yra 8-1. Pastumiamas skaičius į kairę trim bitų pozicijom ir prie jo pridedamas pats tas skaičius paverstas į neigiamą skaičių two's compliment formate. Pastumimas galimas daiktas vyksta per automatišką schemą ir taip pat pridedamas jis pats paverstas per XOR operacija su vienetais ir +1 (per automatišką schemą, kitaip tariant viskas vyksta per vieną ciklą).
:Todėl maksimaliai daugyba su pentium Gali vykti 3 kartus greičiau nei su 387. Bet realus pagreitėjimas gali būti ir 2x. Tai priklauso kaip ten visa ta dauginimo vidinė schema padaryta (iš skaičių nuo 0 iki 7).
:Jei FPU neegzistuoja, o yra reklama ar panašiai, tai kad sudauginti du 64 bitų skaičius, kurių mantisa yra 52 bitai, eksponentė 11 bitų, ir minuso ženklas 1 bitas, tai pagal antro skaičiaus pirmą (kairiausią) mantisos bitą pirmas skaičius arba paliekamas (jei tas bitas 1) arba vietoj jo rašomas 0 (ignoruojamas), jei tas bitas 0.
:Toliau, pagal antrą nuo kairės bitą antro skaičiaus, pirmas skaičius arba paliekamas, jei tas bitas 1 arba ignoruojamas, jei tas bitas 0. Paskui jei tas bitas buvo 1, tai pirmas skaičius pastumiamas viena bito pozicija į dešinę su shift right instrukcija ir pridedamas prie to nepastumto pirmo skaičiaus.
:Iš pradžiu pridedami 32 dešiniausi bitai nepastumto ir pastumto pirmo skaičiaus. Jeigu buvo carry iš jų sudeties. Tai sudedant kairiasias 32 bitų tas dvi dalis dar pridedamas carry, naudojant instrukciją '''ADD with Carry'''.
:Dabar yra 64 bitų skaičius saugomas dviejuose 32 bitų RAM adresuose.
:Toliau pagal antro dauginamo skaičiaus trečia bitą, pirmas skaičius pastumiamas trim pozicijom į dešinę ir pridedamas prie gauto 64 bito skaičiaus, jei tas antro dauginamo skaičiaus trečias bitas 1 ir nepridedamas, jei tas trečias bitas 0.
:Kiekvieną kartą nereikia šiftinti iki 52 kartų į dešinę. Užtenka išsisaugot kas kart pašiftintą viena poziciją pirmą 64 bitų skaičių (šiftinama du kartus, vieną kart kairiausi 32 bitai ir antrą kart dešiniausi 32 bitai).
:Tai iš viso reikia 52 šiftinimų *2 (nes šiftinama į dešine po 32 bitus) ir 52 sudėčių *2 (nes sudedama po 32 bitus). Tai iš viso reikia 52*2 + 52*2 = 104+104 = 208 operacijos. Bet kartais sudėties operacijos gali būti praleidžiamos, tai vidutiniškai reikia ne 104 sudeties opeacijų (32 bitų), bet 52, nes vidutiniškai pusė bitų antro skaičiaus nuliai ir pusė vienetai. Tai tada vidutiniškai reikia 104+52 = 156 operacijų, kad sudauginti du 64 bitų double precision skaičius.
:Tada realus procesoriaus dažnis vietoje 5 GHz yra apie 156*5 = 780 GHz.
:Single precision (32 bitų) su 23 bitų mantisą reikėtų 46 šift ir 46 add operacijų. Iš viso 92 operacijų maksimum. Bet jeigu vidutiniškai yra 23 sudeties operacijos, tai tada apie 23+46 = 69 opracijų. Free Pascal koduose galima sakyti 4 GHz procesorius daugina 64 bitų dauble precision skaičius naudodamas 4 ciklus ir tai taip pat įskaitant visokius įkrovimus iškraovimus į/iš FPU į/iš RAM atminties. Tai dėl to 780 GHz dažnis nustatytas gana tiksliai ir nereikia jo dauginti iš 4.
:Atradau tokią gudrybę:
:12345 * 12345 = 152399025
:ir
:1.2345 * 1.2345 = 1.52399025.
:Arba
:12345 * 14789 = 182570205,
:1.2345 * 1.4789 = 1.82570205.
:Tai iš esmės galima dauginti single precision mantisas kaip sveikuosius skaičius, o paskui tik padėti kablelį kur reikia (gauto rezultatą įterpti į float mantisą be priekinio vieneto).
:Tikrinimas:
:123456789123456789 * 123456789123456789 = 1.5241578780673678515622620750191e+34
:1.23456789 * 1.23456789 = '''1.52415787'''50190521.
:1.23456789123456789 * 1.23456789123456789 = 1.5241578780673678515622620750191.
:Visgi tikslumas nukentėjo - gudrybė neveikia.
:Antras tikrinimas:
:1.23456789123456 * 1.23456789123456 = 1.52415787806734,83700809383936.
:1.2345678 * 1.2345678 = '''1.524157'''6,5279684.
:Neveikia gudrybė. Dauginant "trumpesnius" skaičius ir tikslumas mažesnis.
:Bet vis tiek nušiftinus minėtą pirmą 64 bitų skaičių jau 32 bitų pozicijom į dešinę, to pirmo skaičiaus 32 dešiniausius bitus jau galima nešiftini ir neatlikinėti su jais sudeties operacijų. Taip dar sutaupant trečdalį laiko (nes pusė laiko dirbama su 64 bitais, o paskui likusią pusę laiko šiftinami ir sudedami tik 32 bitai prie dešinios 32 bitų dalies pirmojo skaičiaus). Todėl sutaupoma 1/3 operacijų. Iš to gaunasi, kad iš viso reikia ne 156 ciklų (dviejų double precision skaičių daugybai), o
:156 - 156/3 = 156 - 52 = 104 ciklų. Vadinasi tada procesoriaus 5GHz dažnis būtų ne 780 GHz, o 5*104 = 520 GHz.
:Tai yra, iš pradžiu loop šiftina ir sudeda 32 kartus kaip su 64 bitais, o paskui pereinama prie antros loop, kur šiftinami ir sudedami 32 bitai.
:Arba, kad nedaryti loop kas irgi naudoja laiką, nes dviem instrukcijom daugiau (decrement register and jump to loop begining if loop register flag not zero). Tai geriau surašyti visas instrukcijas iš eilės kiek kartų viską daryt. Tai užims daugiau RAM atminties, bet veiks greičiau, nes nereikės palaikyti loop vykdymo. Tai užims salyginai mažai RAM - iki vieno kilobaito. Bet gali veikti 1.5 karto ar net 2 kartus greičiau. Ir dar neužims vieno registro kaip counterio skaičiavimui kiek iteracijų atlikta. O jeigu procesorius dar ir negali skaičiuoti per tam skirtą registrą kaip to negali intel 8080, tai reikės dar perkraudinėti į akumuliatorių counter'inimo skaičių, kad pažiurėti ar tas skaičius ne nulis ir neįjungs Zero flag (kai bus nulis). Nors šiaip intel 8080 turi CMP instrukciją (CMP, Compare register with A). Tai tas pats kas atimti kitą registrą ar operand'ą iš atminties iš registro A (akumuliatoriaus), tik rezultatas neįdedamas į akumuliatorių. Tai iš esmės kol count koks nors registras po decrement instrukcijos nėra nulis, tol compare su registru A neįjungs Zero flag. Bet tada išeina į registrą A kiekvieną kartą reikia krauti sveiką skaičių 0 po kiekvienos šiftinimo ir sudeties operacijos.
:Ką tik pažiūrėjau [https://bitsavers.trailing-edge.com/components/intel/MCS80/98-153B_Intel_8080_Microcomputer_Systems_Users_Manual_197509.pdf intel 8080 manual] (51 psl.), tai visgi Zero flag yra '''set''', kai pasirinktas registras po ''decrement register'' instrukcijos tapo lygus nuliui. Tai praktiškai nereikia naujiems procesoriams jokių geresnių instrukcijų išeina. Tik FPU dauginimo ir sudeties bei atimties greitų instrukcijų užtenka. Nes iš mano samprotavimų, viską darant per mikrokodą, viskas veiks praktiškai tokiu pačiu greičiu kaip viską darant per RAM, tik truputi mažiau RAM ilgos instrukcijos užims kaip integer multiply pas intel 8088 per mikrokodą. Vis tiek daugiausiai vietos RAM užima paveiksliukai, teksturos ir kita data, o ne pačios instrukcijos, tai šiuolaikiniams laikams tai neaktualu. O RAM parinkus vieną ROW, ateina iš tos parinktos ROW bitai iš visų Column į kelis tūkstančius SRAM bitų registrų. Paskui iš tų SRAM (static RAM) registrų, kurie dažniausiai yra 4 bitų viename RAM čipe, atgal gražinami nutekėję tos DRAM row visos stulpelių (column) bitų reikšmės atgal į tas pačias vietas ir Row atjungiama, o tie nutekėję bitai į SRAM lasteles (registrus) niekur nedingsta iki tol kol nekreipiamasi į kitą Row. Todėl paskui procesorius ima informaciją ne iš DRAM lastelių, o iš SRAM lastelių. Ir įrašo į SRAM ląsteles, o parinkus naują ROW, tą informacija iš kelių tukstančių SRAM lastelių (registrų) per kelis ciklus grąžinama į visas column tos Row. Instrukcijos vykdomos dažniausiai nuosekliai ir dažniausiai peršokama ribose tos pačios DRAM ROW. Todėl vis naudojami tie patys SRAM registrai nutekintų bitų DRAM stulpelių iš tos pačios ROW. Dėl to DRAM praktiškai gali veikti greičiau nei bet koks cache ir naudoti daug mažiau energijos už cache, kuriam reikia milijonų SRAM lastelių, o ne tukstančių. Tik didesnis atstumas tarp CPU ir DRAM sulėtiną informacijos keliavimą iš DRAM esančių SRAM lastelių dėl šviesos greičio limito iš dalies. Tai, kad cache atmintis gaunasi visiškai beprasmiška, jei tinkamai panaudoti tą DRAM veikimą su SRAM registrais viduje. Kas teoriškai turėtų taip ir būti.
:Jeigu 5 GHz procesorius iš tikro veikia 500 GHz dažniu, o šviesos greitis nėra sumeluotas, tai 1 metrą šviesą gali nueiti 300 milijonų kartų arba 300 MHz. Atstumas tarp procesoriaus ir RAM yra apie 10 cm, tai tada RAM dažnis max gali buti apie 3 GHz. Bet jeigu procesorius veikia 500 GHz, nes jos pagrindinė dalis yra labai sumažinta dėl naujesnės litografijos, o pats CPU base core užima ne daugiau nei milijoną tranzistorių, tai išeitų, kad nespėtų imti iš RAM atminties informaciją CPU dėl šviesos greičio abribojimu (dėl atstumo tarp CPU ir RAM). Tai šita dalyką galima būtų išspręsti, jei visos programos ir programavimo kalbos naudotų 64 bitų ar 32 bitų skaičių daugybai tam skirtą RAM adresą vieno kilobaito ar mažiau, kuris visada būtų adresuojamas ne per DRAM, bet per SRAM esančią pačiame mikroprocesoriuje. Pavyzdžiui tos SRAM kilobaitas galėtų būti pačioje RAM pradžioje, tam skirus 256 adresus saugančius po 32 bitus. Tai tiesiog pirmi 8 (ar 10, nes 2 bitai reiškia 4 baitus..., o procesorius ne 8, o 32 bitų...) kairiausių bitų būtų nukreipti į tą SRAM atminti, o like 24 (ar 22) bitai einantys į SRAM atmintį būtų nuliai. Jei nors vienas iš dešiniausių 24 (ar 22 bitų) būtų ne nulis, tai Adresas iš CPU nukeliautų ne į vidinę SRAM atmintį, o į išorinę lėtesnę DRAM atmintį. Tai tiesiog prieš pradedant daugybą, visos programos galėtų naudoti tą SRAM atmintį pernešant ten du dauginamuosius į atitinkamas vietas, kuriuos jau supa daugybos kodas... Ir su 500 GHz mažyčiu procesorium (be visokių nereikalingų kvailų instrukcijų) galėtų užtekti šviesos greičio būtent tokiam dideliam dažniui, kad atlikti double ar single precision daugybą. Intel iš tikro sudetingos instrukcijos asembleryje galėtų būti kaip aukštesnio lygio programavimo kalba, kuri paskui visas tas instrukcijas paverčia į 8080 CPU tipo instrukcijas, o FPU daugybą perkelia į tam skirtą SRAM atminties vietą, o rezultatą gražina lyg niekur nieko, ten kur ištransliavo asembleris... O vėliau ir aukštesnio lygio programavimo kalbos kaip C ir Pascal.
:Ne. Visgi skaičiavimo trukmė sumažėja ne trečdaliu, o ketvirtadaliu. Tuomet 5 GHz procesoriaus dažnis būtų ne 520 GHz, o
:(156 - 156/4) * 5 (GHz) = (156 - 39) * 5 = 585 GHz.
:'''Update 2.''' Dar yra toks variantas, kad 5 GHz procesorius dirba apie 50 GHz dažniu ir neturi FPU. Iš esmės rodos kodus kaip daugybos ir sudeties benchmarkai su viena daugyba ir/ar sudėtimi buvo skaičiuojami per apie 25-30 sekundžių pirmą kartą su 1 milijardu iteracijų. Jei viena operacija užima 4 ciklus ant 4 GHz procesoriaus tai viena daugyba ir sudėtis užimtu 8 ciklus. O 25-30 sekundžių yra 100-120 ciklų. Tai 120/8 = 15 ciklų daugybai ir sudečiai, jei tik daugybai tai 30 ciklų. Bet buvau ir be daugybos tik, kur yra sudetis ir vis tiek pat kaip su daugyba. Be to ten maži skaičiai iš pradžiu ėjo, tai juos galėjo greičiau daugint. Tik kur nebuvo daugybos, kodėl irgi 25-30 sekundžių darė sudeties operacijas milijardą kartų? Tai gal vistiek ten 20-25 sekundes pirmą kartą kompiliavimas užima ir FPU iš tikro yra. Arba sudetis yra ilga nes sudedami skaičiai su skirtingom eksponentėm ir reikalingas šiftinimas per kelis ar keliasdešimt bitų. Tai visgi gali būti, kad Tikrasis CPU dažnis yra ne 4 ar 5 GHz, o apie 40-50 GHz ar net 100 GHz. Jeigu FPU neegzistuoja. Tada kvadratinę šaknį ir dalyba tikrai ne greičiau bus su Niutono iteracijomis daryti, o taikyti kitus metodus. Dalyba tai ir taip bus beveik toks pat greitis kaip daugyba (jei FPU nėra). O kvadratinei šakniai kažką reik sugalvot.
:Vienintelis įrodymas, kur sinuso Teiloro eilutė pirmą kartą labai greit skaičiuojama. Sudedami sinusai su įvairiom reikšmėm. Problema ta, kad negalima patikrinti teisingumo tų visų ilgo kodo skaičiavimu, gal kažką nukerta ten... Kiti kodai kaip parabolės ilgis ar integralų sumos duoda patikrinamą per integralus rezultatą, bet jie ir skaičiuojami pirmą kartą ilgiau nei antrą kartą.
:Tai geriau kur nereikia taikyti trumpesnes daugybas su tuo 50 GHz procesorium, ką tuomet išeitų žaidimų kurėjai ir game engines kūrėjai ir daro.
:Intel387TM SX MATH COPROCESSOR
:https://datasheets.chipdb.org/Intel/x86/387/datashts/24022509.PDF
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240225-009.pdf
:Intel 387 DX MATH COPROCESSOR
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005.pdf
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005_alt.pdf
:https://ardent-tool.com/CPU/Docs_Intel.html#387
:Intel 386
:www.digchip.com/datasheets/download_datasheet.php?id=59667&part-number=386DX
:https://ardent-tool.com/CPU/docs/Intel/386/datasheets/231630-011.pdf
:https://www.dosdays.co.uk/media/intel/1986_80386_Hardware_Reference_Manual.pdf
:https://www.bitsavers.org/components/intel/80386/230985-001_80386_Programmers_Reference_Manual_1986.pdf
:https://pdos.csail.mit.edu/6.828/2018/readings/i386.pdf
:https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf
===Intel 386 procesoriaus suderinamumas su 286 ir 8086===
:Skaitant čia https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf 129 puslapyje ir ankstesnius kitus puslapius, galima suprasti, kad intel 386 CPU iš tikro niekaip neoptimizuoja apatinės ir viršutinės dalies (16 bitų) RAM adreso saugančio 32 bitus. Pasakyta, kad jeigu dedama apatinė 16 bitų dalis, tai aktyvuojamos kojelės BE0 ir BE1, atsakančios už perdavimą apatinės 16 bitų dalies į 32 bitų RAM vieną adresą. Jeigu aktyvuojamos kojelės BE2 ir BE3, tai viršutiniai 16 bitų dedami ir į apatinę ir į viršutinę dalį 32 bitų RAM vieno adreso. Amerikiečiams labai svarbu būti konkurencingams.
:Tai jeigu pernešti visą intel 286 procesoriaus kodą į 386 procesorių, tai jeigu naudoti tik 32 bitų registrus (EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP) ir 286 procesoriaus 16 bitų kodą įrašyti tik į apatinius 16 bitų visuose adresuose, tai tos visos 16 bitų programos neveiks, nes viršutiniai 16 bitų bus nuliai kai bus pridedama neigiama displacement '''two's complement''' formate. O jeigu visi viršutinai 16 bitų vieno 32 bitų RAM adreso bus vienetai, kad veiktų neigiama displacement, tai tada neveiks teigiama displacement, nes pas teigiama displacement reikia, kad visi viršutiniai 16 bitų būtų nuliai.
:Dar gali būti, kad intel 386 ir intel 286 procesoriai naudoja visiškai skirtingus opcode'us. Su '''REP'''eat prefixu kai kurių instrukcijų 386 procesoriuje nėra, kurios yra 8086 procesore. Vietoje to, 386 naudoja REPeat instrukcijas kaip atskirą opcode'ą nuo ne Repeat instrukcijų. Kitų opcode'ų nelyginau, norintys gali palyginti ir patikrinti kas kaip suderinta. Bet manau ten visi opkodai skirtingi pas 386 nuo 286 ir 8086.
:Gali būti, kad kažkaip dedant į 386 procesoriaus 16 bitų registrus (AX, BX, CX, DX, DI, SI, BP, SP) ir panaudojant kojelę BS16 ant 386 CPU, visi 16 bitų kodai iš 286/8086 veiks su 386 procesorium. Tada 386 procesorius visada skaitys tik iš minėtų 16 bitų registrų (AX, BX, CX, DX, DI, SI, BP, SP). Bet tada tai reiškia, kad kai aktyvuota BS16 kojelę, 386 pradeda suprasti 286 procesoriaus opcode'us iš žemutinių 16 bitų RAM adreso 32 bitų. Į viršutinius 16 bitų to paties RAM adreso tada išeina niekas nededama. Aš tai sakyčiau, kad intel inžinieriai nesivargino palaikyti 286 procesoriaus opkodų, o teoretikams kuriantiems instrukcijas ir datasheets ir user's manual'us pasakė, kad viskas veiks maksimaliai optimaliai ir optimizuotai. Todėl greičiausiai reikia perrašinėti 286 visas programas 386 procesoriams arba kai buvo kuriamos programos, buvo kuriamos iškart ir 386 ir 286 procesoriui, o senesnės programos 8086 tuomet arba neveik su 386 CPU arba buvo per kokį nors asemblerį konvertuojamos į 386 procesoriaus kodą paleidus, kas turėdavo atimti laiko paleidimui. Kad intel 8080 procesoriaus opkodai neveikia su 8088/8086 pasakyta wikipedijoje. Todėl nėra prasmės iš 8080 registrų, bet gal jie duoda pasirnkti baitus (A, B, C, D, E, H, L registrai). Arba ten tik girti suderinamumą su 8080 ir 286/8088, bet iš tikro tuomet yra tik 32 bitų 8 registrai EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP. Be to 8086 galėjo būti neveikiantis procesorius, dėl to IBM pasirinko 8088. 8086 deda į RAM adresą 16 bitų, o 8088 į vieną RAM adresą deda tik 8 bitus. O jeigu skaičius yra 16 bitų, tada deda į du RAM adresus kaip intel 8080 CPU.
[https://lt.wikibooks.org/wiki/Wikibooks:Knygos/High_Level_Shading_Language High Level Shading Language]
e2qyda01b4wf1r6dka1mq1955lbrozl
59029
59028
2026-09-10T09:28:19Z
Paraboloid
1294
/* Intel 386 procesoriaus suderinamumas su 286 ir 8086 */
59029
wikitext
text/x-wiki
==Dešimtainių skaičių vertimas į dvejetainius skaičius==
[https://www.google.com/search?client=opera&q=single+precision&sourceid=opera&ie=UTF-8&oe=UTF-8&udm=50&fbs=ABfTbFVyMZGZf1hfvX9uKjN_-G8c4u0nXx4bEIpwm1lnNH832VTJOOCxW_fyN-Q_ezyf8gKjm3rhh_G8jdZ2Q6tji4z8Mva6cfvBj5rpPch2IC955IbzrbwHf6tkbFbd-nuFdbKth9Y-58t5LtnYgTA4TWDFYCUNl4d7F9XBAwiEq2T2c7SNgrvMxYiRBXL4fnQnnTOewx28&aep=10&ntc=1&sxsrf=APpeQnvZo3fX2PwO881uSdWsoRELP18NhQ%3A1788348274309&mstk=AUtExfAJ0Kp9BqT3EJbzHOz3MHMalnyXkoOTLvgho5ZQ9lzjrnmmUFCgux_RVe7bouRlyBSGpkUcLpvfZ39N45TUoor6TyyEW7_PpE7eOwP-kU7IFxE6monk-HtsDXkDu7g362AN4NTW3oRjfwn9OtD0BIj2r4QAVauMhDPCIAiWgLimDuate6cf2o6bB5R-6-vzcCqbCju1KJQTdR4HSF9puZ_HCG1hXQ8QaCIqD1LJ_HWonNnZGma6Biu3I1wWFPy6esLnfaKpIX6VFWZsArfQN97XGipwkH7FLKCyZiev5rZBrhE8KincMthP7B9AJDugKLy8qJfORQ7PQR2GslAaCLnkechjBCnXfrmSohwJskbEWU9db6Q279uO4dGu0eI2YUvpEwoQUqkbREqJqrGpJQWFy7WoTzI93_o_2I61PU1FTiuGq-4AVN7erT0WmuWizHDEE2XsTes&aioh=3&csuir=1&atvm=2&mtid=dQeYaumuDL2Ixc8Px6Ts0QE DI atsakymas]
:Single-precision is a 32-bit computer number format that stores fractional and large numbers using a sign bit, an exponent, and a mantissa. Structure of Single-Precision (Binary32) According to Wikipedia, “Single-precision floating-point format (sometimes called FP32, float32, or float) is a computer number format, usually occupying 32 bits in computer memory.” The 32 bits are divided into three specific parts:
*Sign bit (1 bit): Determines if the number is positive (0) or negative (1).
*Biased exponent (8 bits): Stores the magnitude with an excess-127 bias, giving an actual exponent range of -126 to +127.
*Mantissa/Significand (23 explicitly stored bits): Represents the precision digits of the number, with an implicit leading 1 for normalized values.
:Precision and Applications. Decimal Precision: Provides about 7 to 8 decimal digits of accuracy. Most users on StackOverflow agree that “Single precision typically offers 7-8 decimal digits of precision.”
:'''Dešimtainių skaičių vertimas į dvejetainius.'''
:Step-by-Step Conversion Framework.
*Step 1: Determine the Sign Bit (1 bit). If the number is positive, the sign bit is 0. If the number is negative, the sign bit is 1. Example (-12.625): It is negative, so the sign bit is 1.
*Step 2: Convert the Absolute Value to Binary. Separate the number into its integer and fractional parts, convert both to binary, and combine them.
:Integer part '''12''': (12/2 = 6) ('''rem'''ainder 0), (6/2 = 3) (rem 0), (3/2 = 1) (rem 1), (1/2 = 0) (rem 1) <math>\rightarrow </math> '''1100'''.
:Fractional part '''0.625''': (0.625 * 2 = 1.25) (1), (0.25 * 2 = 0.5) (0), (0.5 * 2 = 1.0) (1) <math>\rightarrow </math> '''101'''.
:Example Combined: '''12.625''' = '''1100.101'''.
*Step 3: Normalize the Binary Number. Shift the binary point so that exactly one non-zero digit (which will always be 1) remains to its left. Count how many places you moved the point. This count is your exponent (E).
:Move the point 3 places to the left: '''1100.101''' <math>\rightarrow </math> '''1.100101'''.
:Example Exponent: Because we moved it 3 places, E = 3.
*Step 4: Calculate the Biased Exponent (8 bits) IEEE 754 single-precision uses a bias of 127. Add your exponent E to this bias, and convert the resulting integer to an 8-bit binary number.
:Calculation: 127 + 3 = 130.
:Convert 130 to 8-bit binary: '''130''' = '''10000010'''.
*Step 5: Extract the Mantissa/Significand (23 bits).
:Take the fractional bits from your normalized number (everything to the right of the binary point). Drop the leading 1. If the bits are fewer than 23, pad the right side with zeros until you reach exactly 23 bits.
:Normalized bits: 1.100101 <math>\rightarrow </math> Drop the ''1.'' (atmesti kairiausią vienetą ir tašką po jo) to get '''100101'''.
:Example Pad to 23 bits: 10010100000000000000000.
:'''Final Assembly.'''
:Combine the three components in order: Sign (1 bit) + Exponent (8 bits) + Mantissa (23 bits).
:Component_____________Bits______________Value for -12.625_______
:Sign____________________1_______________________1______________
:Exponent________________8____________________10000010__________
:Mantissa________________23______________10010100000000000000000
*Final 32-bit Binary String: '''11000001010010100000000000000000'''.
*Hexadecimal Representation: '''C1450000'''.
:Sign: '''1'''1000001010001010000000000000000.
:Exponent: 1'''10000010'''10001010000000000000000.
:Mantissa: 110000010'''10010100000000000000000'''.
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Mantissa: 100101 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 0.578125.
:8 * 0.578125 = 4.625.
:8 * 1 = 8.
:8 + 4.625 = 12.625.
:Reikia gražinti paimtą vienetą priekyje ('''12.625''' = '''1100.101'''; '''1100.101''' <math>\rightarrow </math> '''1.100101'''):
:8 * (1 + 0.578125) = 8 * 1.578125 = 12.625.
:-12.625 = -1.2625 * 10^1.
:Čia mantisa 0.2625, o ekspontentė 1 dešimtainėje sistemoje. Skačių 0.2625 reikia kaskart dauginti iš 2.
:0.2625*2 = 0.525 (ne vienetas, reiškia pirmas skaitmuo 0),
:0.525*2 = 1.05 (vienetas, reiškia antras skaitmuo 1),
:0.05*2 = 0.1 (ne vienetas, reiškia trečias skaitmuo 0),
:0.1*2 = 0.2 (ne vienetas, reiškia ketvirtas skaitmuo 0),
:0.2*2 = 0.4 (ne vienetas, reiškia penktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia šeštas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia septintas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia aštuntas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia devintas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia dešimtas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia vienuoliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia dviliktas skaitmuo 1),
:0.2*2 = 0.4 (ne vienetas, reiškia tryliktas skaitmuo 0),
:0.4*2 = 0.8 (ne vienetas, reiškia keturioliktas skaitmuo 0),
:0.8*2 = 1.6 (vienetas, reiškia penkioliktas skaitmuo 1),
:0.6*2 = 1.2 (vienetas, reiškia šešioliktas skaitmuo 1).
:Tada galutinis skaičius '''0.2625''' yra toks:
:0.0100001100110011.
:Kad gauti didesnį tikslumą, procesą reikia kartoti begalo arba iki reikiamo tikslumo.
:Skaičiau '''0.2625''' reikšmė yra:
:0.0100001100110011 =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*0 + 0.015625*0 + 0.0078125*1 + 0.00390625*1 + 0.001953125*0 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 =
:= 0.26171875 + 0.0007781982421875 = '''0.26249'''69482421875.
:Taigi, 0.26249 labai panašus į 0.2625.
:Vadinasi '''1.2625''' dvejetainėje sistemoje apytiksliai yra '''1.0100001100110011'''.
:Eksponentę 10 reikia versti taip:
:10 = 8 + 2. Tada 10 dvejetainėje sistemoje yra 00001010 (1*0 + 2*1 + 4*0 + 8*1 + 0 + 0 + 0 + 0 = 10).
:Bet eksponentė single precision formate gali būti tik, 0, 1, 2, 4, 8, 16, 32, 64, 128 ir taip toliau.
:Arba reikia daryti taip:
:10/2 = 5 (rem 0),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0)
:1/2 = 0.5 (rem 1).
:Va štai šitaip ir gaunamas skaičius 10. Tai yra 1010.
:Skaičius 1010 pakeičiamas skaičiu 1.010 su eksponente 3 (2^3 = 8).
:Tada skaičius 1.010 iškoduojamas taip:
:1 + 0.5*0 + 0.25*1 + 0.125*0 = 1.25.
:Padauginama iš dviejų pakelta eksponente 3:
:1.25 * 2^3 = 1.25 * 8 = 10.
:Tuomet
:1.2625 * 10 = 12.625.
:'''Grįžimas prie pavyzdžio.'''
:Patikrinimas (-12.625).
:Exponent: 10000010 = 128*1 + 64*0 + 32*0 + 16*0 + 8*0 + 4*0 + 2*1 +1*0 = 130. 130-127 = 3. 2^3 = 8.
:Skaičiaus 1100.101 kablelį galima pastumti 4 pozicijom į kairę ir eksponentę duoti 4. Mantisa tada yra 0.1100101, o eksponentė yra 4.
:0.5*1 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*0 + 0.0078125*1 = 0.7890625.
:2^4 * 0.7890625 = 16 * 0.7890625 = '''12.625'''.
:Bandomas kitas skaičius 24:
:24/2 = 12 (rem 0),
:12/2 = 6 (rem 0),
:6/2 = 3 (rem 0),
:3/2 = 1 (rem 1),
:1/2 = 0.5 (rem 1).
:Reiškia 24 yra 11000 (16*1 + 8*1 + 4*0 + 2*0 + 1*0 = 24).
:Bandomas kitas skaičius 19:
:19/2 = 9 (rem 1),
:9/2 = 4 (rem 1),
:4/2 = 2 (rem 0),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 19 yra 10011 (16*1 + 8*0 + 4*0 + 2*1 + 1*1 = 19).
:Bandomas skaičius 22:
:22/2 = 11 (rem 0),
:11/2 = 5 (rem 1),
:5/2 = 2 (rem 1),
:2/2 = 1 (rem 0),
:1/2 = 0.5 (rem 1).
:Reiškia 22 yra 10110 (16*1 + 8*0 + 4*1 + 2*1 + 1*0 = 16+4+2 = 22).
:Trumpas paaiškinimas kodėl eksponentė saugoma formate nuo 1 iki 254, pridedant 127 prie -126 ir iki 127. Tai greičiausia yra todėl, kad sudedant single precision skaičių tokiame formate kokiame jis yra, jo reiikšmė gali būti gana tiksliai traktuojama kaip sveikojo skaičiaus reikšmė. Su kai kuriais pakeitimais.
:'''-12.625''' yra '''11000001010010100000000000000000'''.
:Pridėjus 127 ('''01111111''') prie eksponentės 3 ('''00000011''') gauname 130 ('''10000010'''):
:'''00000011'''+
:'''01111111'''=
:'''10000010'''.
:Vadinasi tada skaičius '''-12.625''' tampa
:Mantissa: 1100101 = 1 + 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 + 0.03125*0 + 0.015625*1 + 0.0078125*0 = 1.578125;
:2^130 = 1.3611294676837538538534984297271e+39 = 1.3611294676837538538534984297271 * 10^39.
:1.578125 * 2^130 = 2.148032441188424050612552209413e+39.
:O sveikasis skaičius '''11000001010010100000000000000000''' be minuso yra '''01000001010010100000000000000000''' ir užrašomas taip:
:1*0 + 2*0 + 4*0 + 8*0 + 16*0 + 32*0 + 64*0 + 128*0 + 256*0 + 512*0 + 1024*0 +
:+ 2048*0 + 4096*0 + 8192*0 + 16,384*0 + 32,768*0 + 65,536*0 + 131,072*0 +
:+ 262,144*1 + 524,288*0 + 1,048,576*1 + 2,097,152*0 + 4,194,304*0 + 8,388,608*1 + 16,777,216*0 + 33,554,432*1 +
:+ 67,108,864*0 + 134,217,728*0 + 268,435,456*0 + 536,870,912*0 + 1,073,741,824*0 +
:+ 2,147,483,648*1 + 4,294,967,296*0 =
:= 0 + 0 + 43,253,760 + 0 + 2,147,483,648 = 2,190,737,408.
:Kažka supainiojau. Atrodė, kad eksponentė beveik tas pats kas didesni skaičiai priekyje.
==Skaičiavimas inverse square root su C kodu==
:Čia https://en.wikipedia.org/wiki/Fast_inverse_square_root aiškinama kaip naudojant sveikuosius skaičius galima apsakičiuoti <math>\frac{1}{\sqrt{x}}.</math>
:"The fast inverse square generates a good approximation through integer operations by adding and subtracting the integer form of floating-point numbers, and taking a square root by dividing by two (which is just a right-shift)."
:Iš čia https://lt.wikibooks.org/wiki/Sekos_riba#Greitas_šaknies_iš_skaičiaus_a_traukimo_būdas gauta formulė
:<math>y_{n+1} = \frac{y_{n}\left(3-xy_n^2\right)}{2}. \quad (4)</math>
:<math>y_n\approx \frac{1}{\sqrt{x}}.</math>
:Ją dar galima pagreitint, pakeitus z = x/2. Tada ši formulė tampa tokia:
:<math>y_{n+1} = y_{n}\left(1.5 - zy_n^2\right). \quad (4.1)</math>
:Programavimo kalbos C kodas skaičiuoti <math>\frac{1}{\sqrt{x}}</math> yra toks:
<syntaxhighlight lang="c">
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
</syntaxhighlight>
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Šito kodo paaiškinimas yra toks:
:Skaičius '''i''' yra ''long integer'' (32 bitų sveikasis skaičius, https://en.wikipedia.org/wiki/C_data_types).
:Skaičiai '''x2''' ir '''y''' yra floating point skaičiai (32 bitų arba 64 bitų). Jeigu 32 bitų, tai tada tai yra single precision 32 bitų float skaičiai.
:Konstanta threehalfs = 1.5F yra 32 bitų single precision skaičius 1.5.
:Skaičius '''number''' yra 32 bitų single precision float ir paduodamas funkcijai. Kitaip tariant '''number''' yra '''x''', o '''y''' bus <math>y\approx \frac{1}{\sqrt{x}}.</math>
:'''x2''' yra '''number'''*0.5 = z.
:Eilutė
i = * ( long * ) &y;
:paverčia ''floating point number'' '''y''' į ''long integer number'' '''i'''. Dabar i yra toks pat kaip y, tik laikomas kaip sveikasis skaičius, o ne slankaus kablelio skaičius. Simbolis * yra pointer to memory address, skirtas įdėti į tą adresą kažką, o ženklas & skirtas paemimui iš tam tikro RAM adreso (arba atvirkščiai, * paėmimui, o & įdėjimui, gerai neatsimenu).
:Todėl ši eilutė gali būti suprantama kaip
:( long * ) &y
:paimti iš y RAM adreso y reikšmę ir įdėti į ''long integer'' (kol kas be pavadinimo) RAM adresą.
:Paskui iš to ''long integer'' RAM adreso įdėti į sveikojo skaičiaus '''i''' RAM adresą (ši kodo dalis "i = *"). Kaip sakyta, iš esmės floating number '''y''' paverčiamas į long integer number '''i'''.
:Toliau eilutė
i = 0x5f3759df - ( i >> 1 );
:reiškia pastumti sveikąjį skaičių '''i''' viena pozicija į dešinę ir paskui atimti iš sveikojo skaičiaus 0x5f3759df ir rezultatą įdėti į skaičiaus '''i''' adresą. Skaičius 5f3759df turi 8 hexodecimal skaitmenis. Vienas heksodecimal skaitmuo sudaro 4 bitus.
:5 yra 0101, f yra 1111, 3 yra 0011, 7 yra 0111, 5 yra 0101, 9 yra 1001, d yra 1101 (nes A yra 1010, B yra 1011, C yra 1100), f yra 1111. Todėl
:5F3759DFh = 0101,1111,0011,0111,0101,1001,1101,1111;
:5F3759DFh = 01011111001101110101100111011111.
:Gale raidė h reiškia, kad tai hexodecimal skaičius.
:Ši eilutė:
y = * ( float * ) &i;
:paverčia long int '''i''' į float, o paskui tą float perkelią į '''y'''.
:Toliau daromos Niutono iteracijos:
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
:čia y jau turi apytikslią reikšmę <math>\frac{1}{\sqrt{\text{number}}}.</math>
===Worked example===
As an example, the number <math>x=0.15625</math> can be used to calculate <math display=inline>\frac{1}{\sqrt{x}} \approx 2.52982</math>. The first steps of the algorithm are illustrated below:
0011_1110_0010_0000_0000_0000_0000_0000 Bit pattern of both x and i
0001_1111_0001_0000_0000_0000_0000_0000 Shift right one position: (i >> 1)
0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
0100_0000_0010_0111_0101_1001_1101_1111 The result of 0x5F3759DF - (i >> 1)
Interpreting as [[IEEE 754|IEEE]] 32-bit representation:
0_01111100_01000000000000000000000 1.25 × 2<sup>−3</sup>
0_00111110_00100000000000000000000 1.125 × 2<sup>−65</sup>
0_10111110_01101110101100111011111 1.432430... × 2<sup>63</sup>
0_10000000_01001110101100111011111 1.307430... × 2<sup>1</sup>
Reinterpreting this last bit pattern as a floating point number gives the approximation <math>y=2.61486</math>, which has an error of about 3.4%. After one iteration of Newton's method, the final result is <math>y=2.52549</math>, an error of only 0.17%.
:'''Patikrinimas.'''
:<math>x=0.15625.</math>
:Sing: '''0'''011_1110_0010_0000_0000_0000_0000_0000 (1 bitas).
:Exponent: 0'''011_1110_0'''010_0000_0000_0000_0000_0000 (8 bitai).
:Mantissa: 0011_1110_0'''010_0000_0000_0000_0000_0000''' (23 bitai).
:Exponente yra '''01111100'''. Tai yra
:128*0 + 64*1 + 32*1 + 16*1 + 8*1 + 4*1 + 2*0 + 1*0 = 124.
:124-127 = -3. Tada eksponentė yra '''-3'''. 2^(-3) = 0.125.
:Mantisa yra '''010_0000_0000_0000_0000_0000'''.
:Arba '''101''' = 1 + 0.5*0 + 0.25*1 = '''1.25'''.
:1.25 * 0.125 = 0.15625.
:Visi bitai pastumiami viena bito pozicija į dešinę. Tai tas pats kas padalinti iš dviejų mantisą ir eksponentę. Po pastumimo 32 bitų skaičius tampa:
:0001_1111_0001_0000_0000_0000_0000_0000
:Eksponentė tampa 001_1111_0 arba '''00111110'''. Tai yra 128*0+64*0+32*1+16*1+8*1+4*1+2*1+1*0 = 62.
:62-127 = -65. Padauginus iš 2^(-65) = 2.7105054312137610850186320021749e-20 mantisoje nieko neliks.
:Mantisa tapo 001_0000_0000_0000_0000_0000, kas yra
:1001 = 1 + 0.5*0 + 0.25*0 + 0.125*1 = 1.125.
:1.125 * 2^62 = 5,188,146,770,730,811,392.
:1.125 * 2^(-65) = 3.0493186101154812206459610024467e-20 = 3.0493186101154812206459610024467*10^(-20).
:Vat ir išlindo yla iš maišo. Jei nereikėtų prirašyt priekyje vienetoje mantisoje, o eksponentė butų vienetu didesnė, tai rezultatas būtų
:1001 = 0.5*1 + 0.25*0 + 0.125*0 + 0.0625*1 = 0.5 + 0.0625 = 0.5625. (0.5625*2 = 1.125)
:Eksponentė būtų vienetu didesnė (63 arba -64 po atėmimo 127).
:0.5625 * 2^63 = 5,188,146,770,730,811,392.
:0.5625 * 2^(-64) = 3.0493186101154812206459610024467e-20.
:Vadinasi išekstraktuojant atskirai eksponentę ir mantisą ir pastumus mantisos bitus į dešine viena pozicija, į kairiausį bitą turi įeitį vienetas. O prie eksponentės prisidėti vienetas, jeigu norima turėti tą patį skaičių.
:'''Magiškas skaičius reikalingas šiam darbui.'''
:Magiškas skaičius yra
:0101_1111_0011_0111_0101_1001_1101_1111 The magic number 0x5F3759DF
:Sing: '''0''' (1 bitas).
:Exponent: '''101_1111_0''' (8 bitai).
:Mantissa: '''011_0111_0101_1001_1101_1111''' (23 bitai).
:Eksponentė yra 10111110 = 128 + 0 + 32 + 16 + 8 + 4 + 2 + 0 = 190. Atėmus 127 ji tampa 190-127 = 63.
:Mantisa yra
:011_0111_0101_1001_1101_1111 =
:= 0.5*0 + 0.25*1 + 0.125*1 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.431640625 + 0.0007781982421875 + 0.00078868865966796875 + 0.00000083446502685546875 = 0.43320834636688232421875.
:Viso skaičiaus reikšmė yra:
:1.43320834636688232421875 * 2^190 = 2.2490986495375580205008307141844e+57 = 2.2490986495375580205008307141844 * 10^57.
:Arba:
:1.43320834636688232421875 * 2^63 = 13,219,013,784,867,176,448 = 1.3219013784867176448 * 10^19.
:Kaip sakyta, 32 bitų skaičius pastumtas viena bito pozicija į dešinę atimamas iš magiško skaičiaus kaip atiminėjami sveikieji skaičiai.
:0101_1111_0011_0111_0101_1001_1101_1111-
:0001_1111_0001_0000_0000_0000_0000_0000=
:0100_0000_0010_0111_0101_1001_1101_1111.
:Gautas skaičius 0100_0000_0010_0111_0101_1001_1101_1111.
:Sign bit: '''0'''100_0000_0010_0111_0101_1001_1101_1111.
:Exponent: 0'''100_0000_0'''010_0111_0101_1001_1101_1111.
:Mantissa: 0100_0000_0'''010_0111_0101_1001_1101_1111'''.
:Eksponentė yra '''10000000''' = 128. Atėmus 127, eksponentė yra 128-127 = 1.
:Mantisa yra '''010_0111_0101_1001_1101_1111''' =
:= 0.5*0 + 0.25*1 + 0.125*0 + 0.0625*0 + 0.03125*1 + 0.015625*1 + 0.0078125*1 + 0.00390625*0 + 0.001953125*1 + 0.0009765625*0 +
:+ 0.00048828125*1 + 0.000244140625*1 + 0.0001220703125*0 + 0.00006103515625*0 + 0.000030517578125*1 + 0.0000152587890625*1 +
:+ 0.00000762939453125*1 + 0.000003814697265625*0 + 0.0000019073486328125*1 + 0.00000095367431640625*1 +
:+ 0.000000476837158203125*1 + 0.0000002384185791015625*1 + 0.00000011920928955078125*1 =
:= 0.306640625 + 0.0007781982421875 + 0.00001049041748046875 + 0.00000083446502685546875 = 0.30743014812469482421875.
:Gautas skaičius yra:
:1.30743014812469482421875 * 2^1 = 2.6148602962493896484375.
:Arba:
:1.30743014812469482421875 * 2^128 = 4.4489542538766432951275906695431e+38 = 4.4489542538766432951275906695431 * 10^38.
:Toliau tereikia daryti iteracijas pagal formulę:
:y = y * ( threehalfs - ( x2 * y * y ) );
:y_0 = 2.6148602962493896484375. z = x/2 = 0.15625/2 = 0.078125.
:y_1 = y_0 * (1.5 - z * y_0 * y_0) =
:= 2.6148602962493896484375 * (1.5 - 0.078125 * 2.6148602962493896484375 * 2.6148602962493896484375) =
:= '''2.52'''54863388218057175296506574507.
:Tikroji reikšmė:
:1/(0.15625)^0.5 = 2.5298221281347034655991148355462.
:Antra iteracija:
:y_2 = y_1 * (1.5 - z * y_1 * y_1) =
:= 2.5254863388218057175296506574507 * (1.5 - 0.078125 * 2.5254863388218057175296506574507 * 2.5254863388218057175296506574507) =
:= '''2.5298'''109880258619023169570466177. 2.5298109880258619023169570466177
:Trečia iteracija:
:y_3 = y_2 * (1.5 - z * y_2 * y_2) =
:= 2.5298109880258619023169570466177 * (1.5 - 0.078125 * 2.5298109880258619023169570466177^2) =
:= '''2.529822128'''0611201246242590927426. 2.5298221280611201246242590927426
:10 pirmų teisingų skaitmenų (beveik 11).
:Ketvirta iteracija:
:y_4 = y_3 * (1.5 - z * y_3 * y_3) =
:= 2.5298221280611201246242590927426 * (1.5 - 0.078125 * 2.5298221280611201246242590927426^2) =
:= '''2.52982212813470346559'''59044271886.
:21 teisingas skaitmenuo po 4 iteracijų. Tai didesnis tikslumas nei Double precision (64 bits). Maždaug toks tikslumas kaip Double Extended precision (80 bits).
:Darant dalybą ar šaknies traukimą gali prireikti panaikinti eksponentę (padaryti ją lygia nuliui ar vienetui). Tai galima atlikti labai paprastai. Reikia padaryti logišką '''AND''' operaciją skaičiaus
:'''1000,0000,0111,1111,1111,1111,1111,1111''' = '''10000000011111111111111111111111''' su norimu skaičiumi, kaip pvz. '''0011_1110_0010_0000_0000_0000_0000_0000''', kuris yra <math>x=0.15625.</math>
:Po '''AND operacjos''' bus gautas skaičius:
:'''0011_1110_0010_0000_0000_0000_0000_0000''' '''AND'''
:'''1000_0000_0111_1111_1111_1111_1111_1111''' =
:'''0000_0000_0010_0000_0000_0000_0000_0000'''.
:Gauta mantisa 01 = 0.5*0 + 0.25*1 = 0.25. Pridėjus nematomą vienetą priekyje galutinis teisingas skaičius lieka:
:1 + 0.25 = 1.25.
:Eksponentė tampa lygi 0. 2^0 = 1.
:AND operacija veikia tokiu budu:
:1 AND 1 = 1,
:1 AND 0 = 0,
:0 AND 1 = 0,
:0 AND 0 = 0.
:Analogišku budu galima paversti mantisos bitus nuliais ir palikti tik eksponentę, prie kurios paskui galima pridėti arba atimti 8 bitus esančius viena bito pozicija į dešinę nuo kairės. Analogiškai galima panaikinti minuso ženklą su AND operacija. Arba suteikti minuso ženklą pridėjus 32 bitų skaičių '''1000_0000_0000_0000_0000_0000_0000_0000''' prie kokio nors teigiamo 32 bitų skaičiaus (kai pirmas bitas yra 0).
==Kaip FPU galėtų apseit be barrel shifters sudedant skaičius su skirtingom eksponentėm==
:https://en.wikipedia.org/wiki/Barrel_shifter
:Barrel Shifter gali stumti pavyzdžiui 32 bito skaičiaus bitus į kairę ar į dešinę ne per vieną bito poziciją, bet per kelias kaip, kad per dvi bito pozicijas ar per 4 bito pozicijas ar per 8 bito pozicijas.
:Sudedant 32 bitų single precision skaičius su skirtingomis eksponentėmis, mažesnio skaičiaus mantisos bitai turi būti pastumti į dešinę per tiek pozicijų koks yra tu dviejų skaičių eksponenčių skirtumas, atėmus eksponentę mažesnio skaičiaus iš eksponentės didesnio skaičiaus.
:Be barrel shifter dviejų 32 bitų floating point skaičių sudėtį su skirtingomis eksponentėmis galima atlikti pakeitus mažesnio skaičiaus eksponentę į denormal ir padauginus tą mažesnį skaičių iš skaičiaus, kurio fractional part yra vienetas (visa mantisa lygi nuliui, jeigu be nematomo vieneto) o eksponentė yra neigiamas skaičius gautas atėmus didesnio 32 bitų float skaičiaus eksponentę iš mažesnio 32 bitų float skaičiaus eksponentės. Tada šio skaičiaus eksponentė bus neigiama ir ją padauginus iš paversto į denormal mažesniojo skaičiaus, bus gautas rezultatas, kad mažesnio skaičiaus mantisa bus pastumta į dešinę tiek pozicijų koks yra tos neigiamos eksponentės skaičiaus modulis.
:Bet turbūt vis tiek reikės šiftinti mantisos bitus į dešinę, kad paversti mažesnio 32 bitų skaičiaus mantisą kaip pas denormal skaičius pastumta atitinkama bitų skaičių į dešinę, kad paskui pridėti gautą mantisą prie didesnio skaičiaus mantios, ir eksponentę po sudeties palikti didesnio skaičiaus, jeigu ji nepakils vienetu dėl sudeties.
:Tai tiesiog čia kaip veikia skaičių sudėtis su skirtingom eksponentėm. Buvau pagalvojęs, kad galima apseiti be barrel shifter, bet pasirodo, kad ne. Arba reikia šiftinti po vieną bitą (jeigu nėra barrel shifter circuit), kas gali pareikalauti nemažai laiko, jeigu sudedamų skaičių eksponentės stipriai skiriasi.
:Todėl iš esmės sudėtis gali būti ilgesnė nei daugyba net su barrel shifter, nes daugyba su padaryta schema galinčia padauginti mantisas iš vieno ciklo, bus daug greitesnė. Ten iš esmės ta daugybos schema turėtų ir užimti didžiausią dalį FPU. Senesni FPU daugino iš daug ciklų, nes negalėjo sau leisti daug transistorių daugybos transistorių schemai. Maždaug Pentium procesoriai pradėjo dauginti iš vieno ciklo, jei neskaičiuoti visokių įkrovimo, užkrovimo, išėmimo operacijų dviejų dauginamų skaičių.
:'''Update 1.''' Pagal kitą informaciją Pentium (1993 metų) procesorius daugino 3 kartus greičiau nei 387 FPU (apie 1986 metų), nes daugino ir shiftino ne po vieną bitą, o po tris bitus. Buvo sukurtos schemos dauginti iš 0, 1, 2, 3, 4, 5, 6, 7. Nes 3 bitai gali turėti 8 kombinacijas. Tai vietoje 64 ar 66 sudėčių (2 bitai apvalinimui su 80 bitų Extended precision) reikėjo sudėti tik 22 kartus. Ir šiftinimas buvo po 3 bitus į dešinę Su Pentium procesorium.
:Intel procesoriai Nahalem pradėjo dauginti iš vieno ciklo su gerokai sudetingesne dauginimo schema ir su daug daugiau tam reikalingų transistorių. https://en.wikipedia.org/wiki/Nehalem_(microarchitecture)
:Nahalem architektura yra 2008 metų. Variantai:
:Core i5-7xx
:Core i7-8xx
:Core i7-9xx Extreme.
:Tai patys pirmi Core procesoriai. Po jų sekė Core 2 procesoriai (2009 metų):
:Core 2 Solo
:Core 2 Duo
:Core 2 Quad
:Core 2 Extreme.
:Dauginimas kart 0 yra dauginimo praleidimas, jei kalbėti apie 64 bit * 64 bit sveikųjų skaičių daugybą.
:Dauginimas kart 2 yra pastumimas skaičiaus viena bito pozicija į kairę.
:Dauginimas kart 3 reikalauja specialios schemos. Arba pastumimas viena bito pozcija į kairę + jis pats, t. y. 2+1.
:Dauginimas kart 4 yra pastumimas skaičiaus dviem bito pozicijom į kairę.
:Dauginimas kart 5 yra dauginimas iš 4 + pats skačius.
:Dauginimas iš 6 yra dauginimas iš 8 + pats skaičius pastumtas į kairę viena bito pozicija ir paverstas į Two's compliment neigiamą skaičių (Padarius XOR su vienetų seka ir pridėjus vienetą). Kitaip tariant dauginimas iš 6 yra 8-2. Arba galima 4+2, tada pastumiamas dviem pozicijom į kairę ir pridedamas jis pats pastumtas viena pozicija į kairę.
:Dauginimas iš 7 yra 8-1. Pastumiamas skaičius į kairę trim bitų pozicijom ir prie jo pridedamas pats tas skaičius paverstas į neigiamą skaičių two's compliment formate. Pastumimas galimas daiktas vyksta per automatišką schemą ir taip pat pridedamas jis pats paverstas per XOR operacija su vienetais ir +1 (per automatišką schemą, kitaip tariant viskas vyksta per vieną ciklą).
:Todėl maksimaliai daugyba su pentium Gali vykti 3 kartus greičiau nei su 387. Bet realus pagreitėjimas gali būti ir 2x. Tai priklauso kaip ten visa ta dauginimo vidinė schema padaryta (iš skaičių nuo 0 iki 7).
:Jei FPU neegzistuoja, o yra reklama ar panašiai, tai kad sudauginti du 64 bitų skaičius, kurių mantisa yra 52 bitai, eksponentė 11 bitų, ir minuso ženklas 1 bitas, tai pagal antro skaičiaus pirmą (kairiausią) mantisos bitą pirmas skaičius arba paliekamas (jei tas bitas 1) arba vietoj jo rašomas 0 (ignoruojamas), jei tas bitas 0.
:Toliau, pagal antrą nuo kairės bitą antro skaičiaus, pirmas skaičius arba paliekamas, jei tas bitas 1 arba ignoruojamas, jei tas bitas 0. Paskui jei tas bitas buvo 1, tai pirmas skaičius pastumiamas viena bito pozicija į dešinę su shift right instrukcija ir pridedamas prie to nepastumto pirmo skaičiaus.
:Iš pradžiu pridedami 32 dešiniausi bitai nepastumto ir pastumto pirmo skaičiaus. Jeigu buvo carry iš jų sudeties. Tai sudedant kairiasias 32 bitų tas dvi dalis dar pridedamas carry, naudojant instrukciją '''ADD with Carry'''.
:Dabar yra 64 bitų skaičius saugomas dviejuose 32 bitų RAM adresuose.
:Toliau pagal antro dauginamo skaičiaus trečia bitą, pirmas skaičius pastumiamas trim pozicijom į dešinę ir pridedamas prie gauto 64 bito skaičiaus, jei tas antro dauginamo skaičiaus trečias bitas 1 ir nepridedamas, jei tas trečias bitas 0.
:Kiekvieną kartą nereikia šiftinti iki 52 kartų į dešinę. Užtenka išsisaugot kas kart pašiftintą viena poziciją pirmą 64 bitų skaičių (šiftinama du kartus, vieną kart kairiausi 32 bitai ir antrą kart dešiniausi 32 bitai).
:Tai iš viso reikia 52 šiftinimų *2 (nes šiftinama į dešine po 32 bitus) ir 52 sudėčių *2 (nes sudedama po 32 bitus). Tai iš viso reikia 52*2 + 52*2 = 104+104 = 208 operacijos. Bet kartais sudėties operacijos gali būti praleidžiamos, tai vidutiniškai reikia ne 104 sudeties opeacijų (32 bitų), bet 52, nes vidutiniškai pusė bitų antro skaičiaus nuliai ir pusė vienetai. Tai tada vidutiniškai reikia 104+52 = 156 operacijų, kad sudauginti du 64 bitų double precision skaičius.
:Tada realus procesoriaus dažnis vietoje 5 GHz yra apie 156*5 = 780 GHz.
:Single precision (32 bitų) su 23 bitų mantisą reikėtų 46 šift ir 46 add operacijų. Iš viso 92 operacijų maksimum. Bet jeigu vidutiniškai yra 23 sudeties operacijos, tai tada apie 23+46 = 69 opracijų. Free Pascal koduose galima sakyti 4 GHz procesorius daugina 64 bitų dauble precision skaičius naudodamas 4 ciklus ir tai taip pat įskaitant visokius įkrovimus iškraovimus į/iš FPU į/iš RAM atminties. Tai dėl to 780 GHz dažnis nustatytas gana tiksliai ir nereikia jo dauginti iš 4.
:Atradau tokią gudrybę:
:12345 * 12345 = 152399025
:ir
:1.2345 * 1.2345 = 1.52399025.
:Arba
:12345 * 14789 = 182570205,
:1.2345 * 1.4789 = 1.82570205.
:Tai iš esmės galima dauginti single precision mantisas kaip sveikuosius skaičius, o paskui tik padėti kablelį kur reikia (gauto rezultatą įterpti į float mantisą be priekinio vieneto).
:Tikrinimas:
:123456789123456789 * 123456789123456789 = 1.5241578780673678515622620750191e+34
:1.23456789 * 1.23456789 = '''1.52415787'''50190521.
:1.23456789123456789 * 1.23456789123456789 = 1.5241578780673678515622620750191.
:Visgi tikslumas nukentėjo - gudrybė neveikia.
:Antras tikrinimas:
:1.23456789123456 * 1.23456789123456 = 1.52415787806734,83700809383936.
:1.2345678 * 1.2345678 = '''1.524157'''6,5279684.
:Neveikia gudrybė. Dauginant "trumpesnius" skaičius ir tikslumas mažesnis.
:Bet vis tiek nušiftinus minėtą pirmą 64 bitų skaičių jau 32 bitų pozicijom į dešinę, to pirmo skaičiaus 32 dešiniausius bitus jau galima nešiftini ir neatlikinėti su jais sudeties operacijų. Taip dar sutaupant trečdalį laiko (nes pusė laiko dirbama su 64 bitais, o paskui likusią pusę laiko šiftinami ir sudedami tik 32 bitai prie dešinios 32 bitų dalies pirmojo skaičiaus). Todėl sutaupoma 1/3 operacijų. Iš to gaunasi, kad iš viso reikia ne 156 ciklų (dviejų double precision skaičių daugybai), o
:156 - 156/3 = 156 - 52 = 104 ciklų. Vadinasi tada procesoriaus 5GHz dažnis būtų ne 780 GHz, o 5*104 = 520 GHz.
:Tai yra, iš pradžiu loop šiftina ir sudeda 32 kartus kaip su 64 bitais, o paskui pereinama prie antros loop, kur šiftinami ir sudedami 32 bitai.
:Arba, kad nedaryti loop kas irgi naudoja laiką, nes dviem instrukcijom daugiau (decrement register and jump to loop begining if loop register flag not zero). Tai geriau surašyti visas instrukcijas iš eilės kiek kartų viską daryt. Tai užims daugiau RAM atminties, bet veiks greičiau, nes nereikės palaikyti loop vykdymo. Tai užims salyginai mažai RAM - iki vieno kilobaito. Bet gali veikti 1.5 karto ar net 2 kartus greičiau. Ir dar neužims vieno registro kaip counterio skaičiavimui kiek iteracijų atlikta. O jeigu procesorius dar ir negali skaičiuoti per tam skirtą registrą kaip to negali intel 8080, tai reikės dar perkraudinėti į akumuliatorių counter'inimo skaičių, kad pažiurėti ar tas skaičius ne nulis ir neįjungs Zero flag (kai bus nulis). Nors šiaip intel 8080 turi CMP instrukciją (CMP, Compare register with A). Tai tas pats kas atimti kitą registrą ar operand'ą iš atminties iš registro A (akumuliatoriaus), tik rezultatas neįdedamas į akumuliatorių. Tai iš esmės kol count koks nors registras po decrement instrukcijos nėra nulis, tol compare su registru A neįjungs Zero flag. Bet tada išeina į registrą A kiekvieną kartą reikia krauti sveiką skaičių 0 po kiekvienos šiftinimo ir sudeties operacijos.
:Ką tik pažiūrėjau [https://bitsavers.trailing-edge.com/components/intel/MCS80/98-153B_Intel_8080_Microcomputer_Systems_Users_Manual_197509.pdf intel 8080 manual] (51 psl.), tai visgi Zero flag yra '''set''', kai pasirinktas registras po ''decrement register'' instrukcijos tapo lygus nuliui. Tai praktiškai nereikia naujiems procesoriams jokių geresnių instrukcijų išeina. Tik FPU dauginimo ir sudeties bei atimties greitų instrukcijų užtenka. Nes iš mano samprotavimų, viską darant per mikrokodą, viskas veiks praktiškai tokiu pačiu greičiu kaip viską darant per RAM, tik truputi mažiau RAM ilgos instrukcijos užims kaip integer multiply pas intel 8088 per mikrokodą. Vis tiek daugiausiai vietos RAM užima paveiksliukai, teksturos ir kita data, o ne pačios instrukcijos, tai šiuolaikiniams laikams tai neaktualu. O RAM parinkus vieną ROW, ateina iš tos parinktos ROW bitai iš visų Column į kelis tūkstančius SRAM bitų registrų. Paskui iš tų SRAM (static RAM) registrų, kurie dažniausiai yra 4 bitų viename RAM čipe, atgal gražinami nutekėję tos DRAM row visos stulpelių (column) bitų reikšmės atgal į tas pačias vietas ir Row atjungiama, o tie nutekėję bitai į SRAM lasteles (registrus) niekur nedingsta iki tol kol nekreipiamasi į kitą Row. Todėl paskui procesorius ima informaciją ne iš DRAM lastelių, o iš SRAM lastelių. Ir įrašo į SRAM ląsteles, o parinkus naują ROW, tą informacija iš kelių tukstančių SRAM lastelių (registrų) per kelis ciklus grąžinama į visas column tos Row. Instrukcijos vykdomos dažniausiai nuosekliai ir dažniausiai peršokama ribose tos pačios DRAM ROW. Todėl vis naudojami tie patys SRAM registrai nutekintų bitų DRAM stulpelių iš tos pačios ROW. Dėl to DRAM praktiškai gali veikti greičiau nei bet koks cache ir naudoti daug mažiau energijos už cache, kuriam reikia milijonų SRAM lastelių, o ne tukstančių. Tik didesnis atstumas tarp CPU ir DRAM sulėtiną informacijos keliavimą iš DRAM esančių SRAM lastelių dėl šviesos greičio limito iš dalies. Tai, kad cache atmintis gaunasi visiškai beprasmiška, jei tinkamai panaudoti tą DRAM veikimą su SRAM registrais viduje. Kas teoriškai turėtų taip ir būti.
:Jeigu 5 GHz procesorius iš tikro veikia 500 GHz dažniu, o šviesos greitis nėra sumeluotas, tai 1 metrą šviesą gali nueiti 300 milijonų kartų arba 300 MHz. Atstumas tarp procesoriaus ir RAM yra apie 10 cm, tai tada RAM dažnis max gali buti apie 3 GHz. Bet jeigu procesorius veikia 500 GHz, nes jos pagrindinė dalis yra labai sumažinta dėl naujesnės litografijos, o pats CPU base core užima ne daugiau nei milijoną tranzistorių, tai išeitų, kad nespėtų imti iš RAM atminties informaciją CPU dėl šviesos greičio abribojimu (dėl atstumo tarp CPU ir RAM). Tai šita dalyką galima būtų išspręsti, jei visos programos ir programavimo kalbos naudotų 64 bitų ar 32 bitų skaičių daugybai tam skirtą RAM adresą vieno kilobaito ar mažiau, kuris visada būtų adresuojamas ne per DRAM, bet per SRAM esančią pačiame mikroprocesoriuje. Pavyzdžiui tos SRAM kilobaitas galėtų būti pačioje RAM pradžioje, tam skirus 256 adresus saugančius po 32 bitus. Tai tiesiog pirmi 8 (ar 10, nes 2 bitai reiškia 4 baitus..., o procesorius ne 8, o 32 bitų...) kairiausių bitų būtų nukreipti į tą SRAM atminti, o like 24 (ar 22) bitai einantys į SRAM atmintį būtų nuliai. Jei nors vienas iš dešiniausių 24 (ar 22 bitų) būtų ne nulis, tai Adresas iš CPU nukeliautų ne į vidinę SRAM atmintį, o į išorinę lėtesnę DRAM atmintį. Tai tiesiog prieš pradedant daugybą, visos programos galėtų naudoti tą SRAM atmintį pernešant ten du dauginamuosius į atitinkamas vietas, kuriuos jau supa daugybos kodas... Ir su 500 GHz mažyčiu procesorium (be visokių nereikalingų kvailų instrukcijų) galėtų užtekti šviesos greičio būtent tokiam dideliam dažniui, kad atlikti double ar single precision daugybą. Intel iš tikro sudetingos instrukcijos asembleryje galėtų būti kaip aukštesnio lygio programavimo kalba, kuri paskui visas tas instrukcijas paverčia į 8080 CPU tipo instrukcijas, o FPU daugybą perkelia į tam skirtą SRAM atminties vietą, o rezultatą gražina lyg niekur nieko, ten kur ištransliavo asembleris... O vėliau ir aukštesnio lygio programavimo kalbos kaip C ir Pascal.
:Ne. Visgi skaičiavimo trukmė sumažėja ne trečdaliu, o ketvirtadaliu. Tuomet 5 GHz procesoriaus dažnis būtų ne 520 GHz, o
:(156 - 156/4) * 5 (GHz) = (156 - 39) * 5 = 585 GHz.
:'''Update 2.''' Dar yra toks variantas, kad 5 GHz procesorius dirba apie 50 GHz dažniu ir neturi FPU. Iš esmės rodos kodus kaip daugybos ir sudeties benchmarkai su viena daugyba ir/ar sudėtimi buvo skaičiuojami per apie 25-30 sekundžių pirmą kartą su 1 milijardu iteracijų. Jei viena operacija užima 4 ciklus ant 4 GHz procesoriaus tai viena daugyba ir sudėtis užimtu 8 ciklus. O 25-30 sekundžių yra 100-120 ciklų. Tai 120/8 = 15 ciklų daugybai ir sudečiai, jei tik daugybai tai 30 ciklų. Bet buvau ir be daugybos tik, kur yra sudetis ir vis tiek pat kaip su daugyba. Be to ten maži skaičiai iš pradžiu ėjo, tai juos galėjo greičiau daugint. Tik kur nebuvo daugybos, kodėl irgi 25-30 sekundžių darė sudeties operacijas milijardą kartų? Tai gal vistiek ten 20-25 sekundes pirmą kartą kompiliavimas užima ir FPU iš tikro yra. Arba sudetis yra ilga nes sudedami skaičiai su skirtingom eksponentėm ir reikalingas šiftinimas per kelis ar keliasdešimt bitų. Tai visgi gali būti, kad Tikrasis CPU dažnis yra ne 4 ar 5 GHz, o apie 40-50 GHz ar net 100 GHz. Jeigu FPU neegzistuoja. Tada kvadratinę šaknį ir dalyba tikrai ne greičiau bus su Niutono iteracijomis daryti, o taikyti kitus metodus. Dalyba tai ir taip bus beveik toks pat greitis kaip daugyba (jei FPU nėra). O kvadratinei šakniai kažką reik sugalvot.
:Vienintelis įrodymas, kur sinuso Teiloro eilutė pirmą kartą labai greit skaičiuojama. Sudedami sinusai su įvairiom reikšmėm. Problema ta, kad negalima patikrinti teisingumo tų visų ilgo kodo skaičiavimu, gal kažką nukerta ten... Kiti kodai kaip parabolės ilgis ar integralų sumos duoda patikrinamą per integralus rezultatą, bet jie ir skaičiuojami pirmą kartą ilgiau nei antrą kartą.
:Tai geriau kur nereikia taikyti trumpesnes daugybas su tuo 50 GHz procesorium, ką tuomet išeitų žaidimų kurėjai ir game engines kūrėjai ir daro.
:Intel387TM SX MATH COPROCESSOR
:https://datasheets.chipdb.org/Intel/x86/387/datashts/24022509.PDF
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240225-009.pdf
:Intel 387 DX MATH COPROCESSOR
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005.pdf
:https://ardent-tool.com/CPU/docs/Intel/387/datasheets/240448-005_alt.pdf
:https://ardent-tool.com/CPU/Docs_Intel.html#387
:Intel 386
:www.digchip.com/datasheets/download_datasheet.php?id=59667&part-number=386DX
:https://ardent-tool.com/CPU/docs/Intel/386/datasheets/231630-011.pdf
:https://www.dosdays.co.uk/media/intel/1986_80386_Hardware_Reference_Manual.pdf
:https://www.bitsavers.org/components/intel/80386/230985-001_80386_Programmers_Reference_Manual_1986.pdf
:https://pdos.csail.mit.edu/6.828/2018/readings/i386.pdf
:https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf
===Intel 386 procesoriaus suderinamumas su 286 ir 8086===
:Skaitant čia https://ardent-tool.com/CPU/docs/Intel/386/manuals/231746-001.pdf 129 puslapyje ir ankstesnius kitus puslapius, galima suprasti, kad intel 386 CPU iš tikro niekaip neoptimizuoja apatinės ir viršutinės dalies (16 bitų) RAM adreso saugančio 32 bitus. Pasakyta, kad jeigu dedama apatinė 16 bitų dalis, tai aktyvuojamos kojelės BE0 ir BE1, atsakančios už perdavimą apatinės 16 bitų dalies į 32 bitų RAM vieną adresą. Jeigu aktyvuojamos kojelės BE2 ir BE3, tai viršutiniai 16 bitų dedami ir į apatinę ir į viršutinę dalį 32 bitų RAM vieno adreso. Amerikiečiams labai svarbu būti konkurencingams.
:Tai jeigu pernešti visą intel 286 procesoriaus kodą į 386 procesorių, tai jeigu naudoti tik 32 bitų registrus (EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP) ir 286 procesoriaus 16 bitų kodą įrašyti tik į apatinius 16 bitų visuose adresuose, tai tos visos 16 bitų programos neveiks, nes viršutiniai 16 bitų bus nuliai kai bus pridedama neigiama displacement '''two's complement''' formate. O jeigu visi viršutinai 16 bitų vieno 32 bitų RAM adreso bus vienetai, kad veiktų neigiama displacement, tai tada neveiks teigiama displacement, nes pas teigiama displacement reikia, kad visi viršutiniai 16 bitų būtų nuliai.
:Dar gali būti, kad intel 386 ir intel 286 procesoriai naudoja visiškai skirtingus opcode'us. Su '''REP'''eat prefixu kai kurių instrukcijų 386 procesoriuje nėra, kurios yra 8086 procesore. Vietoje to, 386 naudoja REPeat instrukcijas kaip atskirą opcode'ą nuo ne Repeat instrukcijų. Kitų opcode'ų nelyginau, norintys gali palyginti ir patikrinti kas kaip suderinta. Bet manau ten visi opkodai skirtingi pas 386 nuo 286 ir 8086.
:Gali būti, kad kažkaip dedant į 386 procesoriaus 16 bitų registrus (AX, BX, CX, DX, DI, SI, BP, SP) ir panaudojant kojelę BS16 ant 386 CPU, visi 16 bitų kodai iš 286/8086 veiks su 386 procesorium. Tada 386 procesorius visada skaitys tik iš minėtų 16 bitų registrų (AX, BX, CX, DX, DI, SI, BP, SP). Bet tada tai reiškia, kad kai aktyvuota BS16 kojelę, 386 pradeda suprasti 286 procesoriaus opcode'us iš žemutinių 16 bitų RAM adreso 32 bitų. Į viršutinius 16 bitų to paties RAM adreso tada išeina niekas nededama. Aš tai sakyčiau, kad intel inžinieriai nesivargino palaikyti 286 procesoriaus opkodų, o teoretikams kuriantiems instrukcijas ir datasheets ir user's manual'us pasakė, kad viskas veiks maksimaliai optimaliai ir optimizuotai. Todėl greičiausiai reikia perrašinėti 286 visas programas 386 procesoriams arba kai buvo kuriamos programos, buvo kuriamos iškart ir 386 ir 286 procesoriui, o senesnės programos 8086 tuomet arba neveik su 386 CPU arba buvo per kokį nors asemblerį konvertuojamos į 386 procesoriaus kodą paleidus, kas turėdavo atimti laiko paleidimui. Kad intel 8080 procesoriaus opkodai neveikia su 8088/8086 pasakyta wikipedijoje. Todėl nėra prasmės iš 8080 registrų, bet gal jie duoda pasirnkti baitus (A, B, C, D, E, H, L registrai). Arba ten tik girti suderinamumą su 8080 ir 286/8088, bet iš tikro tuomet yra tik 32 bitų 8 registrai EAX, EBX, ECX, EDX, EDI, ESI, EBP, ESP. Be to 8086 galėjo būti neveikiantis procesorius, dėl to IBM pasirinko 8088. 8086 deda į RAM adresą 16 bitų, o 8088 į vieną RAM adresą deda tik 8 bitus. O jeigu skaičius yra 16 bitų, tada deda į du RAM adresus kaip intel 8080 CPU.
:Nors šiaip yra opcodai dėti į 16 bitų registrus (AX, BX, CX, DX, DI, SI, BP, SP), kurie yra ir pas 286/8086/8088 procesorių. Reikia pasirinkti laukelius opkoduose pakeitus kai kuriuos bitus vietoj 0 į 1 ar atvirkščiai. Bet tai nereiškia, kad tokie visi 386 opkodai bus lygiai tokie patys kaip 286 opkodai tuose žemutiniuose 16 bit RAM adreso, kai kojelė BS16 aktyvuota. Tai tiesiog tada arba reikia perrašyti 286 programos kodus 386'am procesoriui arba tie seni 286'o registrai priduoda tik funkcionalumo kai kada bet nei kiek neįtakoja suderinamumą tarp 386 ir 286 ar 8088/8086.
[https://lt.wikibooks.org/wiki/Wikibooks:Knygos/High_Level_Shading_Language High Level Shading Language]
a93v5fvi17sg8q1ax87vh8zfh4a18hm