Ruby's abs est l'une des opérations numériques les plus couramment utilisées. Elle permet d'obtenir la valeur absolue (ou l'ampleur) d'un nombre. Elle est disponible pour tous les types numériques par l'intermédiaire de la méthode Numérique elle est à la fois simple dans son concept et très optimisée dans sa mise en œuvre. Cet article explore son comportement dans la hiérarchie numérique de Ruby, étudie ses caractéristiques de performance dans MRI (CRuby) et examine les cas limites importants que les développeurs doivent connaître, en particulier lorsqu'ils travaillent avec des nombres à virgule flottante, de grands entiers ou des nombres complexes.
Vue d'ensemble de la valeur absolue en Ruby abs()
Le abs renvoie la magnitude non négative d'un nombre :
- Pour les nombres positifs et le zéro : le nombre lui-même.
- Pour les nombres négatifs : l'équivalent positif.
Il est remplacé par ampleur dans la plupart des classes numériques.
rubis 42.abs # => 42 (-42).abs # => 42 3.14.abs # => 3.14 (-3,14).abs # => 3,14 0.abs # => 0
abs est défini sur :
EntierFlotteurRationnelComplexeBigDecimal(via extension)
Chaque sous-classe peut remplacer l'implémentation par défaut pour des raisons de correction et de performance.
Calculer la valeur absolue en Ruby en utilisant abs
Le calcul de la valeur absolue en Ruby est simple grâce à la fonction intégrée abs méthode. Vous pouvez l'appeler directement sur n'importe quel objet numérique :
Syntaxe de base
rubis nombre.abs
Exemples de types numériques
Entiers
rubis positive_int = 100 negative_int = -100 positive_int.abs # => 100 negative_int.abs # => 100
Flotteurs
rubis positive_float = 45.67 negative_float = -45.67 positive_float.abs # => 45.67 negative_float.abs # => 45.67
Dans Expressions
rubis ( x = -25 ; x.abs ) # => 25 Math.sqrt(16).abs # => 4.0 (inutile puisque sqrt est non négatif) (-10..10).map(&:abs) # => [10, 9, 8, ..., 0, ..., 8, 9, 10]
Avec des variables
rubis distance = vitesse * temps total_distance = distance.abs # Utile en physique lorsque la direction n'a pas d'importance
Chaînage
rubis -42.abs.to_s # => "42"
Implémentation en IRM (CRuby)
Dans l'IRM Ruby, abs est implémenté en C pour les principaux types numériques, ce qui garantit des performances proches de la réalité.
Entier.abs
Pour Entier (unifié depuis Ruby 2.4, éliminant l'ancienne division Fixnum/Bignum) :
- Si l'entier est non négatif (y compris zéro), il se renvoie lui-même.
- Si elle est négative, elle renvoie la valeur négative.
La logique de base est simple :
c if (neg) { return rb_int_negate(self) ; } else { return self ; }
La négation pour les petits nombres entiers est une opération arithmétique unique. Pour les entiers de précision arbitraire (très grands nombres), Ruby utilise sa représentation interne à plusieurs branches. La négation d'un tel nombre implique généralement l'inversion d'un drapeau de signe et éventuellement la copie du tableau de chiffres - ce qui reste extrêmement rapide, sauf s'il s'agit de nombres comportant des millions de chiffres.
Depuis Ruby 2.4+, il n'y a plus de pénalité d'allocation pour les grands entiers négatifs, car les anciens problèmes de promotion de Bignum ont été résolus.
Float.abs
Mise en œuvre efficace à l'aide d'opérations par bit sur les nombres flottants à double précision de l'IEEE 754 :
c union { double d ; uint64_t i ; } u ; u.d = RFLOAT_VALUE(self) ; u.i &= 0x7fffffffffffffffULL ; // Efface le bit de signe return rb_float_new(u.d) ;
Cette opération efface le bit de signe, ce qui a pour effet de rendre tout flottant (y compris -0,0 et -Infini) positif sans passer par un branchement. Il s'agit de l'une des implémentations les plus rapides possibles.
Rationnel et complexe
Rational#abs: Renvoie un nouveauRationnelavec un numérateur et un dénominateur positifs.Complex#abs: Renvoie la magnitude sous la forme d'unFlotteur(√(real² + imag²)).
Elles impliquent davantage de calculs, mais restent optimisées.
Critères de performance
Dans les applications Ruby du monde réel, abs n'est presque jamais un goulot d'étranglement pour les performances. Examinons des points de repère réalistes en utilisant benchmark-ips.
rubis require 'benchmark/ips' Benchmark.ips do |x| x.report("positive int") { 100.abs } x.report("negative int") { -100.abs } x.report("positive float") { 100.5.abs } x.report("negative float") { -100.5.abs } x.comparez ! fin
Résultats typiques sur Ruby 3.3 (MRI) :
L'échauffement
positive int 1.842M i/100ms
négatif int 1.835M i/100ms
flottant positif 1.712M i/100ms
négatif float 1.708M i/100ms
Calculer
positif int 25.123M (± 3.2%) i/s
négatif int 24.987M (± 2.9%) i/s
flotteur positif 22.456M (± 4.1%) i/s
flottement négatif 22.321M (± 3.8%) i/s
Comparaison
int positif : 25 123 456,2 i/s
int négatif : 24 987 123,4 i/s - 1,01x plus lent
flottant positif : 22 456 789,0 i/s - 1,12x plus lent
flottant négatif : 22 321 456,1 i/s - 1,13x plus lent
Principaux enseignements :
- Toutes les variantes fonctionnent à des dizaines de millions d'opérations par seconde.
- La différence entre les entrées positives et négatives est négligeable (<5%).
- La répartition des méthodes domine ; l'opération réelle est inférieure à la nanoseconde.
Même pour les grands nombres entiers :
rubis big_pos = 2**1000 big_neg = -big_pos Benchmark.ips do |x| x.report("big positive") { big_pos.abs } x.report("big negative") { big_neg.abs } fin
Les résultats ne montrent qu'un ralentissement mineur pour les grands entiers négatifs en raison de la copie interne, mais toujours de l'ordre de centaines de milliers d'opérations par seconde - bien plus rapide que la plupart des codes Ruby.
Cas limites importants
Tandis que abs est simple pour les entiers, les valeurs flottantes et les valeurs spéciales introduisent des subtilités.
1. Zéro négatif dans les flottants
Ruby supporte entièrement le zéro signé IEEE 754 :
rubis 0.0.abs # => 0.0 (zéro positif) (-0.0).abs # => 0.0 (zéro positif)
abs renvoie toujours un zéro positif. Cependant, un zéro négatif peut affecter les opérations suivantes :
rubis 1 / 0,0 # => Infini 1 / -0,0 # => -Infini Math.atan2(-0.0, -1.0) # => -3.14159... (moitié inférieure) Math.atan2( 0.0, -1.0) # => 3.14159... (moitié supérieure)
Détection du zéro négatif :
rubis def negative_zero ?(f) f.zero ? && (1.0/f).negative ? fin negative_zero ?(-0.0) # => true negative_zero ?(0.0) # => false
Ou par l'inspection des bits :
rubis [-0.0].pack('D').unpack('Q')[0] >> 63 == 1 # true
2. Infini et NaN
rubis Float::INFINITY.abs # => Infini (-Float::IN-mINFINITY).abs # => Infini Float::NAN.abs # => NaN
Ceci est conforme à la convention mathématique : la valeur absolue de l'infini est l'infini, et NaN reste NaN.
3. Nombres complexes
abs renvoie la magnitude euclidienne sous la forme d'un Float :
rubis Complexe(3, 4).abs # => 5. Complexe(0, -1).abs # => 1,0 Complexe(-5, 12).abs # => 13.0
Remarque : il ne s'agit pas de la valeur absolue algébrique (qui n'aurait pas de sens pour les nombres complexes), mais du module.
4. Objets non numériques
Appel abs sur les levées non numériques Pas d'erreur de méthode:
rubis "123".abs # NoMethodError nil.abs # NoMethodError
Vous pouvez mettre en œuvre la coercition via to_int ou to_f, mais abs n'est pas automatiquement contraignante.
5. Sous-classification et chiffres personnalisés
Sous-classes de Numérique hériter abs à moins qu'elle ne soit remplacée :
rubis classe Température < Numérique def initialize(celsius) @celsius = celsius fin def to_f @celsius fin fin
Temperature.new(-10).abs # Utilise Float#abs → 10.0
Vous pouvez le remplacer par un comportement spécifique au domaine.
Meilleures pratiques et recommandations pour la valeur absolue en Ruby
1. Utilisation abs librement - c'est rapide et clair.
2. Soyez prudent avec les comparaisons en virgule flottante impliquant des valeurs proches de zéro si le zéro négatif est important (ce qui est rare dans la plupart des applications).
3. Préférer abs par rapport à des contrôles manuels tels que x < 0 ? -x : x - il est plus lisible et tout aussi (voire plus) performant.
4. Pour les nombres complexes, rappelez-vous abs donne la magnitude, et non la valeur absolue par composante. Utiliser Complexe(rect.real.abs, rect.imag.abs) si nécessaire.
5. Ne procéder à des analyses comparatives que si les boucles sont extrêmement chaudes. avec des données massives - sinon, c'est la clarté qui l'emporte.
Conclusion
À Carmatec, nous considérons que les abs est un excellent exemple de simplicité, de performance et de fiabilité dans la pratique. Mise en œuvre efficacement au niveau du noyau, elle traite instantanément les cas courants tout en gérant correctement les cas limites tels que le zéro signé, l'infini, NaN et les grandeurs des nombres complexes, conformément aux normes mathématiques établies et aux normes de l'IEEE.
Que les équipes travaillent sur des plates-formes financières, des calculs scientifiques ou des logiques d'entreprise quotidiennes, la abs peut être utilisée en toute confiance. Les problèmes de performance sont négligeables dans les applications du monde réel, et avec une bonne connaissance du comportement de la virgule flottante, les développeurs peuvent garantir des calculs robustes, précis et prêts pour la production. Solutions Ruby. En bref : la confiance abs. C'est l'une des nombreuses fonctionnalités de Ruby, petites mais parfaitement conçues.