2009-07-03 10 views
24

En ruby, je veux convertir un float en un entier s'il s'agit d'un nombre entier. Par exempleRuby round float to_int si nombre entier

a = 1.0 
b = 2.5 

a.to_int_if_whole # => 1 
b.to_int_if_whole # => 2.5 

Fondamentalement, j'essaye d'éviter d'afficher ".0" sur n'importe quel nombre qui n'a pas un nombre décimal. Je suis à la recherche d'un moyen élégant (ou intégré) de faire

def to_int_if_whole(float) 
    (float % 1 == 0) ? float.to_i : float 
end 

Répondre

5

C'est la solution qui a fini par travailler comme je le veux à:

class Float 
    alias_method(:original_to_s, :to_s) unless method_defined?(:original_to_s) 

    def is_whole? 
    self % 1 == 0 
    end 

    def to_s 
    self.is_whole? ? self.to_i.to_s : self.original_to_s 
    end 
end 

De cette façon, je peux mettre à jour la logique is_whole? (je semble que ce Tadman est le plus sophistiqué) si nécessaire, et il assure que n'importe où un Float sort en une chaîne (par exemple, sous une forme) il apparaît comme je le veux (c'est-à-dire, pas de zéros à la fin).

Merci à tous pour vos idées - ils ont vraiment aidé.

3

Je ne sais pas beaucoup sur Ruby.

Mais il s'agit d'un problème d'affichage . Je serais extrêmement surpris si les bibliothèques que vous utilisez n'ont pas le moyen de formater un nombre lorsque vous le convertissez en chaîne.

Il se peut qu'il n'y ait pas d'option de formatage catch-all qui fasse exactement ce que vous voulez mais vous pouvez configurer une méthode qui renvoie true si float est la représentation flottante d'un nombre entier et false sinon. À l'intérieur d'une routine de formatage que vous créez (vous n'avez donc à le faire qu'une seule fois), changez simplement la mise en forme en fonction de si elle est vraie ou fausse.

This explique comment contrôler le nombre de chiffres qui apparaissent après la virgule lors de l'affichage d'un nombre.

Méfiez-vous des subtilités des représentations à virgule flottante. Math peut dire que la réponse est 3 mais vous pouvez obtenir 3.000000000000000000001. Je suggère d'utiliser un delta pour voir si le nombre est presque un nombre entier.

+0

Ceci est vrai, mais je veux qu'il affiche de cette façon * partout * un résultat flottant sous la forme d'une chaîne. Fondamentalement, je veux remplacer la méthode Float.to_s, ou accrocher cette logique à la fin de celui-ci. –

2

Bien que je suis d'accord avec le message ci-dessus, si vous devez faire ceci:

(float == float.floor) ? float.to_i : float 
0

Je ne sais pas grand-chose à propos de Ruby soit.

Mais en C++, je ferais ceci:

bool IsWholeNumber(float f) 
{ 
    const float delta = 0.0001; 
    int i = (int) f; 
    return (f - (float)i) < delta; 
} 

Et puis je formater la précision de sortie sur cette base.

48

Une façon simple serait:

class Float 
    def prettify 
    to_i == self ? to_i : self 
    end 
end 

C'est parce que:

irb> 1.0 == 1 
=> true 
irb> 1 == 1.0 
=> true 

Ensuite, vous pouvez faire:

irb> 1.0.prettify 
=> 1 

irb> 1.5.prettify 
=> 1.5 
1

Voici ma mise en œuvre horriblement hacktastic fourni à des fins éducatives :

class Float 
    def to_int_if_whole(precision = 2) 
    ("%.#{precision}f" % self).split(/\./).last == '0' * precision and self.to_i or self 
    end 
end 

puts 1.0.to_int_if_whole # => 1 
puts 2.5.to_int_if_whole # => 2.5 
puts 1.9999999999999999999923.to_int_if_whole # => 2 

La raison de l'utilisation de l'appel de style sprintf est qu'il gère les approximations à virgule flottante de manière beaucoup plus fiable que la méthode Float # round.

41

A sprintf un liner ...

sprintf("%g", 5.0) 
=> "5" 

sprintf("%g", 5.5) 
=> "5.5" 
+0

Comment feriez-vous quelque chose comme '" # {% g,%}% off "'? Est-ce possible en tant que one-liner? – Dennis

+0

merci beaucoup, juste ce que je cherchais. – Toontje

+0

Pas exactement, car il ne supprime pas seulement '.0', mais aussi tout si l'exposant est inférieur à -4, et arrondi, par exemple:' "% g"% 1.123456789 # => "1.12346" ' –

2

Si vous utilisez des rails, vous pouvez utiliser l'option aide number_to_rounded avec strip_insignificant_zeros, par exemple:

ActiveSupport::NumberHelper.number_to_rounded(42.0, strip_insignificant_zeros: true) 

ou comme ceci:

42.0.to_s(:rounded, strip_insignificant_zeros: true) 
Questions connexes