Getting the number of decimal places
Write a program (function) to get the number of decimals in a given number.
Examples:
for num = 12.345 decimals = 3 and for num = 12.3450 decimals = 4
(Note that the reference implementation – in Ring – shows a function over a given number rather than a given numeric string, and that the sample values shown above are not enclosed in quotes).
AutoHotkey
<lang AutoHotkey>for i, v in [10, "10", 12.345, "12.345", 12.3450, "12.3450"] output .= v " has " StrLen(StrSplit(v, ".").2) " decimals.`n" MsgBox % output</lang>
- Output:
10 has 0 decimals. 10 has 0 decimals. 12.345 has 3 decimals. 12.345 has 3 decimals. 12.3450 has 4 decimals. 12.3450 has 4 decimals.
Go
<lang go>package main
import (
"fmt" "log" "math" "strings"
)
var error = "Argument must be a numeric literal or a decimal numeric string."
func getNumDecimals(n interface{}) int {
switch v := n.(type) { case int: return 0 case float64: if v == math.Trunc(v) { return 0 } s := fmt.Sprintf("%g", v) return len(strings.Split(s, ".")[1]) case string: if v == "" { log.Fatal(error) } if v[0] == '+' || v[0] == '-' { v = v[1:] } for _, c := range v { if strings.IndexRune("0123456789.", c) == -1 { log.Fatal(error) } } s := strings.Split(v, ".") ls := len(s) if ls == 1 { return 0 } else if ls == 2 { return len(s[1]) } else { log.Fatal("Too many decimal points") } default: log.Fatal(error) } return 0
}
func main() {
var a = []interface{}{12, 12.345, 12.345555555555, "12.3450", "12.34555555555555555555", 12.345e53} for _, n := range a { d := getNumDecimals(n) switch v := n.(type) { case string: fmt.Printf("%q has %d decimals\n", v, d) case float32, float64: fmt.Printf("%g has %d decimals\n", v, d) default: fmt.Printf("%d has %d decimals\n", v, d) } }
}</lang>
- Output:
12 has 0 decimals 12.345 has 3 decimals 12.345555555555 has 12 decimals "12.3450" has 4 decimals "12.34555555555555555555" has 20 decimals 1.2345e+54 has 0 decimals
Java
<lang java>public static int findNumOfDec(double x){
String str = String.valueOf(x); if(str.endsWith(".0")) return 0; else return (str.substring(str.indexOf('.')).length() - 1);
}
Julia
<lang julia>function postprecision(str::String)
s = lowercase(str) if 'e' in s s, ex = split(s, "e") expdig = parse(Int, ex) else expdig = 0 end dig = something(findfirst('.', reverse(s)), 1) - 1 - expdig return dig > 0 ? dig : 0
end
postprecision(x::Integer) = 0 postprecision(x::Real, max=22) = postprecision(string(round(Float64(x), digits=max)))
testnums = ["0.00100", 0.00100, 001.805, 1.0 / 3, 2//3, 12, 12.345, "12.3450",
"12.34555555555555555555", 1.2345e+54, 1.2345e-08, "1.2345e-08", π]
for n in testnums
println("$n has $(postprecision(n)) decimals.")
end
</lang>
- Output:
0.00100 has 5 decimals. 0.001 has 3 decimals. 1.805 has 3 decimals. 0.3333333333333333 has 16 decimals. 2//3 has 16 decimals. 12 has 0 decimals. 12.345 has 3 decimals. 12.3450 has 4 decimals. 12.34555555555555555555 has 20 decimals. 1.2345e54 has 0 decimals. 1.2345e-8 has 12 decimals. 1.2345e-08 has 12 decimals. π has 15 decimals.
Perl
Need pragma bignum
to handle decimals beyond 15 digits.
<lang perl>use bignum;
printf "Fractional precision: %2s Number: %s\n", length((split /\./, $_)[1]) // 0, $_
for 9, 12.345, <12.3450>, 0.1234567890987654321, 1/3, 1.5**63;</lang>
- Output:
Fractional precision: 0 Number: 9 Fractional precision: 3 Number: 12.345 Fractional precision: 4 Number: 12.3450 Fractional precision: 19 Number: 0.1234567890987654321 Fractional precision: 40 Number: 0.3333333333333333333333333333333333333333 Fractional precision: 63 Number: 124093581919.648947697827373650380188008224280338254175148904323577880859375
Phix
<lang Phix>constant fracfmt = iff(machine_bits()=32?"%.14g":"%.18g")
function num_decimals(object o)
integer nd = -1 string s, t if integer(o) then nd = 0 s = sprintf("%d",o) elsif atom(o) then s = sprintf("%.19g",o) o -= trunc(o) if o=0 then nd = 0 else t = sprintf(fracfmt,o) end if elsif string(o) then s = o t = s else crash("unknown type") end if if nd=-1 then integer e = find('e',t) if e then {t,e} = {t[1..e-1],to_number(t[e+1..$])} end if integer dot = find('.',t) nd = iff(dot?max(length(t)-dot-e,0):0) end if s = shorten(s,ml:=5) return {s,nd}
end function
sequence tests = {"0.00100", 0.00100, 001.805, 1/3, 12, 12.345, 12.345555555555,
"12.3450", "12.34555555555555555555", 12.345e53, 1.2345e-08, "12.345e53", "1.2345e-08", "0.1234567890987654321",
"124093581919.648947697827373650380188008224280338254175148904323577880859375"}
for i=1 to length(tests) do
printf(1,"%25s has %d decimals\n",num_decimals(tests[i]))
end for</lang>
- Output:
32 bit
0.00100 has 5 decimals 0.001 has 3 decimals 1.805 has 3 decimals 0.3333333333333333 has 14 decimals 12 has 0 decimals 12.345 has 3 decimals 12.345555555555 has 12 decimals 12.3450 has 4 decimals 12.34555555555555555555 has 20 decimals 1.2345e+54 has 0 decimals 1.2345e-8 has 12 decimals 12.345e53 has 0 decimals 1.2345e-08 has 12 decimals 0.1234567890987654321 has 19 decimals 12409...59375 (76 digits) has 63 decimals
64 bit as above except
0.3333333333333333333 has 18 decimals
Python
Treated as a function over a string representation of a number to allow the capturing of significant trailing zeros. <lang python>In [6]: def dec(n):
...: return len(n.rsplit('.')[-1]) if '.' in n else 0
In [7]: dec('12.345') Out[7]: 3
In [8]: dec('12.3450') Out[8]: 4
In [9]: </lang>
Or, defining a slightly less partial function, over a given number, rather than a string:
<lang python>Report the decimal counts in default stringifications.
import math
- decimalCount :: Num -> Either String (Int, Int)
def decimalCount(n):
Either a message string, or a tuple giving the number of decimals in the default (repr) representations of the real (and any imaginary part) of the given number. # decimalLen :: Float -> Int def decimalLen(f): return len(repr(f).split('.')[-1])
return Right( (0, 0) if isinstance(n, int) else ( (decimalLen(n), 0) ) if isinstance(n, float) else ( tuple(decimalLen(x) for x in [n.real, n.imag]) ) ) if isinstance(n, (float, complex, int)) else ( Left(repr(n) + ' is not a float, complex or int') )
- -------------------------- TEST --------------------------
- main :: IO ()
def main():
Counts of decimals in default stringifications of real (and any imaginary) components of various Python numeric values. print(fTable( 'Decimal counts in stringifications of real and imaginary components:' )(repr)( either(identity)(repr) )(decimalCount)([ 7, 1.25, 1.23456e2, 1 / 7, 2 ** 0.5, math.pi, math.e, complex(1.23, 4.567), None ]))
- ------------------------ GENERIC -------------------------
- Left :: a -> Either a b
def Left(x):
Constructor for an empty Either (option type) value with an associated string. return {'type': 'Either', 'Right': None, 'Left': x}
- Right :: b -> Either a b
def Right(x):
Constructor for a populated Either (option type) value return {'type': 'Either', 'Left': None, 'Right': x}
- either :: (a -> c) -> (b -> c) -> Either a b -> c
def either(fl):
The application of fl to e if e is a Left value, or the application of fr to e if e is a Right value. return lambda fr: lambda e: fl(e['Left']) if ( None is e['Right'] ) else fr(e['Right'])
- identity :: a -> a
def identity(x):
The identity function. return x
- ------------------------ DISPLAY -------------------------
- fTable :: String -> (a -> String) ->
- (b -> String) -> (a -> b) -> [a] -> String
def fTable(s):
Heading -> x display function -> fx display function -> f -> xs -> tabular string. def gox(xShow): def gofx(fxShow): def gof(f): def goxs(xs): ys = [xShow(x) for x in xs] w = max(map(len, ys))
def arrowed(x, y): return y.rjust(w, ' ') + ' -> ' + fxShow(f(x)) return s + '\n' + '\n'.join( map(arrowed, xs, ys) ) return goxs return gof return gofx return gox
- MAIN ---
if __name__ == '__main__':
main()</lang>
- Output:
Decimal counts in stringifications of real and imaginary components: 7 -> (0, 0) 1.25 -> (2, 0) 123.456 -> (3, 0) 0.14285714285714285 -> (17, 0) 1.4142135623730951 -> (16, 0) 3.141592653589793 -> (15, 0) 2.718281828459045 -> (15, 0) (1.23+4.567j) -> (2, 3) None -> None is not a float, complex or int
Raku
Raku does not specifically have a "decimal" number type, however we can easily determine the fractional precision of a rational number. It is somewhat touchy-feely for floating point numbers; (what is the fractional precision for 2.45e-12?), it's pretty pointless for Integers; (zero, aalllways zero...), but Rats (rationals) are doable. Note that these are (mostly) actual numerics, not numeric strings. The exception is '12.3450'. That is a numeric string since actual numerics automatically truncate non-significant trailing zeros. If you want to retain them, you need to pass the value as a string. (As below.)
<lang perl6>use Rat::Precise;
printf "Fractional precision: %-2s || Number: %s\n", (.split('.')[1] // ).chars, $_
for 9, 12.345, '12.3450', 0.1234567890987654321, (1.5**63).precise;
</lang>
- Output:
Fractional precision: 0 || Number: 9 Fractional precision: 3 || Number: 12.345 Fractional precision: 4 || Number: 12.3450 Fractional precision: 19 || Number: 0.1234567890987654321 Fractional precision: 63 || Number: 124093581919.648947697827373650380188008224280338254175148904323577880859375
REXX
Since the REXX language stores numbers as strings, the issue of trailing zeros is a moot point.
If the number (as specified) has trailing zeros, there are left intact.
I took it to mean that the number of decimal digits past the decimal point are to be counted and displayed.
Any number specified in exponential notation is first converted to a whole or fractional integer (or an integer with scale),
and that number is then examined.
<lang rexx>/*REXX pgm counts number of decimal digits which are to the right of the decimal point. */
numeric digits 1000 /*ensure enuf dec digs for calculations*/
@.=; /*initialize a stemmed array to nulls. */
parse arg @.1; if @.1= then do; #= 9 /*#: is the number of default numbers.*/
@.1 = 12 @.2 = 12.345 @.3 = 12.345555555555 @.4 = 12.3450 @.5 = 12.34555555555555555555 @.6 = 1.2345e+54 @.7 = 1.2345e-54 @.8 = 0.1234567890987654321 @.9 = 1.5 ** 63 /*calculate 1.5 raised to 63rd power.*/ end else #= 1 /*the # of numbers specified on the CL.*/
say 'fractional' say ' decimals ' center("number", 75) say '══════════' copies("═", 75)
do j=1 for #; n= countDec(@.j) /*obtain the number of fractional digs.*/ say right(n, 5) left(,4) @.j /*show # of fract. digits & original #.*/ end /*j*/
exit 0 /*stick a fork in it, we're all done. */ /*──────────────────────────────────────────────────────────────────────────────────────*/ countDec: procedure; parse upper arg x /*obtain a number from the invoker. */
if pos('E', x)>0 then do /*handle if the number has an exponent.*/ LX= length(x) /*length of the original number*/ parse var x 'E' expon /*obtain the exponent. */ LE= length(LE) /*the length of the exponent. */ numeric digits LX + LE /*ensure enough decimal digits.*/ x= format(x, , , 0) /*REXX does the heavy lifting. */ end parse var x '.' fract /*parse number, get the fractional part*/ return length(fract) /*return number of fractional digits. */</lang>
- output when using the default inputs:
fractional decimals number ══════════ ═══════════════════════════════════════════════════════════════════════════ 0 12 3 12.345 12 12.345555555555 4 12.3450 20 12.34555555555555555555 0 1.2345E+54 58 1.2345E-54 19 0.1234567890987654321 63 124093581919.648947697827373650380188008224280338254175148904323577880859375
Ring
<lang ring>
- Testing the function
decimals(2) # Unsensitive to the default setting of decimals n = 5.1945 ? NbrOfDecimals(n) # Gives 4
func NbrOfDecimals(n) nTemp = 1 nNbrOfDecimals = 0 while True if nNbrOfDecimals < 9 nNbrOfDecimals++ nTemp *= 10 nTemp1 = n * nTemp - ceil( n * nTemp ) if nTemp1 = 0 return nNbrOfDecimals ok else raise("Acceeding the maximum number of 9 decimals!") ok end </lang>
- Output:
4
Wren
In the following script, the fourth and fifth examples need to be expressed as strings to avoid getting the wrong answer. If we use numbers instead, trailing zeros will be automatically removed and the result will be rounded to 14 significant figures when stringified or printed.
Converting the fourth example to a Rat or BigRat object wouldn't help as the constructor for those classes automatically reduces the numerator and denominator to their lowest terms. BigRat would work for the fifth example but the argument would have to be passed as a string anyway so we might as well just parse the string. <lang ecmascript>var error = "Argument must be a number or a decimal numeric string."
var getNumDecimals = Fn.new { |n|
if (n is Num) { if (n.isInteger) return 0 n = n.toString } else if (n is String) { if (n == "") Fiber.abort(error) if (n[0] == "+" || n[0] == "-") n = n[1..-1] if (!n.all { |c| "0123456789.".contains(c) }) Fiber.abort(error) } else { Fiber.abort(error) } var s = n.split(".") var c = s.count return (c == 1) ? 0 : (c == 2) ? s[1].count : Fiber.abort("Too many decimal points.")
}
var a = [12, 12.345, 12.345555555555, "12.3450", "12.34555555555555555555", 12.345e53] for (n in a) {
var d = getNumDecimals.call(n) var ns = (n is String) ? "\"%(n)\"" : "%(n)" System.print("%(ns) has %(d) decimals")
}</lang>
- Output:
12 has 0 decimals 12.345 has 3 decimals 12.345555555555 has 12 decimals "12.3450" has 4 decimals "12.34555555555555555555" has 20 decimals 1.2345e+54 has 0 decimals