Tengo números enteros grandes (típicamente de 15 a 30 dígitos) almacenados como una cadena que representa una cierta cantidad de una corriente determinada (como ETH). También se almacena con eso el número de dígitos para mover el decimal.
{ "base_price"=>"5000000000000000000", "decimals"=>18 } El resultado que busco en última instancia es 5.00 (que es lo que obtendría si tomara el decimal de 5000000000000000000 y lo moviera a la izquierda 18 posiciones).
¿Cómo haría eso en Ruby?
Dado:
my_map = { "base_price"=>"5000000000000000000", "decimals"=>18 }Podrías usar:
my_number = my_map["base_price"].to_i / (10**my_map["decimals"]).to_f puts(my_number)h = { "base_price"=>"5000000000000000000", "decimals"=>18 } bef, aft = h["base_price"].split(/(?=\d{#{h["decimals"]}}\z)/) #=> ["5", "000000000000000000"] bef + '.' + aft[0,2] #=> "5.00" La expresión regular utiliza la anticipación positiva (?=\d{18}\z) para dividir la cadena en una ubicación ("ancho cero") entre dígitos de modo que sigan 18 dígitos hasta el final de la cadena.
Alternativamente, uno podría escribir:
str = h["base_price"][0, h["base_price"].size-h["decimals"]+2] #=> h["base_price"][0, 3] #=> "500" str.insert(str.size-2, '.') #=> "5.00"Ninguno de estos aborda posibles casos límite como
{ "base_price"=>"500", "decimals"=>1 }o
{ "base_price"=>"500", "decimals"=>4 }Tampoco consideran cuestiones de redondeo.
¿Expresiones regulares e interpolación?
my_map = { "base_price"=>"5000000000000000000", "decimals"=>18 } my_map["base_price"].sub( /(0{#{my_map["decimals"]}})\s*$/, ".#{$1}" ) El número de lugares decimales se interpola en la expresión regular como el conteo de ceros a buscar desde el final de la cadena (más cero o más espacios en blanco). Esto se empareja, y la coincidencia se subtitula con un . en frente de eso.
Productor:
=> "5.000000000000000000"