Ruby's abs menetelmä on yksi yleisimmin käytetyistä numeerisista operaatioista, joka antaa luvun absoluuttisen arvon (tai suuruuden). Käytettävissä kaikille numeerisille tyypeille Numeerinen luokka, se on sekä yksinkertainen konseptiltaan että erittäin optimoitu toteutukseltaan. Tässä artikkelissa tarkastellaan sen käyttäytymistä Rubyn numerohierarkiassa, syvennytään sen suorituskykyominaisuuksiin MRI:ssä (CRuby) ja tarkastellaan tärkeitä ääritapauksia, jotka kehittäjien on syytä ottaa huomioon - erityisesti silloin, kun he työskentelevät liukulukujen, suurten kokonaislukujen tai monimutkaisten lukujen kanssa.
Yleiskatsaus Rubyn absoluuttiseen arvoon abs()
The abs menetelmä palauttaa luvun ei-negatiivisen suuruuden:
- Positiivisille luvuille ja nollalle: itse luku.
- Negatiivisille luvuille: positiivinen vastine.
Se on alias suuruusluokka useimmissa numeerisissa luokissa.
rubiini 42.abs # => 42 (-42).abs # => 42 3.14.abs # => 3.14 (-3.14).abs # => 3.14 0.abs # => 0
abs määritellään:
KokonaislukuFloatRationaalinenMonimutkainenBigDecimal(laajennuksen kautta)
Kukin alaluokka voi ohittaa oletustoteutuksen oikeellisuuden ja suorituskyvyn vuoksi.
Absoluuttisen arvon laskeminen Rubyssä abs:n avulla
Absoluuttisen arvon laskeminen Rubyssa on suoraviivaista sisäänrakennetun abs menetelmä. Voit kutsua sitä suoraan mille tahansa numeeriselle objektille:
Perussyntaksi
rubiini number.abs
Esimerkkejä eri numeeristen tyyppien välillä
Kokonaisluvut
rubiini positiivinen_int = 100 negative_int = -100 positive_int.abs # => 100 negative_int.abs # => 100
Kellukkeet
rubiini positiivinen_float = 45,67 negative_float = -45.67 positive_float.abs # => 45.67 negative_float.abs # => 45.67
Ilmaisuissa
rubiini ( x = -25; x.abs ) # => 25 Math.sqrt(16).abs # => 4.0 (tosin tarpeetonta, koska sqrt on ei-negatiivinen). (-10..10).map(&:abs) # => [10, 9, 8, ..., 0, ..., 8, 9, 10]
Muuttujien kanssa
rubiini matka = nopeus * aika total_distance = distance.abs # Hyödyllinen fysiikassa, kun suunnalla ei ole väliä.
Ketjuttaminen
rubiini -42.abs.to_s # => "42"
Toteutus MRI:ssä (CRuby)
MRI Ruby, abs on toteutettu C-kielellä keskeisiä numeerisia tyyppejä varten, mikä takaa lähes alkuperäisen suorituskyvyn.
Integer.abs
varten Kokonaisluku (yhtenäistetty Ruby 2.4:stä lähtien, jolloin vanha Fixnum/Bignum-jako poistuu):
- Jos kokonaisluku on ei-negatiivinen (myös nolla), se palauttaa itsensä.
- Jos se on negatiivinen, se palauttaa negatiivisen arvon.
Ydinlogiikka on yksinkertainen:
c if (neg) { return rb_int_negate(self); } else { return self; }
Pienten kokonaislukujen negaatio on yksi aritmeettinen operaatio. Mielivaltaisen tarkkuuden kokonaisluvuille (hyvin suurille luvuille) Ruby käyttää sisäistä moniosaista esitystapaa. Tällaisen luvun negaatioon kuuluu tyypillisesti merkin kääntäminen ja mahdollisesti numeromäärän kopioiminen - tämä on silti erittäin nopeaa, ellei kyseessä ole luku, jossa on miljoonia numeroita.
Ruby 2.4+:sta lähtien suurille negatiivisille kokonaisluvuille ei ole enää allokointirangaistusta, koska vanhat Bignum-edistämisongelmat on ratkaistu.
Float.abs
Toteutettu tehokkaasti käyttämällä IEEE 754:n kaksoistarkkuuden liukuluvuilla suoritettavia bittikohtaisia operaatioita:
c union { double d; uint64_t i; } u; u.d = RFLOAT_VALUE(self); u.i &= 0x7fffffffffffffffffffffffULL; // Tyhjennä merkkibitti. return rb_float_new(u.d);
Tämä tyhjentää merkkibitin, jolloin mistä tahansa liukuluvusta (mukaan lukien -0.0 ja -Infinity) tulee positiivinen ilman haarautumista. Tämä on yksi nopeimmista mahdollisista toteutuksista.
Rationaalinen ja monimutkainen
Rational#abs: Palauttaa uudenRationaalinenjoiden osoittaja ja nimittäjä ovat positiiviset.Complex#abs: Palauttaa suuruuden muodossaFloat(√(real² + imag²)).
Näihin liittyy enemmän laskentaa, mutta ne ovat silti optimoituja.
Suorituskyvyn vertailuarvot
Todellisissa Ruby-sovelluksissa, abs ei ole lähes koskaan suorituskyvyn pullonkaula. Tarkastellaan realistisia vertailuarvoja käyttäen benchmark-ips.
rubiini vaativat 'benchmark/ips' Benchmark.ips do |x| x.report("positiivinen int") { 100.abs } x.report("negatiivinen int") { -100.abs } x.report("positiivinen kelluva") { 100.5.abs } x.report("negatiivinen float") { -100.5.abs } x.compare! end
Tyypillisiä tuloksia Ruby 3.3:lla (MRI):
Lämmittely
positiivinen int 1.842M i/100ms
negatiivinen int 1.835M i/100ms
positiivinen kelluva 1.712M i/100ms
negatiivinen kelluva 1.708M i/100ms
laskeminen
positiivinen int 25.123M (± 3.2%) i/s
negatiivinen int 24.987M (± 2.9%) i/s
positiivinen kelluva 22.456M (± 4.1%) i/s
negatiivinen kelluva 22.321M (± 3.8%) i/s
Vertailu
positiivinen int: 25 123 456,2 i/s
negatiivinen int: 24,987,123.4 i/s - 1.01x hitaampi.
positiivinen float: 22 456 789,0 i/s - 1,12x hitaampi.
negatiivinen float: 22 321 456,1 i/s - 1,13x hitaampi.
Keskeiset asiat:
- Kaikkien muunnelmien nopeus on kymmeniä miljoonia operaatioita sekunnissa.
- Positiivisten ja negatiivisten tulojen välinen ero on häviävän pieni (<5%).
- Menetelmien lähettäminen on hallitsevaa; varsinainen toiminta on alle nanosekunnin mittaista.
Jopa suurilla kokonaisluvuilla:
rubiini big_pos = 2**1000 big_neg = -big_pos Benchmark.ips do |x| x.report("iso positiivinen") { big_pos.abs } x.report("iso negatiivinen") { big_neg.abs } end
Tulokset osoittavat vain vähäistä hidastumista negatiivisten suurten kokonaislukujen kohdalla sisäisen kopioinnin takia, mutta silti satojen tuhansien operaatioiden luokkaa sekunnissa - paljon nopeammin kuin useimmat Ruby-koodit.
Tärkeitä ääritapauksia
Vaikka abs on suoraviivaista kokonaisluvuille, mutta liukuluku- ja erikoisarvot tuovat mukanaan hienouksia.
1. Negatiivinen nolla liukuluvuissa
Ruby tukee täysin IEEE 754:n mukaista nollaa:
rubiini 0.0.abs # => 0.0 (positiivinen nolla) (-0.0).abs # => 0.0 (positiivinen nolla)
abs palauttaa aina positiivisen nollan. Negatiivinen nolla voi kuitenkin vaikuttaa myöhempiin operaatioihin:
rubiini 1 / 0.0 # => Ääretön 1 / -0.0 # => - ääretön Math.atan2(-0.0, -1.0) # => -3.14159... (alaosa) Math.atan2( 0.0, -1.0) # => 3.14159... (yläpuoli)
Negatiivisen nollan havaitseminen:
rubiini def negative_zero?(f) f.zero? && (1.0/f).negative? end negative_zero?(-0.0) # => true negative_zero?(0.0) # => false
Tai bittitarkastuksen kautta:
rubiini [-0.0].pack('D').unpack('Q')[0] >> 63 == 1 # true
2. Ääretön ja NaN
rubiini Float::INFINITY.abs # => Ääretön (-Float::IN-mINFINITY).abs # => ääretön määrä Float::NAN.abs # => NaN
Tämä noudattaa matemaattista käytäntöä: äärettömän absoluuttinen arvo on ääretön, ja NaN pysyy NaN:nä.
3. Kompleksiluvut
abs palauttaa euklidisen suuruuden Float-arvona:
rubiini Complex(3, 4).abs # => 5. Complex(0, -1).abs # => 1.0 Complex(-5, 12).abs # => 13.0
Huomautus: Kyse ei ole algebrallisesta absoluuttisesta arvosta (joka ei olisi järkevää kompleksiluvuille), vaan moduulista.
4. Muut kuin numeeriset kohteet
Kutsu abs muiden kuin numeeristen aineiden osalta nostaa NoMethodError:
rubiini "123".abs # NoMethodError nil.abs # NoMethodError
Voit toteuttaa pakottamisen to_int tai to_f, mutta abs ei automaattisesti pakota.
5. Alaluokittelu ja mukautetut numerot
Alaluokat Numeerinen peri abs ellei sitä ohiteta:
rubiini class Lämpötila < Numeric def initialize(celsius) @celsius = celsius end def to_f @celsius end end
Lämpötila.new(-10).abs # Käyttää Float#abs → 10.0
Voit ohittaa sen toimialuekohtaista käyttäytymistä varten.
Parhaat käytännöt ja suositukset Rubyn absoluuttista arvoa varten
1. Käytä abs vapaasti - se on nopea ja selkeä.
2. Ole varovainen liukulukuvertailujen kanssa. arvot lähellä nollaa, jos negatiivisella nollalla on merkitystä (harvinaista useimmissa sovelluksissa).
3. Mieluummin abs manuaalisiin tarkastuksiin verrattuna, kuten x < 0 ? -x : x - se on helppolukuisempi ja yhtä suorituskykyinen (tai suorituskykyisempi).
4. Kompleksiluvuille, muista abs antaa suuruuden, ei komponenttiviisaasti absoluuttista arvoa. Käytä Complex(rect.real.abs, rect.imag.abs) tarvittaessa.
5. Vertailuanalyysi vain erittäin kuumissa silmukoissa massiivisella datalla - muuten selkeys voittaa.
Johtopäätös
klo Carmatec, katsomme Rubyn abs menetelmä on hyvä esimerkki yksinkertaisuudesta, suorituskyvystä ja luotettavuudesta käytännössä. Ydintasolla tehokkaasti toteutettu menetelmä käsittelee tavanomaiset tapaukset välittömästi ja hallitsee samalla oikein reunatapaukset, kuten nollan, äärettömyyden, NaN:n ja kompleksilukujen suuruudet, vakiintuneiden matemaattisten ja IEEE:n standardien mukaisesti.
Olivatpa tiimit sitten tekemisissä rahoitusalustojen, tieteellisten laskelmien tai jokapäiväisen liiketoimintalogiikan parissa, on tärkeää, että abs menetelmää voidaan käyttää täysin luotettavasti. Suorituskykyyn liittyvät ongelmat ovat vähäpätöisiä reaalimaailman sovelluksissa, ja kun kehittäjät ovat tietoisia liukulukujen käyttäytymisestä, he voivat varmistaa vankan, tarkan ja tuotantokelpoisen järjestelmän. Ruby-ratkaisut. Lyhyesti: luottamus abs. Se on yksi Rubyn monista pienistä mutta täydellisesti suunnitelluista ominaisuuksista.